Two questions to ask after a bugfix

As a programmer, you probably spend large part of your time by fixing bugs. The workflow is always the same: Debug a problem, find its cause, prepare a fix, merge it into the codebase, mark the underlying issue as resolved — and you are done. Right?

Well, not quite. There is one important step missing. At the very end, you should always ask the following two questions:

  1. Can this bug occur elsewhere?
  2. How could it have been prevented?

Let’s have a look at them in detail.

Can this bug occur elsewhere?

Code often contains repeated patterns that can’t be easily abstracted away. If you fix a bug in one instance of such pattern, it’s worth trying to find the other instances and fix the bug there too. For example, when you add a missing NULL constraint for one attribute in one database table, it makes sense to check the whole schema for similar omissions.

When fixing all the instances you’ll often discover deeper problems. For example, in case of NULL constraints you can find out that you allow/disallow NULLs rather randomly — there is no consistent policy. The fix is not just making the schema consistent, but also establishing this policy and documenting it.

Which leads us to the second question…

How could it have been prevented?

Every bug was introduced because somebody made an error. It’s worth investigating why that error happened. Was it a simple omission? Was it a copy and paste error? Was it an unintended consequence of some other change? Was it because of wrong assumptions of the implementor? Was it because of poor understanding of the code?

Often there are multiple causes of an error. They form a chain, where each step is derived from the previous one by asking “why”. If possible, establish at least first few links of this chain. 1

Once you have done that, try to think of ways how the bug could have been prevented at each level. Then implement as many of these fixes as you can. The best solutions cover the deepest links of the chain, which means they solve relatively generic issues and prevent whole classes of bugs.

Here is an example (in a form of an imaginary conversation):

A: We had a cross-site scripting bug at our website.
B: Why?
A: Because the front-end programmer forgot to call the HTML escaping function.
B: Why?
A: Because he is a human and humans forget things occasionally.
B: Why?
A: Because of how their brans are wired. It can’t really be changed.
B: OK, let’s stop here. Because we can’t change how human brain works, we need to adapt our tools to fit it better. Let’s change the templating code to escape everything by default so the escaping can’t be forgotten.

You see? We ended up with a generic fix preventing a whole class of problems.

Conclusion

Asking questions discussed above after each bugfix will naturally guide you from one-off fixes to discovery of deeper problems with your code, processes, or maybe your team. If you are diligent in fixing them, your bug rate will go down over time and you’ll end up writing better software. And that’s what you want, isn’t it?


  1. In reality, the chain is often a tree, because there are multiple valid answers to some of the “whys”. But let’s stick with the chain metaphor for simplicity. ↩︎