Some Thoughts About TDD and TAD
An essay I wrote back in late ’09, titled TDD is Dead; Long Live TDD has picked up some comments after all this time. Commenter “Shmoo” made an interesting point, essentially that all my arguments in favor of TDD were really just arguments in favor of unit tests, and could just as validly build the case for a test-after style of unit testing. Shmoo writes:
All good points, Joel, except that they have little to do with TDD, and more to do with unit testing (before or after the fact).
“TDD As Kind Teacher” – except for the first line, where you mention, “Test-first,” all the rest applies to test-after development (TAD).
“TDD As Dopamine Drip” – the green bar rush you get here is the same in TAD.
“TDD As Brain Girdle” – everything here applies to TAD (indeed the, " … tests make it easier for me to re-orient myself to my old code …" has even less to do with TDD).
“TDD as Scrooge” – as you say yourself, “When I code without tests, I might end up rowing upstream in a gold-plated dinghy …” This clearly applies to TAD.
“TDD As SmellOVision” – As you say, “… you find that you’re having a really hard time writing one particular test. 9 out of 10 Pragmatic Press authors agree this is a strong indication that your code could use some rethinking.” That applies precisely to TAD as it does to TDD.
None of your advantages is specifically related to writing the tests before the code.
All you’ve done is praise testing.
Didn’t Naresh pick you up on this?
You make some interesting points, Shmoo. I hadn’t really considered the Test-After approach as a desirable alternative to TDD. The reason for my “blinkered” focus on TDD is due to my early experience with TAD.
I began learning unit testing and TDD in the middle of a project, and so for new code I was doing TDD. But I really wanted coverage of all the code I had written before learning anything about unit tests, and so I also undertook to write unit tests for all the old stuff.
Those two exercises, done in parallel (TDD & TAD) really sold me on TDD. The two experiences were dramatically different. I do not think I exaggerate to say that the TAD work was an order of magnitude more difficult and time-consuming than the TDD work. And of course my TDD’d code emerged with a better, more testable design.
That last sentence may be the crux of the difference between the two. I find that TDD leads to better code than TAD, and I also find that TDD is a better teacher of good code design than TAD. The TDD workflow seems (for me) to unfold good design as I go along, whereas, writing tests after just jabs me in the ribs to tell me that I already got it wrong.
Furthermore, TAD, to be really effective, requires a kind of attention and discipline which, frankly, I don’t have in spades. It is to easy to write code for 30 minutes, an hour, or half a day, before that nagging feeling that I need some more tests finally overcomes my coding-engrossed brain, and I shift gears and try to put some tests around the bridge to nowhere that I have been building. TDD ensures the testing is built-in, and once the habit is formed, I don’t have to remember to test; the testing and the coding quite naturally come up together. For me, that’s way, easier.
This isn’t to say that an already mature, well disciplined developer mightn’t do quite well with a test-after workflow. But I find that TDD is a fine ladder which is helpful in elevating the skills of developers through a wide range of skill levels. I have been practicing it for a few years now, and I still find it helpful. And the number of TDD-practicing developers whose skill far exceeds my own encourages me not to lightly abandon the practice.