For a couple months now, I've been spoiling myself by spending the bulk of my time reducing my world's entropy in three main ways:

1. Automate more of what I do
2. Organize more of what I know
3. Learn more of what I don't know

I've begun exploring the boundaries of my attachment to Test-Driven Development. How did I get so attached in the first place? In large part, because the thinking process I've learned and applied by deliberately practicing TDD has, in the contexts I've experienced, almost always been an adaptive behavior. This is the first anecdote in a series about TDD's value to humans.

My confidence

Claiming I do my job better with TDD is equivalent to claiming I do my job worse without it — which, last I checked, was true.

Shortly after graduating with my music degree, I was hired as a software developer in a highly regulated, tightly controlled corporate environment. The tools I was hired to develop had begun life as a proof-of-concept system to manage certain kinds of identities in a heterogeneous distributed network. They put it in production. It was a roaring success. Suddenly lots of customers appeared, wanting lots more use cases, business rules, and platforms added. Suddenly the security engineers realized their proof of concept had a long life ahead of it. It would need to be managed and developed as a software product.

They may have been confident in my fitness for the job, but I sure wasn't. While I'd been a longtime contributor to some significant open-source projects, I hadn't been a professional software developer since 2002. And that had been my first programming job. I'd learned a whole lot very quickly, including the rudiments of TDD and other XP practices, and I was probably worth what I'd gotten paid, but I had not, by any means, mastered much of anything.

My first assignment

To their credit, my superiors had devised for me a relatively gentle introductory exercise: when an identity is no longer needed, provide a way to “disable” it. I did not need to devise a mutually authenticated client and server that spoke a network protocol in order to manipulate the server's database of identities by issuing commands to be processed or rejected according to business rules. I didn't even need to tweak the database schema; it already contained the needed fields. I just needed to add a “disable” command. Reading through the “create” and “update” commands, I copy-paste-edited a new “disable” command. Then I figured out how to run my copy of the server, how to point my client at it, and how to prove to myself that my copy of the database correctly marked a given identity as disabled. (When I demonstrated my progress, I found out there was considerably more to it than that. A great deal of much-needed handholding occurred.)

Then it dawned on me: I bet a bunch of the other commands need to behave differently when asked to operate on an identity that's been disabled, now that such things exist. Taking my best guesses at those behaviors, I edited the SQL backing those other commands until they probably handled both the old and new cases. Since I didn't understand how most of the commands were intended to be used, let alone how to verify whether they worked, we code-reviewed my very simple changes. We agreed they looked fine.

My first deployment

Guess what happened when we put my code into production? That's right: my new “disable” command worked, but users complained that several other commands no longer did. It was easy to spot the mistakes, now that we knew where they were, and to make the obvious fixes. We scrambled to redeploy.

Before I tell you how the postmortem went, remember the context:

1. The environment was highly regulated and tightly controlled
2. The code was security-sensitive
3. I'd been uncertain of my ability to do the job before screwing up in production

”Bill,” I said, because that was Bill's name, “if you need me to avoid screwing up like that again…”

”Uh huh,” replied Bill.

”…then I need to be doing Test-Driven Development.”

”Listen,” said Bill. “Do whatever you think you need.”

I did. And over the several years that followed, I never screwed up like that again.

Before (code pouring all over the place) and after (code pouring cleanly through the TDD funnel)

Illustration by Rebekka Dohme.

My conclusion

You might suggest that that's because I quickly learned the problem domain and our system's model thereof, remembered how to write basic SQL, and made a habit of checking my assumptions and my results more carefully. You'd be right. You might suggest that I could have done all that without TDD. You'd be right again. But I'm fairly sure I did them sooner, better, and more reliably — all of which felt crucially important at the time — because of TDD.

As near as I can tell, TDD saved my job.