“If debugging is the process of removing software bugs, then programming must be the process of putting them in. ” – Edsger Dijkstra
As close as you can get to a guarantee in programming is that if you’re building a program of any kind of complexity, you’re going to be spending a lot of time debugging it. Taking Eagleson’s Law* into account, the situation is even more dire – you’re almost always debugging code that might as well have been written by someone else. How would you prefer past-you to have written code so that current-you has an easy time fixing anything that comes up? Besides writing tests, of course, you would prefer that they wrote the code to make the process of debugging as easy and fast as possible.
“ Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. ” – Brian Kernighan
The code needs to be simple and easy to understand. You can get some of the way there with tools like Code Climate, but the simplicity I’m talking about here goes beyond what static analysis can really quantify. Not only do you need to worry about the low level details of your methods and classes, but you also need to worry about your overall design. Imagine explaining the design to someone new on your team. If the explanation involves layers upon layers of new classes, while not obviously wrong, you should probably get up and walk around a little bit asking yourself if there is absolutely no other way to accomplish what you’re trying to accomplish.
Beyond a simple overall design, think about each line you write from the perspective of someone unfamiliar with the code reading it for the first time. Consider the `try` method in rails. Any time someone encounters it being used, they now have to incorporate the thought that the caller could be `nil` and have to think about what cases that could be true. Unfortunately, the `try` method doesn’t provide any of the information needed to determine when and whether the object is legitimately null. You should write code that clearly explains to the reader why it is doing what it is doing.
Debugging time increases as a square of the program’s size.
— Chris Wenham in Physics for Programmers
To optimize for debugging speed, after taking simplicity and readability into account, you need to write as little code as possible to accomplish your task. Steve Yegge argues that size is Code’s Worst Enemy, but it is also debugging’s worst enemy. There is plenty written about brevity being the first thing to optimize for, and from a perspective of making the code as easily debugged as possible you absolutely need to focus on expressing ideas in as little code as reasonably possible.
You’re always going run into cases where, like Jeff Atwood mentions above, you’ve completely exhausted all your other options and have to write some code under duress. In these cases, just writing a small amount of code isn’t enough, there is something much more dangerous to watch our for. You need to think about the other code that people are going to need to write to interact with yours, and minimize future-code as much as possible too. Not only do you need to fight the urge to not bring new lines of code into the world, but you need to make it easy for the users of your code to do the same. If you’ve just written a small readable class that now requires all its users to write hundreds of lines of setup just to use it, you’ve still failed. You are now responsible for bringing an immeasurable number of lines of code into the world over the lifetime of the project.
* Eagleson's Law: Any code of your own that you haven't looked at for six or more months might as well have been written by someone else.
One thought on “Optimize for Debugging Speed”
I think this is totally spot on. One dimension that I think is missed (or may just be out of scope), though, is trying to push back on complexity at specification time.
Too often, I’ve seen product owners (generally at the very early stage) ask for features whose business impact did not justify the inherent complexity increases in the application, no matter how well the code is.