While discussing our natural tendency to spend too much time and effort refactoring code, Jul (aka -jul-) raised an interesting point:

This is why I think TDD is good. According to the rules, you should not write a single line of code before having a failing test, but you should not write more than one failing test. Then write some code which makes the test pass, but then, start over (eg. don’t write code). If you follow this cycle, you won’t code l’art pour l’art. Haha. Odds are you’ll end up writing tests l’art pour l’art.

— Jul, commenting on 7 Reasons To Hate Your Code

Note for those coming from digg: “l’art pour l’art” can be translated as “just for the sake of it”. Much love, my friends.

When I discovered TDD back in 2003 it came like a breath of fresh mountain air sweeping through the midst of my abstraction-freak analysis paralysis. At last I could code relaxed again, without trying to over-engineer every single feature. It set me free.

Years later I couldn’t help noticing I was writing more test code than program code and that I’d stopped working on side projects, because every time I started I thought:

Ok, I’ll hack out a prototype for this cool little game in no time. So, well, I guess I’d best start by writing a few tests for the core mechanics. Right, time to get my framework out. And… uh… ugh, maybe I’ll go watch some Buffy instead.

Writing tests turned me off the whole enterprise so much that eventually I decided I’d just write without them. And nothing awful happened! In fact, it was as much a breath of fresh air as I’d first felt when I started. What gives?

It wasn’t until reading Jul’s comment yesterday that I realized why this was: I hadn’t stopped doing TDD, I’d just stopped doing the T part.

Essentially, the core of many test-driven development processes looks something like this:

Make a list of features and prioritize them Pick the most important or fundamental feature Write test cases for it and implement just enough code to make them pass Repeat form step 2 until you’ve got something useful

I hadn’t stopped doing this, I’d just skipped the “write test cases” part. Writing just enough code to implement the tests you would have written turns out to work just as well!

Let me be clear – I both like and appreciate unit tests. There are many places in which I still use and rely on them. You’d be crazy to skip the unit tests when you’re writing a complicated translation function, or implementing a novel data-analysis algorithm. However, it was still an eye-opener for me to realize that there are places in which they’re a waste of time. Shock! Call Kent Beck! No, it’s true. Albert Einstein backs me up:

Make things as simple as possible, but not simpler

— Albert Einstein (somewhat paraphrased)

Thanks, Al. In this case, testing your accessors and adding half a dozen interface patterns just to support a burgeoning test suite isn’t as simple as possible. Writing no tests at all for your parsing code is too simple. Finding the perfect level of minimalism between code, tests and bugs is difficult, but liberating.

The truth is, doing TDD for all those years changed my programming style, moulded it into a new, more efficient form that stays efficient even when I stop writing tests for everything. I guess this is common; maybe you should try abandoning your test-first framework for your next side-project and see how it works out for you.

TDD without the T: you may find it surprisingly refreshing.

Note: Yield Thought has moved to http://yieldthought.com – check there for the latest posts!