(posted with permission from Jon Reid at qualitycoding.org)
I must of first heard about TDD around five or six years ago, pretty much as I was taking a temporary furlough from IT to set up a microbrewery. When I came back to IT as a profession after around three years away I was working with Objective-C in Cocoa and Cocoa Touch. This is not an encouraging area to try TDD because the majority of iThing programs are highly focused on UX. My impression was that TDD does not have a lot of leverage in this area. It struck me at the time that Behaviour Driven Development (BDD) might be a better choice and indeed I’ve made serious use of the Kif suite in the past.
Forward to the present day, when I have a component of a product suite, relying on some UX software that already exists, and some time to make an experiment. I was inspired by some postings and a screencast by Jon Reid at QualityCoding.org . He has a series of amazingly helpful screencasts on how TDD can be used in a non trivial way, and how it can be used with the pretty ubiquitous UIView and standard UIControl objects that litter the Cocoa Touch workspace API.
At first progress was almost painfully slow. I kept track of things and was horrified to discover that my total output for the first day was a risable 15 lines of production code along with around 100 lines of testing code. To be fair, the day did involve a certain amount of time reviewing the screen casts, downloading libraries, and so forth. Unfortunately by the end of the week I was still only managing 50 – 75 lines of production code a day.
I mentioned this in passing to Jon Reid himself – he was kind enough to respond to a technical query – and he told me that it takes around three months to master. Three months! I’d budgeted three days….
Well, perhaps that was naive of me, and in fact the early results were intruiging enough to encourage me to persist. What had happened was that after several days of TDD, I’d written a single line of code that caused a cascade of actions. The impact involved five or six classes, a remote server and some tricky failure modes. I would normally expect to write the final line and then fire up the debugger to get it to actually work.
Except that it did just work.
I haven’t written a piece of code that worked first time, since I gave up functional programming languages so this was a pretty notable event. On the other hand I did have to balance the substantial effort and cost involved in getting this far. I’ve been able to get a rough and ready measure the costs involved because I chose to recreate one class that I already had available from another non TDD project.
My original class had 32 LOC in the .h and 111 LOC in the .m files (excluding blanks and comments). My TDD version had 32 LOC in the .h, 94 in the .m, and 246 LOC in the tests, effectively TDD was tripling the effort involved. The production class was actually slightly smaller because it did not contain the fairly involved description method that was in the original. Since I wasn’t doing a lot of logging or debugging, it was never needed. My impression is that this ratio has held true throughout the project so far.
I think at this point, if I’d been asked anything about the issue of code quality I would have claimed that the actual code produced was marginally better. My TDD code certainly handled errors and failures better, but had poorer information hiding and some dependency injection that I would not normally have bothered with. So six of one, half a dozen of the other.
Then my tests found a bug that I don’t think I would have spotted until the code hit the field.
It was a complicated problem, revolving around caching, and although I can’t prove a hypothetical, I’m pretty sure that I would not have noticed the issue until the code was in the client’s hands, and then only under a fairly unusual set of circumstances – changing a server IP address for example. In other words, the kind of problem that appears after around a long period of constant use, and can only be solved with a great deal of forensics, and long after I’ve forgotten how everything works. The sort of bug that costs. Big time.
This has allowed put a different perspective on my views of the costs of TDD. Before my experiment started I believed that TDD was expensive in terms of time and developer effort. At this point my position has shifted, I now think that TDD shifts costs around. It makes the creation of code more expensive but reduces the costs of debugging and deployment. I can’t say at the moment whether the resulting systems are cheaper over the long run (although there are certainly plenty of people out there who will make that claim). I’m still of the view that trying to do a lot of UX tests using TDD is hard.
On the other hand, I am convinced that the overall quality of my code is improved. Right now I’m planning to use TDD _and_ BDD in production systems in the future, and fire and forget for my prototypes.
Work continues on the CardBoard app, but there’s nothing much to see this week. I’ve been concentrating on improving some internals so that there is a firm base for the next step.