How to debug a program


Debugging is a fundamental programming skill. It's also one of the least fun and least glamorous tasks a programmer faces. In order to reduce time spent debugging, I've invested time in examining my debugging methods. Below, I've documented the techniques that most reliably lead me to a solved problem.

I'll avoid getting into the gritty details of particular tools and instead stick to basic principles that can be applied in almost any situation.

Reproduce the problem

The first step is to reproduce the problem. If you cannot reliably reproduce the problem, you will not know if you have successfully fixed the problem or not. Some bugs will initially be difficult to reproduce, such as race conditions. Often, the problem can be flushed into the open by adding extra code that checks assumptions along the way. In some cases, you will need to implement a verification function to walk a complex data structure and check it for consistency. Verification checks will slow the program down, but they can easily be commented out later.

Question assumptions

If a program does not operate in the way you believe it should, one or more of your assumptions about the code is wrong. Debugging is the art of identifying which of your assumptions is the culprit. Sometimes you will feel that a particular part of the code is suspect and begin investigating it. With luck, you're right and will quickly identify and fix the problem. However, if you start to feel like your banging your head against a wall, you're probably relying on wrong assumptions to guide your debugging methods. Take a step back and question.

Look at more than code

Suppose you've implemented a moderately complex algorithm, but it produces the wrong output. You look over the code, find what you believe to be a problem and change it. Now the program produces different wrong output. After you've done this a few times on the same code, you start to get the feeling that you're taking stabs in the dark. If you find yourself changing your mind about a "fix" and reversing it, you know you're lost.

To make progress, examine the state of the machine as the algorithm executes. Tracing through the execution with a debugger may be sufficient. In some cases, you will be best off by implementing a function that prints the relevant state of the program and calling the function at key points in the algorithm. By viewing the progress of the algorithm on actual data, you can pinpoint where it went wrong.

If you're not sure what the state of the program should be in the middle of the algorithm, step away from the computer and trace through the algorithm with pencil and paper. You cannot fix a program without a clear vision of how it should work.


Leave a Reply