Chapter 13
IN THIS CHAPTER
Finding and reading error messages
Tracking down common bugs
Debugging strategies
Bugs have been causing coders headaches and frustration for decades. Coders spend upwards of 50 percent of their time debugging — fixing errors in their code, and not just writing code! This is part of what makes coding so “hard.” It’s not impossible to figure out — it just takes persistence when the computer is telling you you’re wrong.
You can encounter two different kinds of coding bugs: syntax bugs and semantic bugs. Syntax bugs are ones where you have written something incorrectly. Maybe you spelled a variable wrong, or you forgot a semicolon at the end of your line of code. These bugs are often easy to fix and result in your programming environment displaying an error message to you. Semantic bugs are ones where your logic is incorrect. For example, you’re iterating through a list of five numbers and you try to access the sixth element, or you’re checking to see whether variable1
is less than variable2
in an if-then
statement, but you accidentally write variable1 > variable2
. These bugs are often harder to find because they require you to test your code and see what happens, and then trace your code to find where the error is located.
When teaching and guiding young coders through their coding journey, it's important to help them understand that errors, bugs, and mistakes in coding are normal and expected. Performing the debugging process is a huge part of being a coder, and learning strategies, patience, and persistence is critical to coders if they are to succeed in their coding adventures.
If you haven’t heard of the infamous Admiral Grace Hopper and how she coined the term “bug” for an error in a computer program, you should definitely read the sidebar about her in Chapter 5!
Teaching your young coder about debugging is also about teaching her that computers are very precise. When we’re introducing this concept to young coders, we like to present them with the following scenario: If I told you to go to your shoes and put your room on, you could probably figure out that I meant to say go to your room and put your shoes on. But if I were to tell a robot to go to its shoes and put its room on, it would literally walk over to its shoes and then try to put its room on…which is impossible! And it would probably tell you that it cannot complete the action, or maybe even try so hard to “put its room on” that it just crashes.
As a fun activity to do with a group of young coders, you can play a game of “Describe the Picture.” In this game each person is given a unique picture drawn on a grid. Each person keeps his picture hidden from his partner. Then, each person is given a blank sheet of grid paper.
In one version of the game, each partner takes a turn describing his picture to his partner using only descriptive language (not showing them the image). He has five minutes to describe the picture while his partner tries to re-create it on the grid paper. Then they switch who is describing and who is drawing. At the end of the ten minutes they show each other their drawings. Most of the drawings will be incorrect, and at that point you can talk about what went wrong and how hard it is to be precise!
In another version, you can add an element of debugging to the end of the game. Rather than describing the picture to their partners, each person writes a series of steps that his partner should follow to re-create the picture. For example, if a coder is given the picture shown in Figure 13-1, then he has these steps to re-create the picture:
However, based on these steps, his partner could also create the image shown in Figure 13-2. After each partner completes their steps and draws their partner’s drawing, they can work together to improve their list of steps — essentially debugging the error in their steps. For example, partners may decide that adding specifics such as “below,” “touching but not overlapping,” and, “with centers aligned along the y-axis” may improve their instructions.
One of the hardest parts about debugging is finding where the program is displaying the errors in your code, and then interpreting them. Each programming language and coding application has slightly different ways of displaying and describing error messages. This section gives some examples of error messages that you might see in different programming languages, where to find them in an example application, and how to interpret them.
A scoping error is one where you’ve created a variable and you try to access the variable, but the part of the code that you are in doesn’t know about it. Scopes can be described by looking at what is between curly braces, or within the “mouth” of a block-based language. For example, if you write a for loop in Java it would look like this:
for(int index = 0; index < 10; index++)
{
System.out.println(index);
}
The index
variable only lives within the scope of the for loop. So that means, when you print the index
variable within the curly braces of the for loop, the computer knows what variable you want to print, and can accurately print it. However, if the code looked like this:
for(int index = 0; index < 10; index++)
{
System.out.println("In scope:" + index);
}
System.out.println("Out of scope:" + index);
The line that tries to print the index that is outside of the curly braces would throw a scoping error. The error in Java would be:
java:lineNumber: error: cannot find symbol
System.out.println("Out of Scope: " + index);
^
symbol: variable index
location: class className
1 error
This error would appear when you try to compile your Java code.
Though it's pretty difficult to get a syntax error in block-based languages because the blocks are already created for you, it’s possible to get a scoping error. For example, in App Inventor, you could create a local variable called myName
and set a label to that variable. However, if you only had one variable that was local, and you tried to set a label to that variable outside of its own “mouth,” it would show a scoping error — there would be no variables available for that block. Figure 13-3 shows an example of this error in App Inventor.
Typing errors can happen to the best of us! Especially for young coders who don’t have a lot of experience typing on a keyboard, or who may not be the best spellers, typing errors can be fairly common. Common typing errors are misspelling variables and methods, but another type of typo can be mis-capitalization. For example, in a simple Python program that assigns a value to a variable, and then attempts to print the variable:
myName = 'Sarah'
print (myname)
The error is that the variable has camelCasing when it’s defined, but then has only lowercase when used in the print function. The error you would get if you ran the program would be:
Traceback (most recent call last):
File "example.py", line 2, in <module>
print myname
NameError: name 'myname' is not defined
Scratch, on the other hand, is a language that really tries to protect the user from typing errors. For example, Figure 13-4 shows a simple program; it’s impossible to make the same error as you can with Python (mis-capitalizing the variable name), because you don’t re-type the variable names each time with Scratch; you simply use the variable block that is created when you first created the variable. But a potential mis-capitalization is checking the value of the variable. Notice that the value was set to Sarah
, but the if-then
statement checks to see whether the value was sarah
. In a language such as Python or Java this error would make the if-then
condition evaluate to false, but in Scratch it evaluates to true, making the sprite say “Hello there!” when the green flag is clicked.
Another syntax error to watch out for is using incorrect data types. This can happen only in strongly typed programming languages, like Java. For example, say you had the following program:
int month = "11";
int day = "15";
System.out.println("My birthday: " + month + "/" + day);
The errors that you would get when compiling your program is:
java:lineNumber: error: incompatible types: String cannot be converted to int
int month = "11");
^
java:lineNumber: error: incompatible types: String cannot be converted to int
int day = "15");
^
2 errors
Usually the error messages regarding incorrect data types are straightforward; however, understanding what data types are the correct ones to use can be a tricky subject for young coders. For example, the error messages in the code example indicate that the coder attempted to assign a string type (11
and 15
are strings because they have quotation marks around them) to a variable that is an integer type, which is a number.
Unlike syntax errors, semantic errors are often more difficult to capture. This is because semantic errors are typically errors in the programming logic, rather than something that you typed incorrectly. This section has a couple of examples of semantic errors that you and your young coder might encounter in a few different programming languages.
Infinite loops are loops that never end! They go on infinitely. This can be a problem because it might seem like the code just isn't working, but really the program is just running forever and ever.
If you wrote a small Java program where you wanted to print the numbers 0 through 9, you might write something like this:
for(int index = 0; index < 10; index--)
{
System.out.println(index);
}
But there is an error in this code! Instead of updating the index to be index + 1, the code updates the index to be index – 1! So the code does the following:
index = 0
Is index < 10? Yes
Print index 0
index = index – 1 index = -1
Is index < 10? Yes
Print index -1
index = index – 1 index = -2
Is index < 10? Yes
Print index -2
index = index – 1 index = -3
Is index < 10? Yes
Print index -3
This continues forever, because it’s impossible for index to be greater than or equal to 10. So when you run the Java code, the program continues to print forever, until you kill the program!
Although infinite loops can be a problem, some programming languages deliberately have implemented infinite loops to make some pretty neat effects! For example, in Scratch there is a forever block. Figure 13-5 shows two programs that are very similar. The left figure shows a program that checks to see whether the mouse pointer is touching the sprite. If it is, then the sprite meows. The problem is that the if-then block is checked only once — at the moment when the green flag is pressed — and it’s never checked again. The right figure shows the same program, but with a forever loop block around the if-then block. Now, after the green flag is clicked, the if-then block keeps asking, forever, whether the mouse pointer is touching the sprite. This makes it a program that always makes the sprite meow when the mouse pointer touches it until the red stop sign is pressed (which is the same as killing the program).
Another very common error to run into is called an off by one error. This is very common when dealing with lists and iterating through lists. For more information about lists, check out Chapter 11.
Scratch, as usual, handles off by one errors for the user without really indicating there’s a problem. For example, Figure 13-6 shows a program that loops through a list of pets and has the sprite say Hi petName
, where petName
is replaced with the item from the list (either Luke, Winston, or Princess). The loop repeats four times, but there are only three items in the list. Instead of completely breaking, on its last iteration, Scratch prints Hi
with nothing after it (as shown in Figure 13-6).
Other programming languages are not as forgiving. For example, in Python you might have the following program to say hello to the three pets:
pets = ['Luke’, 'Winston', 'Princess']
for x in range(1, 3):
print (‘Hi ‘ + pets[x])
If you run this program, the output would be:
Hi Winston
Hi Princess
The reason ‘Hi Luke’ doesn’t print is because lists in Python start at 0, not at 1. The correct code would be:
pets = ['Luke', 'Winston', 'Princess']
for x in range(0, 3):
print ('Hi ' + pets[x])
range(0, 3)
Represents the elements 0, 1, and 2 because the range function includes the first number but excludes the second.
Another version of an off by one error in Python would be if you went beyond the length of the list. For example:
pets = ['Luke', 'Winston', 'Princess']
for x in range(0, 4):
print ('Hi ' + pets[x])
This causes even more of an issue, because instead of simply missing an element in the list, you’re trying to access an element that never existed in the first place. The output for running this code is:
Hi Luke
Hi Winston
Hi Princess
Traceback (most recent call last):
File "filename.py", line 4, in <module>
print ('Hi ') + pets[x]
IndexError: list index out of range
There is an actual error, because the data for pets[4]
doesn’t exist, so the computer cannot resolve it; therefore it doesn’t know what to do.
When you and your young coder are trying to debug, sometimes no error messages give you insight into the problem. This section has a list of strategies for debugging programs where you don’t get an error message, or the error message doesn’t give you enough information.
One of the best ways to debug is to disable sections of code so that you have small sections to test.
In Scratch, you might have a lot of scripts that start when the green flag is pressed. This can cause problems if some of the scripts cancel out or affect the other scripts. For example, Figure 13-7 shows three scripts associated with one sprite. The problem that the user notices is when the mouse pointer is touching the sprite, it does not meow. To try to figure out the problem, the coder might disconnect all the scripts and only have one script connected at a time, like in Figure 13-8. Then they see that the code for the mouse pointer touching the sprite works correctly, but that there is another problem (the code that stops all sounds forever). By connecting and disconnecting the blocks, the coder can identify the problem.
Other block-based languages like App Inventor make turning sections on and off even easier! For example, in Chapter 15 there is a Puppy Play Day mobile game that you can create. Figure 13-9 shows how if you right-click a code block, you can disable that block. In this example, you might want to make sure the initial property settings work before creating the lists and hiding the items. Then, after you confirm the properties are set properly, you can enable the call to setupLists
to make sure that works. Then you can enable the call to hideItems
to make sure that works.
Text-based languages have a similar way of turning sections on and off — you only have to comment out the lines of code. In Python, you can comment out a single line of code like this:
#print 'Hi'
And you can comment out multiple lines of code like this:
"'
for x in range(0, 4):
print ('Hi ' + pets[x])
"'
Commenting out code is how you “turn off” or “disable” parts of your code when you’re in a text-based language.
A common bug that coders run into is not testing data to make sure that the program works. This can especially be a problem if you’re writing programs that take user input. It’s important to make sure you and your young coder think about what kind of input you’re expecting, and test to make sure the input is handled correctly.
You might have a program that gets input from the user and prints what the user types, like this Python code:
name = raw_input('What is your name? ‘)
print (‘Hi ' + name)
It’s important to test to make sure that if you put the following types of input, they still do what you, as the coder, expect:
Sarah
Sarah Guthals
13
11/15
Sarah 55 Guthals
By mixing letters, spaces, numbers, and other symbols like / you’re ensuring that your program performs as expected. This type of testing is unit testing and ensures that small portions of your program execute correctly with varying input.
One of the most challenging aspects of coding is that the code is abstract and sometimes the data is hidden. This is especially tricky when you have complex data or are performing complex operations on data. By adding a number of output messages in your code, you can indicate when certain sections of code have been reached, or you can show the current values of certain variables at various points during execution.
An example of adding output messages to a program to gain insight in Python follows. Here, your goal is to write a program to solve an algebraic expression. For example:
x = input('Provide a number for x: ')
y = input('Provide a number for y: ')
first = 2*x
second = 6*y
sum = first - second
print '2x + 6y = '
print (sum)
There is an error in this program; instead of adding the first and second elements, the coder is accidentally subtracting. Though this error is fairly obvious because this example is small, it shows how a simple typo could completely change the output. If you run this code, you get results like:
Provide a number for x: 2
Provide a number for y: 3
2x + 6y =
-14
This is clearly wrong. 2*2 + 6*3 = 4 + 18 = 22, not -14. One way of debugging this code is to add output messages at each point. For example, you could change your code to:
x = input('Provide a number for x: ')
print ('x: ')
print (x)
y = input('Provide a number for y: ')
print ('y: ')
print (y)
first = 2*x
print ('first: ')
print (first)
second = 6*y
print ('second: ')
print (second)
sum = first - second
print "2x + 6y = "
print sum
Then, when you run the code you get the following output:
Provide a number for x: 2
x:
2
Provide a number for y: 3
y:
3
first:
4
second:
18
2x + 6y =
-14
Then the coder can see that x, y, first, and second are all correct. This must mean that it’s just when the sum is calculated that there is an error.
Although there might be other ways to debug for different languages and programming environments, one universal strategy is to walk away from your code. Sometimes, when you stare at the same code for hours, you just aren’t seeing the obvious problems. By walking away from your program and giving yourself a 20-30 minute break, you can gain perspective. One of Sarah’s professors liked to call this the Gilligan’s Island strategy because he would walk away and watch an episode of Gilligan’s Island before returning.
Many expert coders can tell you stories of how they worked on debugging a program for over 5 hours, they went home and went to bed, and when they woke up they knew exactly what the problem was and were able to fix it in a matter of minutes. Camille finds that walking her dog, Pepper, unclutters her own thinking and allows her to approach the problem with fresh eyes and a clear mind. This isn’t magic. It’s letting your brain relax and approach the problem in another way.
3.15.237.255