Test infected developers often claim TDD to be the most important tool in a programmer's toolbox: "TDD makes you more productive. It drives good design and high quality code. I could never go back to not writing tests again." you hear them say. I am like that: infected. I have raved about TDD for years, believing it to be a revolutionizing approach to programming.
But I've changed my opinion about TDD. Not that I don't like it any less - I still think it's a wonderful tool, and it still helps me learning more about design. I use it all the time, and I am eager to show and give support if someone's interested to learn. However, I have realized that TDD is not the instant quality and productivity injection I though it was. Not for every developer, not for every team. My previous assumptions about TDD were based on a typical case of the syndrome works-for-me-must-be-good. And also, it is easy to forget the struggle you've been through to get where you are today. And learning TDD can be a little bit of a struggle.
I think TDD is great in the same way that agile is great, or object-orientation, or design patterns: great when you know how to use it effectively. When you don't, it may cause more harm than good. TDD is deceptively simple: Red-Green-Refactor, Red-Green-Refactor, ... The basic concepts are easy to understand, it is easy to get started. But when trying to solve real-world programming tasks, many TDD beginners are likely to run into enough problems to get turned off by the TDD concept, and there's an obvious risk that they stop using it. And if they are actually able to finish a task using TDD, the solution might very well be the same kind of tightly coupled, brittle, monolithic (or whatever style) software as used to be the case before using TDD, only with a bunch of tests added to the mess. These are some common problems I have observed:* Difficulties doing design at the keyboard. Many developers become paralyzed when trying to come up with tests. They are not used to thinking in terms of tests as specifications. They become stressed by proceeding so slowly with the programming compared to what they're used to. They want to do design before starting programming. I think this is an issue for both die-hard TDDers and newbies: Some developers should do more up-front thinking, others should do less.
* Difficulties to decompose a solution into testable parts (and make them fit together). A small number of big classes and methods is the norm for many developers. And often these parts have dependencies on resources that gets in the way and cannot easily be handled when testing.
* Tests become brittle, and have to be rewritten for the smallest code change. This is one of the true challenges for any TDD practitioner: being able to write tests that are not too tightly coupled to the implementation, while still being simple enough to make progress in the Red-Green-Refactor cycle.
* A lot of tests where it is easy to test, but few, or no, tests in important places. A typical example is testing get/set of dumb properties but failing to cover the interesting scenarios that explain the big picture (the specification aspect), and involve more complex behavior where any potential bugs are likely to appear.
* No real refactoring is going on. Two main reasons I think: developers either don't have a feeling for refactoring (neither smells, nor solutions), or refactoring requires tests to be rewritten, which seems like too big a cost.
Frankly, skilled TDDers run into these problems too. The difference is that when they do, they know how to react. And that's the great thing about TDD: it gives you feedback, instantly challenging your design decisions, and on a deeper level the way you do design. Being able to understand this feedback, and knowing how to react appropriately, takes insight and experience. Not only design experience, but also experience of the TDD specific stuff. Is the design seriously flawed, or does it just need small modifications to become testable? Did I try to solve too big a chunk of the problem when I wrote my test, and if so, how can I find an appropriate smaller chunk to work on? And so on...
So, if TDD is so hard, then why use it? Or at least, why try to sell it to less experienced developers? Well, are design patterns too hard to use? Yes! I hear many developers scream. Or objects - yes again? Well, functions then, are they too hard? Or maybe programming as a whole?
In practice, TDD is not that simple. You will not instantly become a great designer just because you start doing TDD. You are constrained by your existing design skills, your talent for spotting smells and come up with remedies, and you're ability to design software that is easy to test. But at the same time, that is why I believe TDD can be such a good tool for developing skills in those areas. It gives you feedback! The only hard prerequisite is your interest in software design, and you're willingness to put in some work to become a better developer. Prepare for a little struggle, prepare for improvement!