Every time I've done a job interview in the last 10 years, TDD (Test Driven Development) has always come up in the discussion. In almost all cases, the more the people who have been interviewing me have waxed on lyrically about unit testing and TDD - the less they actually do it.
I'm not necessarily being lied to. Often the people interviewing me are manages and it's common to find a disconnect between what managers say people are doing, and what they are actually doing. So walking into my new job expecting well written/tested code, I find cut-n-pasted hacks, over-engineering, lazy coding, ill thought out designs and no unit tests. Or at least, none worth running. The developers have just ignored or turned them off because they simply could not be bothered maintaining them.
Like any good plane crash, it takes more than one issue to cause it. Managers typically talk a lot, but take no active interest in ensuring code quality. Never checking quality reports. Team leads are too busy to enforce good habits. Testers (if there are any) are almost always never have the skills to automate tests. And most developers are neither conscientious or proactive enough ot bother with testing. So we have a systemic failure from top to bottom in terms of actually ensuring that the code does what is expected.
TDD is usually promoted as a solution to this. The idea being to place testing first and therefore the rest will follow. But does this actually work?
Sometimes. TDD, like other programming practices, patterns, methodologies, etc, has it's place. It's not "The Answer" any more than any of the others are. It's just another tool which is sometimes the appropriate tool to use, and sometimes not.
If I'm writing what I would call exploratory or spike code, TDD is not the right choice. By this I mean code where I have any idea of what I want to achieve, but I'm not sure how I am going to achieve it. In this mode I'll typically write something pretty broad, or pick out a part of the functionality and write it. Then I'll refine it until I have something that does what I need. TDD doesn't work well when doing this. The reason is that if I tried and TDD it, I end up with a lot of tests which I'm constantly refactoring and rewriting as I work out what direction the code is going in. Because the direction of the code is unclear, trying to TDD slows everything down to a crawl because added load of the test code slows down the work. In addition, it also tends to dissuade you from making significant design changes because of the hurdle of refactoring the tests for it.
My preferred mode here is to concentrate on the code, get it worked out, then stop and bring the unit tests up to speed.
Where TDD also doesn't work is at higher levels. If you try and write tests first for higher level functionality, you will find you end up with large amounts of time with broken tests whilst you write all the code need to get them green.
So where does TDD actually work?
Mostly I find it's best used when you are dealing with lower level code that has to cope with a variety of inputs. For example, a function that accepts a string which it then has to parse. This sort of low level situation is ideal because you can quickly write a large number of tests with all sorts of input strings. Then simply adjust the parsing until it copes with all input strings being passed to it. This is ideal for TDD because it makes sense to setup a range of test scenarios first.
In all of these situations there is one overriding thing that will dictate the success or failure of your code, regardless of what techniques you apply. That thing is your own self-discipline. Without that, no amount of TDD or other practices will help you.
You have to know when to stop and write tests, to know when to switch to and from TDD, and have enough self control to not only think about testing, but to make yourself do it.