TDD Accelerates Development

Periodically, well-meaning Agile advocates will make the argument that practicing Test-Driven Development trades development speed for better quality. That is, it slows you down but it’s worth it because the resulting code is better.

Rubbish, I say. TDD makes me a faster coder.

I could make a number of arguments here about TDD speeding up development in the long-term – TDD cuts down on time spent chasing regression bugs; TDD helps avoid writing code you don’t actually need; etc. And they would all be true and valid arguments.

But I think I can make a case for TDD accelerating development in the short-term as well.

When I started out programming, starting a new project or a new feature was a haphazard process. I’d often start off with “Blank Slate Syndrome” – not knowing where to begin, I’d freeze up for a while considering different angles to attack the problem from. Then I’d make a few tentative stabs at different parts of the program. Often beginning to write one subsystem would lead me to think of something else I’d be “needing”, causing me to leave the current piece half-finished and work on something else. After a few days of chasing these tangents I’d have a bunch of false starts, rewrites, and half-written methods lying around, and no working code.

Learning to practice TDD drastically cut down on my blank-slate issues, because now the question “what next” always had a simple answer: make a failing test pass. If all the tests pass, write a new test. Don’t worry about what you will “need”; just make those tests pass. This alone gave me a huge boost to my initial ramp-up speed in new projects.

Of course, this still left me with the question: what test do I write next? Lately I’ve found that using Cucumber to write acceptance criteria for my apps has all but eliminated this last vestige of Blank Slate Syndrome. When I’m just getting started and I’m not sure where to start writing tests, I step back and write a plain-english explanation of the functionality I want to implement. Then I start at the beginning and start turning each step into runnable code with Cucumber. If at any point I find myself writing non-trivial amounts of code in order to make the step pass, I drop down to the unit level and start writing RSpec expectations (unit tests). Then I move on to the next step, and so on.

The upshot of all this is that I find myself wasting very little time on premature architecture and false starts at the beginning of a project r a feature. And that means real, short-term savings of time.