Updated: Apr 10, 2022
In our first code debugging guide post, we described different types of bugs and best practices for debugging deterministic crashes by focusing on the reported error message. In this blog, we describe another technique that should be in your toolbox - understanding what your code is actually doing (which may be different from what you thought it was doing when you wrote it!)
This type of debugging can be used for every type of bug, but it is particularly useful for bugs where the code is failing in a deterministic way (i.e. doing something you did not expect and doing it every time). Here is a simple example. You wanted to add up all the numbers between 1 and 5. The code below, however, returns 10 when the sum of numbers (1,2,3,4,5) is not 10 but 15. What is wrong?
count = input("How many numbers to add up? ") sum = 0 for i in range(0,int(count)): sum = sum + i print(sum)
When this code is run, the output is
How many numbers to add up? 5 10
Now - this code is simple enough that you may already know what is wrong - but using a step-by-step process to debug it is still a good idea since that same process will help debug errors that are less obvious. What are the steps?
First, we know that the sum is calculated by the loop. Let's confirm for ourselves that the loop does what we expect. To do this, we will add a print statement inside the loop that prints out every number that is being added at each iteration (run) of the loop. Once we add this, the code looks like this:
count = input("How many numbers to add up? ") sum = 0 for i in range(0,int(count)): print(i) sum = sum + i print(sum)
Now, let's run this program. We can see that the prints show
How many numbers to add up? 5 0 1 2 3 4 10
So - this is not what we expect! We wanted the numbers 1-5 but we are getting 0-4. Why is this?
Now - let’s look at the loop more carefully. We can see that the way it is written, it will only run till 4. Now - how to fix this? We can set the range of the loop to one plus the number we want. So - let's do that. This gives us the code
count = input("How many numbers to add up? ") sum = 0 for i in range(0,int(count)+1): print(i) sum = sum + i print(sum) Now lets run the code again with the print still in place. It now shows
How many numbers to add up? 5 0 1 2 3 4 5 15
Ok! Issue fixed! Now we can remove the print
count = input("How many numbers to add up? ") sum = 0 for i in range(0,int(count)+1): #print(i) sum = sum + i print(sum)
What are the key things you should learn from this process?
Examine what the code is doing at key points to figure out if the actual execution matches your intent.
You have to decide WHAT to examine. For example - in the above code, if I examined the variable count, it would have only confirmed that the program got 5 correctly as the input. It would not tell me anything about how the loop was functioning. Deciding WHAT to examine is a skill that you will develop over time. The best way to do that is to think about what your program is supposed to do and find the variable (or variables) that will best expose whether it is doing what it is supposed to do.
Once you have figured out WHAT to examine, the next is the HOW. How you examine the code is up to you. In the above example, I used a very simple technique - which is to add a print() wherever I needed to see the value of a variable. This is not the only way though. There are many debuggers available for different code environments that will let you step through code, examine variables, configure the code to stop at specified places (called breakpoints), and more. Over time, you will likely learn many of these tools and find your favorites.
The key, however, is that all these tools will help you in how to examine your program. but none will tell you what to examine in your problem. That you have to decide for yourself. Only then can these tools help you. You also need to understand your intent well enough to know if the variables have the values you would expect if your program was running correctly.
If you are new to coding, my recommendation is to start with print statements. As your programs become larger and more sophisticated, you will find that using print everywhere will become tedious. At that point, you should consider using debugging tools. We will discuss those further in a future blog post.