Test Driven Development – TDD
Maybe it has a lot to do with my “introduction” to TDD but I don’t get it. What’s the point of TDD?
I get writing test and think that’s a terrific idea. I’ve written a fair number of class libraries and, where possible, I’ve attempted to get as much test coverage as possible using automated tests. They’ve saved me on a number of occasions and, perhaps more importantly, they have made me think about what I am trying to achieve and to re-think the way I was proposing to achieve it.Problem is… I’m old. I’ve written a lot of code. I generally start out on a project by thinking about it. Thinking about it gives me a list of requirements. A set of requirements gives me a structure. A structure implies classes and methods and properties. So, when I start writing code, I know roughly what I need a class to do.
Not at all TDD.
I have been known to create whole, complete, functional classes before I write any tests at all. I know, shocking isn’t it. How can that be possible?
And that’s where TDD falls off my world map. I have worked with people who tell me that I cannot possibly write a class without writing tests for it first. These same people tell me that I must not create properties and methods in a class unless I actually need them. Creating methods that I know I will need is just not part of the philosophy.
But that’s what I do all the time. It’s part of my makeup. If I create a class to handle configuration, I know up font what methods I’m likely to need, so can get those coded straight away. I know the interface I want to present to the user, so I can code an interface file for the basic functionality. And I fail to see what’s wrong with that. Ok, there are times where I add a method and the signature has to change or the method may turn out to be redundant, but that happens way less than it does with TDD.
Maybe it’s the way I was introduced to TDD. In a previous life I worked for a company where we had to write an application ‘urgently’. By urgently, the deadline was a fixed date when a technician was going to turn up to a data-centre, turn off a machine, load it in to the back of a van and scrap it. We needed a replacement application written before that happened.
No pressure then.
Failure is Ok, Failure is good.
The guy who took the project on (who is a nice guy, no matter what I said to him at the time – just out of his depth) decided that we were going to use TDD techniques to construct this replacement. So, we had a team of five people who sat and waited while he wrote the first few failing tests. Why wait? Well, until we had those first (failing) tests written, we didn’t have anything else to write. TDD would not let us think past the first failing test.
He struggled for the first 30 minutes or so to come up with an appropriate failing test.
While he was doing this, I was sitting there thinking – I can’t help it, it’s just one of the annoying things I do. By the time we had a failing test, I had mentally designed the configuration options infrastructure and was moving on to something else.
The first failing test was fixed… it was hard coded to return true and the project was under way. So, he wrote another failing test. Bugger, our successful project had just fallen in a heap again. All a little too negative for me.
Ok, I could rant on like this for a very long time – honestly, I could (and probably did at the time) but that’s not constructive. Eventually, we ended up at the point where half of the team could go away and start writing failures of their own. By the time that happened, I had wandered off and started coding the configuration class that I knew everyone else would need pretty quickly. I was vilified for doing this as you never write code that you do not immediately need, but I’m old and played deaf.
A couple of days later, I was looking at my configuration code and found that half the team had already used and extended it with their own options. I continued to be vilified by the TDD brigade, but a little more quietly.
My fear of TDD
My fear of TDD in this example came down to two issues.
- Failure is Ok, Failure is good.
- Planning and thinking beyond the next method is wrong.
Failure is NOT Ok, Failure is NOT good
I started programming writing S/370 Assembler on mainframes. My code plugged directly in to the operating system. If I wrote failing code, then the operating system failed right along with it. That gave me an appreciation for planning, checking and rechecking code before I committed it to production use. Failure was not an option and, over the course of 10 years, I managed to never bring down an operating system with my code.
Fast forward several years and I find myself in a situation where failure is a requirement of the development process. Planning is dismissed with derisory comments about “waterfall”. And I struggle. I do not want to become the kind of developer who can casually accept that my code is going to fail, indeed that it should fail.
Planning and thinking about the whole is actually good.
On this particular project, I turned out to be one of the two highest code contributors in the end. My code turned out to be reliable and easily refactored when the requirements changed, as they do often when there is no coherent plan. How did that I manage that? I thought about a class before I wrote it, decided how I would want to use it, how it should expose its functionality to me. Then I coded it. Then I wrote tests to prove it did what I expected.
Was my code perfect? Far from it. No code is. I had to refactor some of my classes three or four times before they became stable. However, having decided on the structure of the classes, the API hardly changed, just the internals, and most of the changes were to add functionality that came from new requirements.
The final product
So, did the project fly in to production and beat the man with the van? Well, it went in to production with days to spare. It also went in with 50% of the functionality missing because we ran out of time. In my mind, had we done some planning up front, we could have delivered at least half of that missing functionality. But the cries of “Waterfall” drowned me out and the project was deemed a roaring success for delivering at all. We “lost” some clients along the way as they didn’t get the functionality they needed, but that was deemed Ok too.
Am I now biggoted against TDD? You bet your life I am.
Am I a waterfall advocate? Not in the slightest; waterfall is a dead development methodology, it does not fit this moderm world.
In my new position with my new company, I write and distribute code quickly and in small chunks. I get feedback and adjust the product accordingly. I hesitate to call it agile, I hesitate to call it anything other than good practice and working closely with your colleagues. My bosses love it as they get to shape the product at its earliest stages. For me, it’s business as usual. From my earliest days developing software I’ve lost count of the conversations where I have presented software and not a “no, that’s not it” response. Presenting early and tweaking the results is a way of life.