Practicioners of Test Driven Development (TDD) have received some flak lately. You may have seen Jim Coplien talk about this – at JavaZone last year he used the opportunity to deploy somewhat of an all-out attack. I’m a TDD fan myself, but I found his criticism targeted at the practice of TDD – or rather the practice of TDD and nothing else – not at TDD itself. Or something.
The main argument against TDD seems to be that it doesn’t help you grow a coherent architecture. Test-driven code could very well turn out a messy collection of disparate, scattered code snippets, with no hint of an over-arching common idea, other than the fact that they are all “use cases” somehow. Therefore, even if you practice TDD, you need to grow the design as well. If that is the argument, then well, duh. The design has never appeared by itself, not before TDD and with it.
Oh, and before we proceed: For anyone with “architect” in their title and conscious about it, just subsitute “design” with “architecture” in the above and below. OK? Proceeding…
But what can you with TDD if the design is already there?
Some context: My project recently started the work of porting a huge Java code base to C#. I was deep into the Java infrastructure layer, built on OSGI. I could go into it, but I’ll save that for later posts. The point is simple and it is this: The design was there, and it was in fact documented to some extent, but many details were in my head.
It was also a fact that I was the one with the most insight into this design, and that I was to leave the project only a few months into the porting process. Everyone agreed that we should mostly reproduce the infrastructure, to facilitate the porting of the actual business components – the POJOs, now to be PONOs – right? In short: picking my brain was high on everybody’s list.
After having everybody reading up on .NET for a while – it was new to everyone – I got impatient and jotted down a quick, OSGi-like core in C#. Again, I’ll leave out the details, but it turns out to be not so much work to write a simple, bootstrap OSGi-ish thing, to get us started.
Phrasing this as a methodologist would if there were such a thing as Vertical TDD: Then I put on my Test Driver hat, and launched the Vertical TDD. As you might guess, being Test Driver means you only do the first step of TDD: Write tests. After writing a test, instead of moving horizontally into the “make it pass” step, you drop down, vertically, and write another test. Point your co-workers to the failing tests and have them make them pass. This leaves the next two vertical columns in their hands: Make pass, refactor to remove duplication.
This seems to have worked well because:
- The tests had a clear function. They were a way to trace the contours of the design as I knew it, concisely listing the most essential features and behaviors that I knew we would need.
- They compelled my colleagues to re-create my design to some extent, and thus create a much deeper understanding of it. This is a lot more effective than sitting through presentations or reading design documents.
- The tests were an excellent checklist for tracking progress – each test would describe a certain feature, or a failure mode that we were able to handle. (Like non-compliant components.) Everyone could see green checkmarks flourish as progress was made.
And so on. All the usual arguments for having tests apply, but the main benefit here was the knowledge transfer. Not by reading about it or being monologued to about stuff, but by actually reconstructing it. Once the framework as starting to manifest in everybody’s heads, it became easier to outline remaining features on a higher level. People were developing the mental “hooks” required to see where everything fit in. In contrast, reading high-level documentation at an early stage, before any hands-on experience, will tend to produce whooshing sounds over your head.
You may find that making tests pass is considered more fun than refactoring out duplication. To counter this, I read up on the code and wrote more tests that exposed bugs of two kinds: One where one duplicate has it but not the other, and of course the one where the bug has to be fixed twice. Both drive the point home: Duplication is bad.
So what’s the lesson? It could be this, for instance: If you have a clear idea about a design in your mind, you can drive it through by providing the groundwork for it, and using TDD to help your co-workers fill it out. Of course, this is not a case for Big Upfront Design; you need a working, well-understood, accepted design. I may be using a slightly too strong definition of “clear idea” here, since it means “having worked with it for a couple of years”.
Anyway, I think this was a good experience, and it may even have been encouraging to anyone who’s been put down over their dangerously-veering-on-unfashionable TDD recently.