Friday, May 4, 2012

Essentials of debugging

Debugging software is challenging. Without a process to follow, resolving problems can seem impossible. Most inexperienced programmers find themselves in precisely that situation when confronted with a bug. In this article,  learn the art of debugging and highlight six essential elements of the debugging process.


1 > Reproducibility. First, find a way to reliably reproduce the error, which, in itself, often points you straight at the problem through deductive reasoning. The bug may happen in precisely one circumstance, which can only happen in one place in the code. A bug that appears randomly is essentially unsolvable unless you have a leap of insight. You need a guarantee of cause and effect to make inferences about changes you introduce. A change in the code that fixes the problem may or may not really fix it, as the problem randomly appears and disappears.

2 > Reduction. Reduce the problem to its essence. Determine the smallest input that will cause the bug. The simpler the data or path to the bug, the more easily you can deduce or track down the problem. A large data set introduces a great deal of noise that camouflages the essential cause of the trouble. If you have a large data input file that causes the problem, do a binary search type reduction. Cut the file in half, throwing out the last half. If you still have a problem, then you can ignore the final half. If the problem goes away, then start whittling down the final half, as it must contain the input that causes the problem.

3 > Deduction. This is your primary weapon once you have a small input that reliably causes a problem. What is the general path through the program used by the input? What components might be the problem or mangle the data so that a future component fails? What is the difference between the input that doesn't work and some other input that does? Try to reduce the scope of possibilities by forming and eliminating hypotheses. In a sense, this process is similar to that followed by experimental physicists, who try to explain natural phenomena with a theory or an equation. To support their claims, they carefully design experiments that, if successful, have only one likely explanation -- namely, their theory. Other physicists try to reproduce the results to verify or refute the hypothesis.

4 > Experimentation. Psychologists study the human mind by testing it in varying situations with different stimuli. They use brain scans, response times, and so on to support their hypotheses about how human brains work. Similarly, you must change the conditions of the test to see if your bug disappears. If you correct the bug with a change, that change might tell you what the problem is, or at least give you a big hint about what is going on. You form hypotheses with your logic and deductive reasoning skills and then filter them by experimentation and observation.

5 > Experience. Experience has no substitute. To become a good programmer, apprentice yourself to a good programmer or fumble your way through it yourself for a few years (making lots of mistakes either way). Experience helps in the debugging process in two ways: first, you hone your ability to execute the previous four elements; and second, you might have seen a similar bug or just plain know more about a particular problem. Borrowing the experience of other developers is also important. Searching the Web and talking to other developers can save you a huge amount of effort by leveraging other peoples' experience. Interestingly, just explaining the problem to another developer (or even your spouse or a friend) can line things up properly in your head so that simple deduction tells you where the problem lies.

6 > Tenacity. All bugs are caused by computers doing exactly what they are told; absolutely no mystery in that, and hence all problems are solvable. You must begin with the attitude that you will never give up no matter how long it takes to find the problem and correct it. You will become more and more confident each time you solve a problem. You must be tenacious. I have never, ever left a bug unsolved that I considered worth fixing. Sometimes the lengths you have to go are extreme, but result in good war stories. In the process of solving insidious bugs, you will become a much better programmer as you start to anticipate errors and to code in a manner less likely to produce mysterious bugs.
In practice, these six elements are used in combination and their sequence may vary, though points 1 and 2 make sense to do first.