The Dramatic Nature Of The Easiest Bugs To Solve
Given a programmer who’s good at their job — who knows the language, frameworks, and runtime well — the dramatic nature of a bug is in inverse proportion to the difficulty of the fix as long as the bug is actually in that programmer’s code.
In my experience, drama doesn’t make bugs either easier or harder to solve. Brent identifies many of the sources of difficult bugs. Some others – often restatements of Brent’s principles:
- Inconsistent: bugs that sometimes occur and sometimes do not are much harder to fix than those that are predictable. Race conditions are one common source of inconsistent bugs, but unusual usage can be just as effective. (You clicked where?)
- Delayed: bugs whose effects are separated from their cause are challenging. For example, an error when writing a file might be apparent only when reading the file.
- Distant: debugging a system that uses multiple threads, processes, or computers is hard, especially when it’s unknown which system is mistaken. For example, fixing a sync problem is harder than fixing a problem when saving.
- Inconspicuous: today’s users have been trained to avoid tech support when possible, and in consequence they may not report problems you would normally expect to hear about. They may also assume that a bug is simply the way things ought to be; in WW2, the US Army printed and used millions of “Indentification Cards”, presumably because everyone involved assumed that someone else knew how to spell the word.
- Cumulative. Some bugs, such as memory leaks, are tolerable in small doses and only become noticeable after the error has been repeated over and over. In the worst cases, a side-effect of slow-acting bugs may remove some of the evidence; once the crime scene is discovered, the footprints have been trampled.
- Fragile. It’s hard to fix bugs in complex, fragile, and poorly-tested systems. Refactoring and test-driven development have given us powerful tools to cope, but of course that means writing tests and refactoring a legacy system before we can really tackle the bug.
- Infallible. Conversely, excessive cleanliness can also hide ghastly blunders. The
goto
disasters earlier this year was a classic example. We’ve all had cases where the code said “=” when it meant “==”, where we checkedif(ready)
when we wantedif(!ready)
. These blunders are especially hard to pick up in small, clean classes. - Cloaked. Some bugs are tied to creating and dismantling the object or the system. These may be difficult to handle when they occur so early or so late in the process that your tools and instruments can’t see them. Related to these are bugs that appear only with specific and unusual system configurations or data, or bugs that appear only when the network connection goes haywire or only on Tuesdays. (Yes, I’ve had a bug that only happens on Tuesdays.)