Working on a team that’s not yet onto the value of unit testing, I frequently encounter what Michael Feathers calls “legacy code.” It is not unit-tested and can’t be. That doesn’t mean I need to forget test-first.
When you have a programming problem, what’s the first thing you should do? Here’s a hint: It’s not what they taught you in school. In school, they taught us how to analyze the problem and code a solution. That’s how many engineers approach a programming problem. They start by thinking how they’re going to solve it, and then they start writing the code to do so.
But there’s something that you need to do first, and it’s a prime reason why junior software developers churn out bad code. And if you do this thing, it will immediately set you above your peers. You will work faster, and your code will be better.
When I was much younger, I was working on diagnostics and production-test software for electronic musical instruments. We had this cool, automated hardware-test system for our production line. On one project, the software was basically done. The factory requested just one tiny change. It was trivial. The change took longer to recompile than to code. I released the new version of the software and sent it off.
The next day, a critical bug appeared. I looked into it and discovered that the change I had made had a significant typo in it, which was causing the software no longer to work anything like it should. I had released a change without testing it.
Robert, my manager at the time, asked what happened. After I told him, he simply said, “Don’t do that.”
I said, “I agree.” I fixed the code, tested it, and released a new version. Pray that if you ever make a mistake like that, you’ll have a manager as enlightened as Robert.
Given a programming problem, before writing a line of code, before even thinking about how you’ll solved the problem, the first thing you need to do is to ask, “How will I know when the solution works?” In other words, how will you test your code? And how will you know when you’ve written enough code to solve the problem?
Then perform those tests. Watch them fail. Confirm that the feature you need to add really needs to be added. Confirm that the bug you need to fix really exists. Confirm that you understand how the system currently behaves so that you will know how the code you’re about to write affects it.
Then, while you’re actually writing code, continue to ask yourself, “How will I know that this line of code works?” Make sure you test each and every change you make to the codebase. And test frequently. And when you have enough code to implement the full feature, stop coding. You’re done.
Few things are more frustrating than to hunt down a bug only to discover that it came from the source-code repository and that the person who put it in there obviously didn’t test their changes. Don’t be that person. Even if you can’t use automated tests, do practice test-first.