Debugging Python with pdb is one of the fastest ways to stop guessing and start understanding what your code is actually doing. Many beginners rely only on print() statements when something breaks. That can help for small scripts, but it quickly becomes messy when the bug depends on several variables, nested function calls, loops, or data that changes over time. The Python debugger gives you a more professional workflow: pause the program, inspect values, move line by line, and test assumptions while the script is still running.
This English version is adapted for developers who want a practical debugging guide, not a literal translation. You will learn what pdb is, how to start it with breakpoint(), which commands matter most, how to inspect variables, how to debug loops, how to run a script directly under the debugger, and when an IDE debugger may be more convenient. If you are still building your foundation, start with this Python beginner guide and this article on common Python errors.
What Is pdb?
pdb stands for Python Debugger. It is a built-in module from the Python standard library, so you do not need to install anything before using it. It works in the terminal, on local machines, inside many remote environments, and even in situations where a graphical debugger is unavailable. That makes it especially useful for scripts, automation, servers, containers, and small projects where opening a full IDE is unnecessary.
The official Python pdb documentation describes it as an interactive source code debugger. In practical terms, it lets you pause execution, look around, move forward one line at a time, inspect the call stack, print variables, evaluate expressions, and continue running the program. Instead of only seeing the final error message, you can investigate the program while it is alive.
Why Debugging Beats Guessing
When a script fails, the natural reaction is to guess. You may think a variable is empty, a condition is wrong, a loop is skipping an item, or a function receives the wrong argument. Those guesses may be correct, but they are still guesses. Debugging turns those guesses into evidence. You pause the code at the suspicious line and check the real values directly.
This is especially important in Python because the language is dynamic. A variable may contain a string in one run and None in another. A dictionary may be missing a key. A function may return a value you did not expect. A list may be empty because the filter logic removed everything. If values and types are still a weak point, review Python data types, None in Python, and Python dictionaries.
Start Debugging with breakpoint()
The easiest modern way to start pdb is to place breakpoint() in your code. When Python reaches that line, execution pauses and the terminal opens an interactive prompt that starts with (Pdb). From there, you can run debugger commands.
def calculate_bonus(salary):
rate = 0.15
breakpoint()
bonus = salary * rate
return bonus
print(calculate_bonus(3000))When the program stops, you can type variable names, evaluate expressions, and move through the function. The older style import pdb; pdb.set_trace() still works, but breakpoint() is cleaner and easier to remember. It was introduced to provide a standard debugging hook that can be customized through environment variables if needed.
Essential pdb Commands
Once the debugger opens, you need only a small set of commands to be productive. You do not have to memorize the entire debugger. Learn the basic navigation commands first, then add advanced commands as your debugging problems become more complex.
- l or list: show nearby lines of source code so you know where you are.
- n or next: execute the current line and move to the next line in the same function.
- s or step: step into a function call so you can inspect what happens inside it.
- c or continue: continue normal execution until the next breakpoint or the end of the program.
- p: print a variable or expression, such as
p salaryorp type(salary). - w or where: show the call stack so you can see how the program reached the current line.
- q or quit: stop debugging and exit the program.
The difference between next and step matters. Use next when you trust the function being called and only want to move past the current line. Use step when you suspect the bug is inside the called function. This simple distinction prevents you from getting lost in code you do not need to inspect.
Inspect Variables and Expressions
The most common debugging task is inspecting values. At the (Pdb) prompt, you can type p variable_name to print a variable. You can also evaluate expressions, call type(), check lengths, inspect dictionary keys, and test conditions. This lets you verify your assumptions without editing the script repeatedly.
(Pdb) p salary
3000
(Pdb) p rate
0.15
(Pdb) p salary * rate
450.0
(Pdb) p type(salary)
<class 'int'>This is much more flexible than adding several print() calls, running the script again, changing the prints, and running it again. You can ask new questions while the program is paused. If you are still using print statements for every debugging situation, this guide to print in Python is useful, but pdb is the next step when prints become noisy.
Debugging Function Calls
Bugs often hide inside function calls. A function may receive the wrong argument, return the wrong value, or modify data in a way you did not expect. With pdb, you can pause before a function call, use step to enter it, then inspect the local variables inside that function.
def normalize_name(name):
return name.strip().title()
def build_profile(raw_name):
breakpoint()
clean_name = normalize_name(raw_name)
return {"name": clean_name}
print(build_profile(" ana maria "))When execution stops before normalize_name(), use s to step into the function or n to run it and stay in build_profile(). This helps you decide whether the bug is in the caller or the called function. If you want a refresher on how functions return values, read this guide to the Python return statement.
Debugging Loops Without Losing Your Mind
Loops are a common source of bugs because the same code runs many times with different values. A bug may appear only on the tenth item, the fiftieth row, or one malformed dictionary. Placing a breakpoint at the top of the loop and pressing next dozens of times is painful. A better approach is to create a conditional breakpoint in code.
users = [
{"name": "Ana", "email": "[email protected]"},
{"name": "Broken", "email": None},
]
for index, user in enumerate(users):
if user["email"] is None:
breakpoint()
print(user["email"].lower())The debugger opens only when the suspicious condition occurs. This saves time and keeps your attention on the data that matters. If loops are still confusing, read these guides to for loops in Python and loops in Python.
Run a Script Directly with pdb
You do not always need to edit the source file to add breakpoint(). You can run a script directly under pdb from the command line. This is useful when you want to debug from the beginning or when you are investigating a script temporarily.
python -m pdb app.pyThe debugger starts before the first line of the script runs. You can use n to move forward, set breakpoints, continue execution, and inspect state. If your script accepts arguments, include them after the filename. This is helpful for command-line tools, automation scripts, and data processing jobs.
For terminal-heavy workflows, this guide on how to run Python commands in the terminal can make debugging more comfortable. The better you understand how scripts run from the command line, the more useful pdb becomes.
Set Breakpoints from Inside pdb
You can also set breakpoints while already inside the debugger. Use the b command followed by a line number or function name. This is useful when you know where you want the program to stop later, but you do not want to edit the file.
(Pdb) b 42
Breakpoint 1 at app.py:42
(Pdb) cAfter setting a breakpoint, use continue. Python runs normally until it reaches that line. This is a useful workflow when the beginning of the script is uninteresting and the bug happens later. You can list breakpoints with the b command and clear them when they are no longer needed.
Inspect the Call Stack
When a bug happens deep inside a program, you need to know how execution got there. The call stack shows the chain of function calls that led to the current line. In pdb, use w or where to display it. Then use u to move up the stack and d to move down.
(Pdb) where
(Pdb) up
(Pdb) downThis is valuable when the current function looks correct, but the caller passed bad data. You can move up to inspect the caller’s variables and understand where the wrong value entered the flow. Debugging is often less about the line that crashed and more about the earlier line that created the bad state.
pdb vs IDE Debuggers
Visual debuggers in VS Code, PyCharm, and other IDEs are excellent. They show variables in panels, let you click to set breakpoints, and make stepping through code comfortable. The VS Code Python debugging guide explains that workflow in detail. However, pdb remains worth learning because it works almost anywhere.
If you are debugging a script on a remote Linux server, inside a Docker container, through SSH, or in a minimal environment without a graphical interface, pdb may be the simplest option. It also teaches you what debuggers are doing under the interface. Once you understand pdb, visual debuggers become easier to use because the concepts are the same: breakpoints, stepping, stack frames, variables, and execution flow.
Common pdb Mistakes
The first mistake is leaving breakpoint() in production code. Before committing or deploying, search your codebase for debugging statements and remove them. A forgotten breakpoint can pause a running service, break automation, or confuse another developer. The second mistake is stepping through too much code. Debugging is not about watching every line. It is about testing specific hypotheses.
The third mistake is debugging without reproducing the bug reliably. If you cannot reproduce the failure, it is hard to know whether your fix worked. First create a minimal case that fails consistently. Then place the breakpoint near the suspicious logic. The fourth mistake is ignoring tests after debugging. Once you understand the bug, write a test that would have caught it. This guide to unit testing in Python is a good next step.
A Professional Debugging Workflow
A reliable workflow looks like this: reproduce the bug, identify the smallest suspicious area, add a breakpoint before that area, inspect inputs, step through the logic, compare actual values with expected values, confirm the root cause, apply a focused fix, remove the breakpoint, and add a test. This is much better than randomly changing code until the error disappears.
Debugging is a scientific process. You create a hypothesis, collect evidence, and revise the hypothesis if the evidence disagrees. A good debugger helps you inspect reality. It does not replace thinking, but it makes thinking more precise. This is the mindset that separates random trial-and-error from professional troubleshooting.
Final Checklist
Use breakpoint() to pause execution. Use n to move to the next line and s to step into functions. Use p to inspect variables and expressions. Use w to inspect the call stack. Use conditional breakpoints in loops. Run scripts with python -m pdb script.py when you want to debug from the beginning. Remove debugging statements before committing code.
Learning pdb will change how you solve Python problems. Instead of flooding your code with prints or guessing where the bug lives, you can pause the program and inspect the exact state that created the failure. That skill makes you faster, more confident, and much more independent as a Python developer.

