How to debug small programs

One of the most frequent categories of bad questions I see on StackOverflow is:

I wrote this program for my assignment and it doesn’t work.
[20 lines of code].

And… that’s it.

If you’re reading this, odds are good it’s because I or someone else linked here from your StackOverflow question shortly before it was closed and deleted. (If you’re reading this and you’re not in that position, consider leaving your favourite tips for debugging small programs in the comments.)

StackOverflow is a question-and-answer site for specific questions about actual code; “I wrote some buggy code that I can’t fix” is not a question, it’s a story, and not even an interesting story. “Why does subtracting one from zero produce a number that is larger than zero, causing my comparison against zero on line 12 to incorrectly become true?” is a specific question about actual code.

So you’re asking the Internet to debug a broken program that you wrote. You’ve probably never been taught how to debug a small program, because let me tell you, what you’re doing now is not an efficient way to get that problem solved. Today is a good day to learn how to debug things for yourself, because StackOverflow is not about to debug your programs for you.

I’m going to assume that your program actually compiles but its action is wrong, and that moreover, you have a test case that shows that it is wrong. Here’s how to find the bug.

First, turn on all compiler warnings. There is no reason why a 20 line program should produce even a single warning. Warnings are the compiler telling you “this program compiles but does not do what you think it does”, and since that is precisely the situation you are in, it behooves you to pay attention to those warnings.

Read them very carefully. If you don’t understand why a warning is being produced, that’s a good question for StackOverflow because it is a specific question about actual code. Be sure to post the exact text of the warning, the exact code that produces it, and the exact version of the compiler you’re using.

If your program still has a bug, obtain a rubber duck. Or if a rubber duck is unavailable, get another computer science undergraduate, it’s much the same. Explain to the duck using simple words why each line of each method in your program is obviously correct. At some point you will be unable to do so, either because you don’t understand the method you wrote, or because it’s wrong, or both. Concentrate your efforts on that method; that’s probably where the bug is. Seriously, rubber duck debugging works. And as legendary programmer Raymond Chen points out in a comment below, if you can’t explain to the duck why you’re executing a particular statement, maybe that’s because you started programming before you had a plan of attack.

Once your program compiles cleanly and the duck doesn’t raise any major objections, if there’s still a bug then see if you can break your code up into smaller methods, each of which does exactly one logical operation. A common error amongst all programmers, not just beginners, is to make methods that try to do multiple things and do them poorly. Smaller methods are easier to understand and therefore easier for both you and the duck to see the bugs.

While you’re refactoring your methods into smaller methods, take a minute to write a technical specification for each method. Even if it is just a sentence or two, having a specification helps. The technical specification describes what the method does, what legal inputs are, what expected outputs are, what error cases are, and so on. Often by writing a specification you’ll realize that you forgot to handle a particular case in a method, and that’s the bug.

If you’ve still got a bug then first double check that your specifications contain all the preconditions and postconditions of every method. A precondition is a thing that has to be true before a method body can work correctly. A postcondition is a thing that has to be true when a method has completed its work. For example, a precondition might be “this argument is a valid non-null pointer” or “the linked list passed in has at least two nodes”, or “this argument is a positive integer”, or whatever. A postcondition might be “the linked list has exactly one fewer item in it than it had on entry”, or “a certain portion of the array is now sorted”, or whatever. A method that has a precondition violated indicates a bug in the caller. A method that has a postcondition violated even when all its preconditions are met indicates a bug in the method. Often by stating your preconditions and postconditions, again, you’ll notice a case that you forgot in the method.

If you’ve still got a bug then learn how to write assertions that verify your preconditions and postconditions. An assertion is like a comment that tells you when a condition is violated; a violated condition is almost always a bug. In C# you can say using System.Diagnostics; at the top of your program and then Debug.Assert(value != null); or whatever. Every language has a mechanism for assertions; get someone to teach you how to use them in your language. Put the precondition assertions at the top of the method body and the postconditions before the method returns. (Note that this is easiest to do if every method has a single point of return.) Now when you run your program, if an assertion fires you will be alerted to the nature of the problem, and it won’t be so hard to debug.

Now write test cases for each method that verify that it is behaving correctly. Test each part independently until you have confidence in it. Test a lot of simple cases; if your method sorts lists, try the empty list, a list with one item, two items, three items that are all the same, three items that are in backwards order, and a few long lists. Odds are good that your bug will show up in a simple case, which makes it easier to analyze.

Finally, if your program still has a bug, write down on a piece of paper the exact action you expect the program to take on every line of the program for the broken case. Your program is only twenty lines long. You should be able to write down everything that it does. Now step through the code using a debugger, examining every variable at every step of the way, and line for line verify what the program does against your list. If it does anything that’s not on your list then either your list has a mistake, in which case you didn’t understand what the program does, or your program has a mistake, in which case you coded it wrong. Fix the thing that is wrong. If you don’t know how to fix it, at least now you have a specific technical question you can ask on StackOverflow! Either way, iterate on this process until the description of the proper execution of the program and the actual execution of the program match.

