Duct Tape, Astronauts, and Everything In Between
Joel Spolsky recently blogged about what he calls a “duct-tape programmer.” The blog post made the rounds with a few people at the office after my boss brought it up at lunch yesterday.
From what I read, Joel’s opinion is that it’s better to ship usable software than spend time engaging in architecture cosmology, which nobody with a moderate amount of experience and any sense would disagree with. However, Joel makes his point by drawing two totally different extremes and making some statements that completely conflict with both my experience and common sense.
The first extreme is the “duct-tape programmer.” Joel lists a person of this description’s attributes as the following.
- Not afraid to speak up when another programmer is pitching an overly-complicated solution.
- Comfortable with a 50%-good solution that ships over a 99% solution that nobody uses.
- Focuses on shipping as a top concern.
- Avoids complicated coding and difficult frameworks/libraries/languages/techniques that are too hard for the human brain to handle.
- Doesn’t give a shit what others think about them.
- Spends little to no time writing unit tests.
- Smart enough to do things like xor the “next” and “prev” pointers into a single DWORD (which makes you wonder how they are not smart enough to work with some of the languages/libraries/frameworks/techniques that are apparently so hard to grasp – but whatever).
The second extreme is the “architecture astronaut.” Here’s the attribution for this person.
- Likes over-complicated solutions too hard for the average programmer to understand.
- Bullies other programmers in to using their frameworks/solutions by relying on the level of complication in code that only they understand as a barrier to objections.
- Likes multiple inheritance.
- Spends more time writing frameworks than shippable code.
- Attends design pattern meetings.
- Speaks at conferences, writes articles and books.
I have a little perspective on this since (like most software engineers with a few years under their belt) I’ve both been and dealt with both personality extremes during my career. Both extremes are equally damaging to software products in different ways.
Let’s start with the duct-tape guy. I’d say most of the attributes for this personality type are positive qualities; pragmatism, the self-confidence to disagree with bad ideas, a tendency for choosing the simpler approach, and a focus on making a shippable product. The quality of this guy that really bothers me is the lack of use/respect for unit testing, and the tone Joel takes with unit testing in general in his post. The argument seems to be that unit testing takes too long to do and doesn’t support rapid development and isn’t worth it.
This is utter nonsense. I’ve been on projects where the only reason we were able to ship in a short timeframe is because we unit tested early and often, and were able to spot bugs within seconds of them being added to the codebase, which saved time we would have spent debugging broken code later in the product lifecycle.
The thing with unit testing is that you have to be pragmatic with it as well. Looking to hit 100% coverage? You’re wasting your time. Only testing a handful of methods? You’re missing out on important stuff that will bite you in the ass later. Writing the core of an app with a GUI? If you wait to test until the GUI starts coming together, how do you know your code works? I’ve met nobody, and I mean truly nobody that is good enough to write code the first time and get it right every time. In the absence of unit tests, you find yourself manually testing everything from whatever presentation technology/transport you are using to ensure that it works, which is both slow and error-prone. The point is, choose a reasonable amount of coverage (60%-80% tends to be the sweet spot in my experience) and use it to your advantage.
So what about the astronaut? Well, this guy suffers from two problems: he’s not a very good programmer, and he has an awful manager.
Why isn’t he a good programmer? My experience has been that programmers go through four stages when learning a language or framework. As a novice, they barely understand what they are doing and mainly follow the advice of others. As they gain competence, they acquire understanding at a level that makes them productive. As an advanced user, they start pushing the limits of the technology and doing tricks with it, but their solutions are complicated and inelegant. As an expert, they learn to get the seemingly complicated stuff done in a manner that others can understand with little effort and put to use just as easily.
Clearly, if the astronaut is making frameworks that are that complicated, they aren’t helping anybody. Using Spring as an example, the reason it’s such an excellent framework is that that it makes every part of itself so easy to use. The source code I have seen in Spring is terse and easy to follow, even though it deals with advanced techniques. This is the mark of expert programming worthy of respect.
Why does his manager suck? Because they’re not keeping this guy on a tight enough leash to focus on what’s important, which is getting stuff done and writing maintainable code that anybody can walk in to and change. Instead, the astronaut is stroking his ego writing frameworks and libraries to “prove” how awesome he is at using the language. I can say this with confidence because I’ve been there and done it earlier in my career, and just like the astronaut, I pulled the wool over my manager’s eyes as well. Luckily, I came through the other side, but I’ve seen lots of people get stuck at this stage. They are easy to spot: they are the people who write code that only they understand – not because it’s good, but because it’s unreadable and unnecessarily complicated. A true expert with the language would certainly be able to slog through this spaghetti, but they’d spend most of that time wanting to punch the astronaut in the face for making it so hard on everybody else instead of making something elegant.
That’s not all. There are some other things that simply don’t make sense or conflict with themselves in Joel’s post.
If you write an app with no unit tests and ship it, unless you either (a) never have to change it again or (b) wrote a trivially tiny app, you are likely to find yourself having a really hard time refactoring/adding to it. After all, you’ve got no test suite to validate your changes and prove that you haven’t broken anything. The only way you could even say that you were confident the code still worked in the absence of a test suite is to be able to hold all the details about the app in your head (every branch, every scenario, etc.) which Joel already stated is too hard for the average programmer.
If you are using a technology that is overcomplicated and/or is buggy (C++ on Windows, CORBA, COM, etc.), then use something else. Techniques like multiple inheritance aren’t so much difficult to grasp as they are generally acknowledged as a code smell in 90% of use cases. And to say that multi-threading is going to doom your project because it can be complicated is really kind of a stupid thing to say; there are some things that simply can’t be done effectively/performantly without multi-threading. Its use should be limited in scope accordingly and written by somebody who understands threading. I don’t think anybody writes multi-threaded code for the fun of it, except perhaps for the astronaut who we’ve already proven is an epic douche to begin with.
There’s a general tone to the post that any time spent on creating utility library/framework code and/or researching elegant solutions is wasteful. This is a bad generalization. I once had to help save an app at the eleventh hour that one of the guys on my team had spent two months writing. It had failed an embarrassing number of QA drops due to input validation failures. Why? He hadn’t spent any time writing a basic framework for the validation concerns of the app, and every time he patched the bugs found in QA, something else got broken or acted inconsistently with other parts of the app. I sent everybody except me and my programmer home, spent an hour writing a simple validation framework, and we spent the night plugging it in to the app. It passed QA the next day. When I asked him why he hadn’t taken the time to do this in the first place, he said he was under pressure to get the app delivered and didn’t have time to implement a cleaner solution. It goes without saying that excessive architecture is wasteful and damaging, but no architecture at all is just as bad. Liek everything else, you need to be pragmatic and pick sensible battles.
To summarize: no doubt, programmers should spend 90% of their time writing shippable code that is useful to end users, implemented in the simplest way, using the easiest thing that works. However, to unilaterally throw out unit testing and claim that any level of design work or framework implementation is a bad quality is a dangerous oversimplification that I fear many of the people following Joel’s blog may be inclined to interpret as good advice.
- Using AppleScript to Position iChat Windows (2)
- An Objective-C Tutorial for Enterprise Java Programmers (12)
- Performing SQL Validation (a.k.a. “Data Dips”) with RIATest (4)
- Configuring Tomcat SSL Client/Server Authentication (28)
- Calculating the Differences Between Consecutive Rows with SQL (3)
- JDK 1.7.0 on Snow Leopard
- Using Axis’s wsdl2java in a Maven Build (13)
- How to Install m2eclipse in Flex Builder 3 (3)
- Saving OmniGraffle Documents in Subversion
- How To Become A Software Engineer/Programmer (15)
- Speak and Spell Samples (13)
- Eulogizing the Insanely Late Steve Jobs (1)
- How to find old Airport Express/Extreme/Time Capsule firmware
- Using curl with a web site secured by Rails Authenticity Token (6)
- An AppleScript to Toggle Sleep Modes
- Jamming on the Arduinome with mlrv (3)
- SammichSID Finished (1)
- Greater Orlando Hackerspace (3)
- My Arduinome Build in Pictures