Welcome back! In our last lesson, we focused on the crucial habit of planning before you code. You learned how to define input/output examples and create a pseudocode blueprint to ensure you have a solid plan before writing a single line of Python. This planning greatly reduces the chance of errors, but it doesn't eliminate it. No programmer, from a first-day beginner to a senior engineer, writes perfect code on the first try.
This lesson addresses the inevitable next step: what to do when your code doesn't work as planned. We will explore a systematic approach for finding and fixing errors, known as debugging. This isn't a frantic, random process of changing code until it works; it's a methodical cycle of investigation and learning.
Specifically, by the end of this lesson, you will be able to apply an attempt–check–correct cycle to a programming exercise. Just as importantly, you'll learn how to record the cause of each mistake, turning errors from frustrating roadblocks into powerful learning opportunities.
The Attempt–Check–Correct Cycle
Programming is an iterative process. You write some code (Attempt), see if it works (Check), and fix it if it doesn't (Correct). This loop is the fundamental rhythm of software development. The key is to make this cycle deliberate and structured.
Let's start by watching a short video that introduces the concept of debugging and shows how to interpret the first clues the computer gives you when something goes wrong: the error message and the stack trace.
Debugging with stack traces | Intro to CS - Python | Khan Academy
This Khan Academy video, "Debugging with stack traces," provides an excellent overview of the debugging mindset. It walks through fixing several common types of errors that you will definitely encounter.
Please watch the first four minutes, from the beginning until the end of the runtime error discussion. Pay close attention to how the presenter: Identifies the type of error (SyntaxError, IndentationError, RuntimeError). Uses the file name and line number from the error message (the "stack trace") to locate the problem. Sometimes has to think about the intended purpose of the code when an error message is confusing.
The video demonstrates the core loop. Your plan from the last lesson guides your Attempt. Running the program and observing the output or error message is the Check. The process of using the error message to find and fix the bug is the Correct phase.
A more formal way to think about this process, especially the "Correct" phase, is outlined in the following article. It breaks down debugging into a scientific-like method.
Why You’re Not Learning From Your Mistakes When Coding – AlgoCademy Blog
The AlgoCademy blog provides a fantastic, structured approach to debugging that turns it into a clear, repeatable process. This will be our guiding framework.
Read the section titled A Systematic Debugging Process. Focus on understanding the five steps presented: Reproduce the Error: Make sure you can trigger the bug reliably. Read the Error Message Carefully: This is your primary clue. Form a Hypothesis: Make an educated guess about the cause. Test Your Hypothesis: Change the code to test your guess. Reflect and Generalize: Learn the underlying principle. This five-step process is the heart of the "attempt-check-correct" cycle.
The "Check" Phase: Reading the Signs
When your code doesn't work, the first step is to understand the feedback Python is giving you. This feedback, the error message and traceback, is not a sign of failure; it's a helpful report pointing you toward the problem.
Let's dive deeper into how to interpret these reports.
How to Debug Common Python Errors – Real Python
This article from Real Python gives a clear, practical breakdown of how to read the information Python provides when an error occurs.
Start at the beginning of the article and read down to the section "How to Debug Python Errors With print()". You will read through two main sections: The introduction gives an example of a NameError. Pay attention to the table that breaks down each part of the traceback. The next section details a TypeError. Notice how the caret (^) symbol helps pinpoint the exact operator that caused the issue.
As you've seen, Python errors generally fall into a few categories:
- Syntax Errors: The code isn't structured correctly according to Python's rules (e.g., a missing colon, mismatched parentheses, invalid indentation). The program won't even start to run.
- Runtime Errors: The syntax is valid, but an operation fails during execution (e.g., trying to add a number to a string (
TypeError), using a variable that doesn't exist (NameError), or accessing a list element that isn't there (IndexError)). - Logical Errors: The code runs without crashing but produces the wrong result. This is often the trickiest category, as Python doesn't give you a traceback to follow. You must identify the error yourself by noticing the output is incorrect.
The "Correct" Phase: Tools for Investigation
Once you have a hypothesis about what's wrong, you need to test it. This involves "looking inside" your program as it runs to see if the variables hold the values you expect. Here are two fundamental techniques for doing this.
Method 1: Print Debugging
The simplest and most universal debugging technique is to strategically place print() statements in your code. This allows you to inspect the value of variables at different points and trace the flow of execution.
The video below demonstrates this and other simple debugging techniques on practical examples.
Debugging in Python | Debugging Tips | Python Tutorials for Beginners #lec81
This video from Jenny's Lectures CS IT provides a series of short, practical debugging scenarios. We'll focus on a few specific examples.
Watch the following segments: Using print: Watch the segment from this timestamp. Notice how adding print() statements helped isolate the line where a variable was not being updated correctly due to using == instead of =. Logical Errors: Watch these three short examples of logical errors, where the code runs but does the wrong thing. range() function error: here. IndexError from random.randint: here. if condition logic error: here.
Method 2: The Visual Debugger (in VS Code)
While print() is powerful, it can become cumbersome. A more advanced and efficient method is using a visual debugger. Since you use VS Code, learning its built-in debugger is one of the most significant productivity boosts you can get. A debugger allows you to pause your program at any point (a "breakpoint"), inspect all variable values, and execute your code line by line.
Let's walk through how to do this.
How to Debug Common Python Errors – Real Python
This section of the Real Python article provides a step-by-step guide to using the visual debugger in VS Code, complete with screenshots. This is a critical skill for any developer.
Read the section titled Debugging With Breakpoints. Follow the steps described: How to set a breakpoint (the red dot). How to start the debugger. How to use the controls to step through your code. How to inspect variables in the side panel. This directly replaces peppering your code with print() statements and gives you a much richer view into your program's state.
Closing the Loop: Learning from Your Mistakes
Fixing a bug is good. Learning from it so you never make that same type of mistake again is even better. This is the "Reflect" step from the AlgoCademy process, and it's the core of the final part of our learning outcome: "record the cause of each mistake."
The best way to do this is to keep a simple "coding journal" or "error log." When you solve a bug, especially one that took you some time, you should briefly document it.
Why You’re Not Learning From Your Mistakes When Coding – AlgoCademy Blog
This final reading from AlgoCademy explains the "why" and "how" of documenting your errors to accelerate your learning.
Read the sections Personal Error Library and Maintain a Coding Journal. The key idea is to create a personal, searchable knowledge base of problems you have already solved.
Your journal entry for a bug can be simple. Here's a template you can use, either in a text file, a notebook, or a notes app:
My Bug Report
- Problem: What was I trying to do?
- Example: Calculate the total cost of items in a shopping cart.
- Symptom: What was the error message or incorrect output?
- Example:
TypeError: can only concatenate str (not "int") to str. The program crashed.
- Example:
- Root Cause: What was the actual mistake in the code?
- Example: I read a price from
input(), which returns a string. I then tried to add it to my numericaltotalwithout first converting the price string to an integer usingint().
- Example: I read a price from
- Lesson: What general principle did I learn?
- Example: Always be aware of data types.
input()always returns a string. Useint()orfloat()to convert user input before doing math with it.
- Example: Always be aware of data types.
Your Turn: Apply the Cycle
Now, let's practice this entire cycle. Below is a small Python program that is supposed to calculate the average score from a list of test results. It's filled with a few common bugs.
Your task is to get it working correctly. But more importantly, for each bug you find and fix, you must fill out a "Bug Report" using the template above.
Here is the buggy code. Copy it into a file in VS Code.
# Function to calculate the average of a list of scores
def calculate_average(scores)
total_score = 0
for score in scores
total_score = total_score + score
average = total_score / num_scores
print("The average score is:", average)
# Test scores
student_scores = ["85", "92", "78", "95", "88"]
num_scores = 5
# Calculate and print the average
calculate_average(student_scores)
Instructions:
- Run the code. It will crash. This is your first Attempt and Check.
- Correct the first bug using the traceback. Fill out a Bug Report for it.
- Run the code again. It will crash with a different error.
- Correct this second bug. You may need to use the debugger or
printstatements to understand it. Fill out a Bug Report for it. - Run the code again. It might run now, but the output could still be wrong (a logical error).
- Correct this final bug. Fill out a Bug Report.
- Your goal is to have working code and three completed Bug Reports.
Take your time and follow the process. Don't just fix the code; understand why it was broken.
Conclusion
In this lesson, we established a robust framework for dealing with the reality of programming: bugs are inevitable. By approaching them systematically, you can transform them from sources of frustration into valuable lessons.
Here are the key takeaways:
- The Attempt-Check-Correct cycle is the core loop of programming.
- Errors come in three main flavors: Syntax, Runtime, and Logic.
- Tracebacks are your guide. Learn to read the file, line number, error type, and message to locate the problem.
- Use
print()statements or the VS Code debugger to inspect your program's state and test your hypotheses about a bug's cause. - The most crucial step is reflection. Keep a coding journal to record the root cause of your errors and the lessons you learn from them.
In our next module, we will begin exploring more complex program logic, starting with how to make decisions in your code using if, elif, and else statements. The debugging skills you've started building today will be absolutely essential as your programs grow in complexity.