While you are running the code in the debugger I encourage you to listen to small doubts. Most programmers have a natural bias to believe their program works as expected, but you are debugging it because that assumption is wrong! Very often I’ve been debugging a problem and seen out of the corner of my eye the little highlight show up in Visual Studio that means “a memory location was just modified”, and I know that memory location has nothing to do with my problem. So then why was it modified? Don’t ignore those nagging doubts; study the odd behaviour until you understand why it is either correct or incorrect.

If this sounds like a lot of work, that’s because it is. If you can’t do these techniques on twenty line programs that you wrote yourself you are unlikely to be able to use them on two million line programs written by someone else, but that’s the problem that developers in industry have to solve every day. Start practicing!

And the next time you write an assignment, write the specification, test cases, preconditions, postconditions and assertions for a method before you write the body of the method! You are much less likely to have a bug, and if you do have a bug, you are much more likely to be able to find it quickly.

This methodology will not find every bug in every program, but it is highly effective for the sort of short programs that beginner programmers are assigned as homework. These techniques then scale up to finding bugs in non-trivial programs.

121 thoughts on “How to debug small programs

  1. Pingback: Iterate through specific rows sql php - CSS PHP

  2. Pingback: php - Mysql query takes a lot of time to execute - CSS PHP

  3. Terrific blog post! I use, or have used, many or all of these methods at one time or another to debug code. They definitely work. I would add one method to the list, although you already touched on it. If possible, developers should use several debugging tools on their code, frequently, sometimes even if the code is not throwing errors or appears to be working correctly. I’m thinking of static and dynamic debuggers like Purify, gdb, valgrind, Allinea, etc. or whatever tools are appropriate for your chosen language. It’s amazing how many times these tools identify (potential) bugs and software defects before they manifest themselves in production code. Look forward to future posts.

  4. Excellent post, i particularly use the rubber duck approach very well. in some cases when all debugging techniques and approaches fail, it’s best to just take a walk and forget about it (very difficult though). the second you come back to the code, the bug would fish itself out 🙂

  5. Pingback: Software Debugging Process | Process Street

  6. if you find yourself thinking – ‘that’s a bug in the compiler’, ‘thats a hardware fault’ or ‘thats a bug in the OS’ – you are wrong. Look again. Once you are really sure and you still think that its compiler, hw or OS, you are still wrong. Look again. Repeat

    • Well, would disagree. But yeah, unless you are *really* sure it is a compiler bug and have eliminated every other possibility, it’s probably you.

      And ever after I ran into problems with a bad RAM stick once, I recommend doing an overnight Memtest if your dev machine isn’t using ECC and you’re seeing impossible things. If it finds a problem it was worth the time and meanwhile rest your brain.

    • (Note that if you actually cause the compiler to crash, and emit messages about internal errors, there may actually be a bug in the compiler. This isn’t guaranteed, though.)

  7. My favorite way to debug small programs is print statements. If you move the print statements around enough, you will eventually find exactly where the problem lies.

  8. Another preemptive attack at bugs is — debug before you “run”. After writing new code, fire up your debugger, and step through some test cases (which should include corner cases, as well), inspect intermediate results, check some internal states. You usually will find bugs that would never ever surface, but only if Murphy would’ve been wrong 😉

    If finding bugs late in the process is so expensive, why not search for them before the first “run”? Start with a debugger session! I do this all the time, and this way I find more bugs earlier than I ever expected.

  9. Pingback: Coding Utilities –

  10. Pingback: How to fix 'Index was outside the bounds of the array' | How Why What

  11. Warnings are the compiler telling you “this program compiles but does not do what you think it does”

    I don’t really understand…I am new to programming but I don’t think that compile-time warning tell you that the program does compile but doesn’t work the way you want it to..aren’t compile-time warning meant for pointing out compiling typos in code? Or is he talking about some different kind of warning?

    • Compiler warnings are different from compiler errors. The compiler outputs an error when it *can’t* compile your code — you’ve got a typo; you’re passing the wrong number of arguments, etc., etc.

      A *warning* is the compiler telling you: “this code looks suspicious; it’s valid code, but it probably isn’t doing what you think it is”. In C, for example, “if (x = y) { /*…*/ }” is perfectly valid code, but you probably didn’t really mean to type that.

  12. Hi Eric – this is a great reference for beginners and I often link to it from StackOverflow. Unfortunately it seems that the links no longer work with http:// – it seems that https:// is now required, so all my previous links are broken. Is there any way to fix this ? Thanks again for a very useful resource.

  13. From my perspective, paying attention to what the compiler error warnings and errors actually say is very important (as well as being able to Google that exception if you don’t know exactly what it means or how to solve it). I’ve seen many bad questions where the OP asks about a compile error and the problem is EXACTLY what the compiler says it is.

    For example, one question I recently fielded included the following two error messages: first, “The Type or namespace name ‘List’ could not be found (are you missing a using directive or an assembly reference?” Here’s a shocker: he was, in fact, missing a using directive (System.Collections.Generic).

    The second compiler error: “No overload method ‘Read Line’ takes 1 argument.” The error: he was trying to pass an argument to Console.ReadLine(), so again the error was exactly what the compiler said.

    Short of that, if it compiles without errors or warnings, I always encourage people to just step through the code – pay attention to what happens at each line and make sure that it’s doing what you expected it to. I’ve fixed many bugs like that.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s