Some insights about TDD

At the recent meeting of Hamburg’s Softwerkskammer – the German Software Craftsmanship movement – we worked through a Coding Dojo on the Roman Numeral Kata. Michael Norton wrote today about one piece that worried me as well. I think Michael did a fantastic job on tackling a different approach. But he reminded me that I wanted to put up some of the thoughts from the Coding Dojo.

We had about 12 participants in the dojo. After explaining some pieces about the format and the kata, we started the dojo. As the kata started, we had one participant asking questions up to the degree that the pair in front of the keyboard stopped doing anything.

Eventually we got the team back up to continue working on the problem. The claim of the interrupter was that we didn’t yet understand the problem well enough to design the solution. Another claim was that TDD was not a design technique. Let’s take a closer look at both of these.

We have to understand the problem before using TDD

You could argue that the Roman Numerals kata is not very complex. Yet it seems that it’s possible to not grasp the domain of the future code model. Let’s take a look on the problem.

The romans counted differently than we do now. They had letters for different numbers. “I” is 1, “V” is 5, “X” is 10. You combine two “I”s to yield a 2: “II”. You combine two tens to yield a twenty: “XX”. You combine five and three to yield an eight: “VIII”. There are two exceptions to this concatenation. The four and the nine are generated by subtracting one from five, respectively ten: “IV” and “IX”.

We started the kata with a test for one, then for two, then we were heading for three. The question was whether we should go for a test for four or five at that point. I recalled Kent Beck’s exercise at his advanced TDD course in November 2010. He had us performing a kata. Then he asked us performing the same kata, but working the tests in a different than the usual order. The thing is that once you have a final design in mind, you work towards it. Now, the situation in the dojo was differently, as we didn’t discuss a design.

Now, we were stuck at implementing a large construct of guard clauses. When we introduced the test for the five, then the test for the four, we finally made some progress. We had three instances of guard clauses that returned some value. All looked similar. We tried to see a different design when we were facing the situation with two similar blocks. The design we worked towards at that point seemed to get in our way once we introduced the four.

The case when we had three distinct blocks was easier, and led to a better design. Now, the question is, if I do not have a particular design in mind, can I still explore possible designs?

I think so. The thing is, that I have to stop thinking while refactoring. I have to start thinking again when I stop refactoring to see whether the design I’m heading towards is any good at all. Using continuous integration, I can then revert back to the old design, and go a little bit further.

TDD is not a design technique

This insight is mostly triggered by the realization that Exploratory Testing is not a test technique, but a test approach. This means that you can combine Exploratory Testing with any testing technique. It’s orthogonal to testing techniques.

Similarly, TDD is not a design technique. It does not solve the problem of designing the source code for you. It helps you to think about the design though. You can combine TDD with any other design technique like UML, prototyping, and whatever you can think of. TDD is an approach to design. It helps you finding a design in your source code, rather than designing it. It’s you that designs the source code.

8 thoughts on “Some insights about TDD”

Both of those fall under “Design”, but on the outlyers. Most designing is implementation of existing algorithms.

TDD is a design technique when it vets a module’s design during each step of its growth. Complaining it’s not this kind of a design technique merely elicits the question, “Why CAN’T you implement a design by alternating testing, coding, and refactoring?”

I think the insightful part of your post is the analogy to exploratory testing which is an approach rather than a technique. Can you take it a bit further? For example, if TDD is the analog to exploratory testing (and they are both approaches rather than techniques), what are the analogous design techniques that can be applied when you are taking a TDD approach?

I give a lot of TDD courses. And I almost certainly always hear the objection that TDD with its tiny steps towards a solution, with its detours because the current design does not hold for the next failing test is not as good as thinking the problem through and then just implement the solution.
Well, I say then, give it a try. I let one pair of the course members donit their way. And the results are always very impressive: the group using TDD is most of the time faster, but more important is the fact that the big design up front guys often get the algo wrong. And what’s even worse, they don’t have tests to see it.
Therefore, TDD does not help directly in designing software – this has to be done by the developer himself – but it gives you a framework within which you can try, refactor, extend, through everything away and strat over.
But if you start over, you are not back at square one, you have a test suite already.
And always remember that the really good solutions are never found in the first trial. TDD simplifies trying over and over again while ensuring that the functionality remains as is.