top of page

Learn code debugging - A guide for K-12 students (PART I)

Updated: Apr 10, 2022


How to effectively debug code when code breaks or crashes, how to fix mistakes in code

Debugging is the process of fixing mistakes in your code. Debugging is an important part of coding, and is essential for any real-world coding project. However, it is rarely covered in classes for kids. There are several reasons for this

  • Classes usually cover concepts and try to teach you as many new programming concepts or techniques as possible.

  • Bugs come in many different forms, and it is hard to cover all possible types of bugs in a class context.


However, debugging is still critical. Why for kids? Even though real-world programming jobs require strong debugging skills, why do kids need to have them? Here is why:

  • If you decide to build a project of any kind (a game, a website, an AI application, etc.), what will matter most is that your project works. Bugs can make your project fail, slow down your progress, etc.

  • Even if the exercises you get for homework in your coding classes are simple enough that bugs are easy to detect, the real-world projects that you want to do will likely have more complex (and really annoying!) bugs!


So, how can we get better at debugging? First, the more code you write, the better your debugging skills will be. But even when you are just getting started, there are some practical steps you can follow to reduce the amount of time you spend debugging (and the annoyance of it :-)). I have learned these the hard way through years of practice, and they are best practices that all software engineers know. I call them the Methodical Debugging process.


Kinds of bugs


No matter what your code does, or what language you wrote it in, there are several different ways your code can break

  • Deterministic Crash. This is the best kind of bug. The program crashes (all the time). This is the most basic type of bug. In Computer Science we call this a deterministic failure. It is deterministic because it happens every time.

  • Determinist but No Crash. The runs to completion but does not do what you expect (all the time). This is also deterministic, but more subtle in that we know it doesn't work but it does not crash either so we don't know exactly where it ran into trouble.

  • Transient/Non-Deterministic Crash. The program runs to completion and does what you expect most of the time, but every once in a while it crashes. This is called transient. It is nastier because you don't know when it will happen and what causes it to happen sometimes and not at other times.

  • Transient/Non-Deterministic and No Crash. Then the worst kind - the program runs to completion all the time but every once in a while it does not do what you expect! This is the hardest to debug, and sometimes even to detect- since you may not even realize it is happening till something else breaks. These failures can frequently be silent and take a very long time to even detect, much less to debug.



Each of these kinds of bugs has best practices on how to detect them (find out something is wrong) and how to debug them (find out what is wrong and fix it). In this PART I blog, we will focus on the first category - Deterministic Crash.


This type of bug will occur in even simple exercises and in virtually any project you do. The steps that I usually use to debug these problems are:


Follow the error message


When your code crashes, it will print out what line it crashed on and a short message describing what the complaint is. Over time you will get better at reading these messages but initially just take it at face value. For example - see the python code below and the error message:



def alphabet_soup(inputstr):
 letters = list(inputstr)
 letters.sort()
 outputstr = ''.join(lettrs)
 return outputstr
 
print(alphabet_soup("egdfacb"))


Error:



Traceback (most recent call last):
  File "main.py", line 8, in <module>
    print(alphabet_soup("egdfacb"))
  File "main.py", line 4, in alphabet_soup
    outputstr = ''.join(lettrs)
NameError: name 'lettrs' is not defined

You can get a lot of good details from this. First, we know that line 8 is where the program crashed. That line is


outputstr = ''.join(lettrs)

We also know that the error is

 NameError: name 'lettrs' is not defined

Look carefully at line 8. Is it what you wanted it to be? Focus specifically on lettrs. Is that correct? You will notice that you meant to add letters but accidentally typed lettrs. This is the bug.


Once you fix the code, it will look like the below and will work


def alphabet_soup(inputstr):
 letters = list(inputstr)
 letters.sort()
 outputstr = ''.join(letters)
 return outputstr

print(alphabet_soup("egdfacb"))

Make only one change at a time


It may be tempting to try many different things to save time, but it will almost always just cause you to take more time. Try one change, run the code and see what happens. This is not good advice if your code is huge and takes hours to run, but it is the right thing to do for small bits of code. One step at a time.


Think about what you EXPECT to happen and check against that


It is very tempting to just try many things - but that almost never works. For each change you try, think about what you thought was wrong and what you expect will happen if you fix it. This way you can check the actual behavior against your assumptions.


Hope this helps! This is the first blog in a series about Methodical Debugging. In the next entry, we will talk about how to inspect your code and see what is happening as it runs.


Happy coding!!


28 views0 comments
bottom of page