Updated – 11/23/2005
The content on this page has been taken down. I want to thank those of you who voted on this topic to send the correct message to Microsoft, and those of you inside of Microsoft who took the complaints seriously and acted on them. </>
This link has been making its way through the blogosphere over the past couple of days. Please take a minute and visit that link, and then come back here.
<Brief musical interlude>
I learned about this from Michael Feathers, who learned about it from Scott Bellware. Ladies and gentlemen, this piece of advice is slightly less than correct. The problem here is not that the advice is wrong (ok, that’s a problem, but we could overlook that since there is so much other good TDD information out there), it is that it is coming from Microsoft. Given that there are billions of MS developers, and they tend to go look to MS for advice about how to develop, this advice carries instant weight and credibility. And yet, it is not correct.
Fortunately, there is a feedback mechanism that you can use should you wish to. At the bottom of that page, there is a place to vote on the quality of information on that page. There is even a place to put comments, gently encouraging the content to be changed.
Why the advice is wrong
Now that the rant is over, I’d like to discuss what is so wrong with the advice. All you experienced TDDers will have picked up on it immediately after reading the page. The problem is that if you develop your software in the way described in that page, you remove the opportunity to learn from feedback. And after all, the whole point of TDD is to poke at your code, learn from it, and use that learning to poke at it again.
The advice as given is (slightly paraphrased with a few steps omitted):
- Make a list of tests that will verify the requirements. A complete list of tests for a particular feature area describes the requirements of that feature area unambiguously and completely.
- Create work items in Team Foundation System for each test that needs to be written
- Write the shells of the classes and interfaces you’ll need to implement these tests
- Generate test stubs for each class and interface
- Carefully examine each test to make sure it tests what you think it should
- Run all tests and watch them fail
- Go through each test and update it to test what you actually want it to. This is where you implement each test fully
- Run all tests and watch them pass
- Fix bugs
Contrast this with a different approach to writing your code:
- Make a list of tests that will verify the requirements. Don’t worry if the list isn’t complete — you can add to it as you go and learn
- Write a test from your list that illustrates some behavior of your system that you consider important but can be implemented quickly (5 minutes?)
- Try to compile it and watch it fail
- Write just enough code to make that single test compile but fail. This may involve creating new classes and interfaces
- Run this test and watch it fail
- Make the test pass
- Run all tests and watch them pass
- If any failed, fix the code and rerun tests
- If feature is implemented and all refactoring is finished, then goto end
- Consider if there are new tests to add to your list
- Goto step 2
The key difference between these two approaches is that the top piece of advice encourages you to plot your entire course through unknown territory up front. You create your entire test list, commit this to stone through TFS work items, write all your classes and interfaces, generate all your tests, implement everything, and then look to see if things pass. If, in this case, you decide you were wrong about something, or some requirement changes, or other strange set of cosmic events occurs, changing your mind is going to be painful. If you just learn something new that makes you take a different course, you have to go back and change all this infrastructure you’ve built up around a guess about how something might work, and it just isn’t going to happen. In this style of development, I believe you’re going to make your stab at it at first, and then make that stab work.
In the bottom style of development, you’re encouraged to work entirely piecemeal. Try something, make it work, learn from it, and try the next thing based on what you’ve learned. If you change your mind, the only baggage you are responsible for bringing along with you is that baggage you’ve already created. You have nothing invested in your guess about how this thing might work, so learning and changing your mind is easy, quick, and cheap.
These are two different styles of development, and only one of them is TDD.