Tim Bray writes about when and how he does TDD. I think he’s exactly right when he talks about not doing TDD for green-field, beginning development. When I’m first writing a cut at a new project, I’ve got almost no idea what I’m doing. Doing TDD at this stage in the game is super expensive and really gets in the way of the exploration necessary for a new project. Having to rewrite my code as it evolves is expected but having to rewrite my code AND my tests because both were completely off base is pretty inefficient.
However, once things are pointed in the right direction, TDD is critical. It makes the incremental evolution of the software much safer, easier and efficient. Without the tests, iterating over your software is painful and liable to introduce bugs in code that was once working. TDD is at its best in the situation where the code, while not stable, is in a state of incremental evolution and not the general state of random upheaval that it is in the beginning of the project.
Like most things, pragmatism is useful. There are shades of gray around everything and saying that there is one true way makes no sense at all. TDD is useful as a tool but hammering every nail you see with it probably isn’t the best way to do things.
I’ve also started to see TDD as a tool to allow those of us who aren’t geniuses to actually produce good code. I’ve known a few true code geniuses in my career and like Picasso, their art tends to spring fully formed out of their heads. They may go back later and write tests around the code. However, they don’t need TDD to be good. For the rest of us, the incremental evolution that TDD supports and encourages helps us to write code that is functional and clean because we have to focus on the client of our software. Those of us who aren’t masters use TDD to fake it, to produce works of art that might not eventually hang on the walls of the Louvre but that make customers at art fairs all over the world reasonably happy.