TDD - is it worth it?
There are many articles on the subject of what TDD is, why and when it is worth it, and which attempt to counter common objections.
This is not one of those.
Well. Maybe a bit.
This is more specifically a response to Marco Arment's comments in his podcast, Build & Analyze, episodes 107 and 108. Episode 108 was the last episode so there is an air of finality to the subject matter. Many Mac and iOS developers (as well as developers for other platforms) listen to the show and, while you'd hope they can all think for themselves and reach their own conclusions, it's undeniable that opinions, if not already well formed, may easily be swayed by what a respected figure says in a high profile, and well polished, medium. This can be unfortunate. I'm sure Marco didn't intend to do any damage. I've listened to every episode of Build & Analyze for over a year and enjoyed it. This is certainly not a flame against Marco or the show. However I'm going to walk through Marco's comments as a proxy for many who make similar statements. In doing so I quote him liberally, rearranging to fit my narrative. I'm including the time markers so you can easily listen to it in the original context
Episode 107: 6:50 The comments in question started in response to a listener question about testing and logging. "Test first development" was mentioned in passing, but Marco groups these all together as "a whole lot of formalism" which "matters a lot in an enterprise environment", but he, "[doesn't] really do any of this stuff".
These are quite different things but let's set that aside for the moment. So far he's just told us he doesn't use any of it and it works ok for him. Ok.
But he carries on, "All of this structure and all of this overhead - I wouldn't be able to release this software as one person and have it stay competitive and have it released frequently - there wouldn't be enough time for that".
Now he's making a claim.
Some more choice phrases:
"You've gotta keep moving so fast"
"It isn't worth it in those environments"
"I don't care about all that stuff"
(The last seems to contradict an earlier statement, "Some of this stuff I do regret not knowing what it is", but we'll let that pass)
The first two continue on the theme of needing to move fast and "all those formalisms" "slowing you down".
Ok. Let's stop a moment. I want to be clear again: I'm not trying to attack Marco here. It is not my intention to pick apart the details of his extemporaneous words. He honestly appears to be speaking on the assumption that all the things the listener had asked about are excessive, "formal things", that "matter a lot in […] medical […] or banking" enterprises, or for a "space shuttle" - but are just unnecessary overhead for a one-man independent developer writing, "for apps on phones that don't do anything important".
If you accept the faulty premise the rest of his reasoning makes a lot of sense.
So after I listened to episode 107 I mailed in to explain the difference between low-level, developer oriented, disciplines - TDD in particular - and the higher level, quality-driven, testing approaches more commonly associated with large enterprises or critical industries. I attempted to briefly summarise the benefits that even a one-man developer shop might get from adopting such a discipline.
Unsurprisingly I was not the only one.
Episode 108, from 28:58: Marco brings up the feedback he received, amused to note that everyone pointed out that TDD was, "not only about testing, but it's about writing, 'self-contained code that makes it easier to refactor…'". He recognises that this (and other principles of software design) are "good programming practice". He references The Pragmatic Programmer as being a good source of such principles - and it is. However he appears to believe that proponents of TDD think this is "exclusive to TDD" and, unfortunately, this takes him off-track again. (These days I would probably recommend Growing Objected Oriented Software Guided By Tests and Clean Code as more up-to-date works that cover similar ground.)
Interestingly, The Pragmatic Programmer thoroughly recommends various forms of testing - including TDD (although not by that name - I'm not sure that term had been coined when the book was written). Pertinent to Marco's objections it has this to say:
"A good project may well have more test code than production code. The time it takes to produce this test code is worth the effort. It ends up being much cheaper in the long run" (emphasis mine).
The book also has a section on Refactoring. The topic was new (by that name) at the time - it even mentions that the "first major book on refactoring" was being published around the same time. Nor had the central role of refactoring in TDD's, "Red, Green, Refactor", cycle been clearly established at the time. Nonetheless it makes these two points that cut to the heart of it:
- "Don't try to refactor and add functionality at the same time."
- "Make sure you have good tests before you begin refactoring. Run the tests as often as possible. That way you will know quickly if your changes have broken anything. […] [Martin] Fowler's point of maintaining good regression tests is the key to refactoring with confidence."
As to the other principles: can they be followed without using a TDD approach? Of course they can. So what was all Marco's feedback about? Simply this: Driving your code from tests forces you to make the code testable. This naturally leads to code that is less coupled, has less responsibilities per unit, with higher cohesion and is written from the start with the idea that it is easy to change. As a by-product you have a great set of automated tests that give you the confidence to refactor to improve all these - and other - principles.
On their own that would all require a lot of discipline - and can be hard to measure (to know how well you are doing). In TDD it's usually easier to follow them from the start - and if you don't the tests will "tell" you by becoming harder to write. TDD is, itself, a discipline - and does require some experience and a prior understanding of the design principles that make it go smoothly. But in my experience it is much easier - and more gratifying - to follow the simple discipline of TDD than to remember to apply all the other individual principles with nothing to guide you.
The result is that your code will tend to be lean and supple and can respond to changes in requirements quickly and easily. You'll spend less time on finding and fixing bugs, and less time on writing code you didn't really need. The time spent writing the tests usually pays for itself almost straight away - often several times over. Saving time and being able to respond to change quickly - are these not the very qualities Marco values so much?
Back in Episode 107, from around 25:57 - right after his first comments on testing - he talks about a situation he got into with The Magazine - a brand new code base for him. "The code was just getting messier and messier". He had a bug but he "couldn't figure out what the heck was going on" and "had all these other things that were problems with that system and it was really clunky". He goes on to say, "it was starting to get to a point where I was fearing adding anything to it. When code gets to a point where it's like a giant pile of messy spaghetti and it feels very fragile and you feel like, 'Oh my God. I need to add an attribute to this - is that going to break anything?'"
It's a shame that he appears to have just dismissed what is probably the best tool we have come up with to date for avoiding this ever happening in the first place.
Thanks to all those who I coerced into reviewing this post for me: Seb Rose, Jez Higgins, Paul Grenyer, Claudius Link, Pal Balog, Hubert Matthews, Peter Pilgrim, Martin Moene, Giovanni Asproni, Yechiel Kimchi, Chris O'Dell, Peter Sommerlad and Graham Lee