Debugging Basics

Find and fix what's going wrong

Beginner 10 min

In this lesson

Debugging is the process of finding and fixing the bugs (errors) in your code. Every programmer writes bugs — the skill isn’t avoiding them, it’s fixing them efficiently — and debugging is a process you can learn that turns frustrating stuck moments into routine puzzles.

Explain it like I’m 5

Debugging is detective work. The program is doing something you didn’t expect, and your job is to gather clues until you find exactly where reality and your expectation part ways.

Read the traceback — it's pointing at the problem

When Python hits an error, it prints a traceback. Read it from the bottom up: the last line names the error type and message, and just above it is the file and line number where it happened. The traceback is not noise — it’s Python telling you precisely where and why it stopped.

Example
def average(numbers):
    return sum(numbers) / len(numbers)

scores = []
print(average(scores))
Output
ZeroDivisionError: division by zero
A traceback names the error and the line.

Print what you think is true

The humblest, most effective tool is print(). When a value isn’t what you expect, print it: print("x is", x). Bugs hide in the gap between what you assume a variable holds and what it actually holds. Printing closes that gap and turns guesses into facts.

Example
price = "10"
quantity = 3
# print(price * quantity) would give "101010" — a clue!
print("price is", price, "type:", type(price))
total = int(price) * quantity
print("total:", total)
Output
price is 10 type: <class 'str'>
total: 30
Printing a value's type reveals a hidden assumption.

This should print the average but it crashes. Read the error in your head, then fix it so it prints 20.0.

def average(numbers):
    return sum(numbers) / len(numbers)

result = average([10, 20, 30])
print("Average:" + result)

Isolate the problem

Narrow down where things go wrong. Comment out sections, or add prints at a few points, to find the last spot where everything was still correct. Once you know the failing change is between line A and line B, the bug has nowhere left to hide. Fix one thing at a time and re-run.

Check your assumptions and explain it aloud

Most bugs come from a wrong assumption: a variable is a string when you thought it was a number, a list is empty, a condition is backwards. ‘Rubber-duck debugging’ — explaining the code line by line to a rubber duck (or anyone) — works because saying it out loud forces you to check each step instead of skimming past the wrong one.

Two more tools: assert and breakpoint()

Beyond print(), two built-ins help you catch and chase down bugs. assert condition states something you believe is true; if it isn’t, Python stops immediately with an AssertionError, catching a broken assumption right where it happens instead of letting it cause weird behaviour later.

breakpoint() is more powerful: placed on a line, it drops you into Python’s interactive debugger when execution reaches it, so you can inspect variables and step through the code. Start with prints; reach for these as your programs grow.

Example
def average(numbers):
    assert len(numbers) > 0, "numbers must not be empty"
    return sum(numbers) / len(numbers)

print(average([10, 20, 30]))
print(average([]))
Output
20.0
AssertionError: numbers must not be empty
assert turns a silent assumption into a clear, early error.

Common mistake: Panicking at a wall of traceback text

Why it happens:

Tracebacks look intimidating, so beginners skip them — but they contain the exact answer to where and why the code failed.

How to fix it:

Read the last line first (the error type and message), then the line number just above it. That’s usually all you need to locate the bug.

Common mistake: Changing code randomly hoping it works

Why it happens:

Without understanding the cause, it’s tempting to shuffle things until the error disappears — which often hides the bug instead of fixing it.

How to fix it:

Form a hypothesis, test it with a print(), then make one deliberate change. Understand why the fix works.

Where is the actual error message in a traceback?

What's a quick way to check what a variable actually holds?

Mini exercise (medium)

This code should greet each name but raises a TypeError: names = "Ada" then for n in names: print("Hi", n). Diagnose what names really is and fix it so it greets Ada, Bo, and Cy.

Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.

names = "Ada"   # BUG: looping a string visits its letters
greetings = []
for n in names:
    greetings.append("Hi " + n)
print(greetings)

What to learn next

You learned a calm process for fixing bugs: reading tracebacks, using print() to inspect values, questioning your assumptions, rubber-duck debugging, and narrowing down where things break. You practised by spotting that looping over a string visits its letters, then fixing it to loop over a list.

It also helps to recognise the specific errors you’ll meet again and again. Common Python Errors walks through SyntaxError, NameError, TypeError, ValueError, IndexError, KeyError, and IndentationError, with the quick fix for each. When you’re ready, continue to the next lesson.