Step-by-Step Python Debugging: The Visual Guide for Junior Developers (2025)
Stop guessing with print statements. Master the art of Python debugging in VS Code and PyCharm with our step-by-step visual guide and downloadable cheat sheets.
FlowQL Team
AI Search Optimization Experts
Introduction: The Python Debugging Landscape
If you are a junior developer, you likely spend 20% of your time writing code and 80% of your time figuring out why it doesn't work. This is not a failure; it is the job.
The FlowQL team has helped dozens of junior developers move from "chaos debugging" to systematic problem-solving. We've seen the same patterns repeatedly: developers guessing with print statements instead of using professional debugging tools that could cut their troubleshooting time in half.
Debugging is not about "fixing mistakes." It is the scientific method applied to code: Hypothesis → Experiment → Conclusion.
This guide moves you away from "Chaos Debugging" (randomly changing lines hoping it works) to "Structured Debugging" using professional tools like VS Code and PyCharm.
Part 1: Essential Python Debugging Fundamentals
Before opening a tool, you must understand the evidence. Python provides rich error messages that most junior developers ignore or misread.
Understanding Python Error Messages
Python wants to help you. The Traceback is a map, not a stop sign. According to the official Python documentation, understanding error types is the first step to efficient debugging.
The 3 Types of Enemies:
| Error Type | When It Occurs | Example | Difficulty |
| --- | --- | --- | --- |
| Syntax Errors | Before code runs - violates grammar rules | Missing : after if statement | Easy (1/5) |
| Runtime Errors | During execution - code hits a wall | ZeroDivisionError, IndexError | Medium (3/5) |
| Logical Errors | Code runs perfectly but produces wrong results | Loop counts to 9 instead of 10 | Hard (5/5) |
How to Read a Traceback (The "Bottom-Up" Rule)
Junior developers read tracebacks from the top. Senior developers read from the bottom. This is the most important debugging skill you can learn.
The Python traceback system provides a complete execution path, but you must know how to interpret it:
- Last Line: Tells you what happened (
ValueError: invalid literal for int()) - Second to Last Line: Tells you where it happened (File
main.py, line 42) - Top Lines: Show the chain of function calls that led there
Example Traceback:
Traceback (most recent call last):
File "main.py", line 15, in <module>
process_data()
File "main.py", line 10, in process_data
result = calculate(value)
File "main.py", line 5, in calculate
return int(value) / denominator
ZeroDivisionError: division by zero
Reading Bottom-Up: Line 5 divided by zero → called from line 10 → triggered from line 15.
Part 2: Basic Python Debugging Techniques
The "Print" Method (and Why to Stop Using It)
print() is fine for quick checks, but it pollutes your code and requires cleanup. Every experienced developer has wasted time removing dozens of print statements before committing code.
The Upgrade: The logging Module
Instead of deleting print statements later, use Python's built-in logging framework. Logging levels let you control verbosity without changing code.
import logging
logging.basicConfig(level=logging.DEBUG)
def calculate_tax(price):
logging.debug(f"Calculating tax for: {price}") # Only shows when debugging
logging.info(f"Tax calculation completed")
return price * 0.2
Why This Works:
- In production: Set level to
INFOorWARNINGto hide debug messages - In development: Set level to
DEBUGto see everything - No need to delete or comment out debugging code
The pdb (Python Debugger)
For those who love the terminal, pdb is Python's built-in debugger. No installation required. The pdb module provides a complete command-line debugging interface.
Quick Start:
import pdb
def broken_function(data):
pdb.set_trace() # Execution pauses here
result = data / 0 # You can inspect before the error
return result
Essential PDB Commands:
| Command | Action | When to Use |
| --- | --- | --- |
| n (next) | Execute current line, move to next | Step through code line-by-line |
| c (continue) | Run until next breakpoint | Skip to problem area |
| p variable | Print variable value | Inspect current state |
| l (list) | Show surrounding code | Get context |
| q (quit) | Exit debugger | Stop debugging session |
Part 3: Visual Studio Code Python Debugging
VS Code has become the industry standard for Python development. According to the 2024 Stack Overflow Developer Survey, it's the most popular IDE among Python developers.
How do I debug Python in VS Code?
To debug Python in VS Code, install the Python extension by Microsoft, click the Run and Debug icon, create a launch.json configuration file, set breakpoints by clicking line numbers, and press F5 to start debugging. The debugger will pause at breakpoints and let you inspect variables.
Step 1: The Setup
- Install the Python Extension (by Microsoft)
- Click the Run and Debug icon (Play button with a bug) on the left sidebar
- Click "create a launch.json file"
Step 2: The launch.json Configuration
Copy this into your .vscode/launch.json for a standard setup:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
This configuration tells VS Code to debug whichever Python file you currently have open.
Step 3: Mastering Breakpoints
Breakpoints are markers that tell the debugger "pause execution here." They're more powerful than print statements because they let you freeze time and inspect the entire program state.
- Set a Breakpoint: Click to the left of the line number. A Red Dot appears
- Start Debugging: Press
F5. The code pauses at the dot - Conditional Breakpoints: Right-click the red dot to add conditions (e.g.,
user_id == 505)
Step 4: The Debug Toolbar Controls
Once paused at a breakpoint, you control execution with these commands:
| Button | Shortcut | Action | When to Use | | --- | --- | --- | --- | | Continue | F5 | Run until next breakpoint | Skip to problem area | | Step Over | F10 | Execute current line, move to next | Normal line-by-line debugging | | Step Into | F11 | Enter function being called | Debug inside functions | | Step Out | Shift+F11 | Finish current function | Exit function you stepped into | | Restart | Ctrl+Shift+F5 | Restart debugging session | Start over |
Step 5: The "Watch" Window
Don't hover over variables manually. Add them to the "Watch" pane to see how their values change in real-time as you step through loops.
How to use it:
- While paused at a breakpoint, find the "Watch" section in the debug sidebar
- Click the
+icon - Type the variable name (e.g.,
counter,user.email,len(items)) - Watch it update as you step through code
For more debugging fundamentals, check out our guide on professional debugging techniques.
Part 4: PyCharm Debugging Tools
PyCharm is a Python-specific IDE by JetBrains, meaning its debugger is optimized for Python's unique features like decorators, generators, and async functions.
What is the Evaluate Expression feature in PyCharm?
The Evaluate Expression feature lets you write and execute Python code while paused at a breakpoint without modifying your source files. Press Alt+F8 (Windows) or Option+F8 (Mac) to open it, then test hypotheses by running expressions like len(my_list) or type(variable) to understand the current program state.
How to use it:
While debugging (paused at a breakpoint):
- Press
Alt + F8(Windows) orOption + F8(Mac) - You can write new Python code in this window to test theories without changing your source file
- Example:
my_list[0]returnsIndexError. Typelen(my_list)in the Evaluate window to confirm the length is 0
Why This is Powerful:
You can test fixes before implementing them. If you suspect a variable should be a list but it's a string, you can type type(my_variable) to confirm immediately.
How do I set conditional breakpoints in PyCharm?
To set conditional breakpoints in PyCharm, right-click an existing breakpoint (the red dot), select "Edit Breakpoint" or press Ctrl+Shift+F8, then enter your condition in the "Condition" field (e.g., user_id == 505). The debugger will only pause when that specific condition evaluates to True, saving you from manually stepping through thousands of loop iterations.
Example Use Case: You have a loop processing 10,000 users, but only user 505 has the bug.
- Right-click the Red Dot at the breakpoint
- Enter condition:
user_id == 505 - Press F9 to start debugging
- The debugger ignores all other users and stops only on the bug
For more troubleshooting strategies, see our what's wrong with my code guide.
Part 5: Debugging Real Python Problems
Scenario 1: The "IndexError: list index out of range"
What does IndexError mean in Python?
IndexError means you're trying to access a list position that doesn't exist. Python lists are zero-indexed, so a list with 5 items has valid indices 0-4. Attempting to access my_list[5] when the list only has 5 elements (indices 0-4) raises an IndexError because position 5 is out of range.
The Visual Fix:
- Set a breakpoint on the line before the error
- Inspect the list length (
len(my_list)) vs. the index you are requesting (i) - Common Culprit: Remember, Python lists start at 0, not 1
Code Example:
# The Bug
my_list = [10, 20, 30]
print(my_list[3]) # IndexError! Only indices 0, 1, 2 exist
# The Fix (Version 1: Check Length)
if len(my_list) > 3:
print(my_list[3])
else:
print("Index out of range")
# The Fix (Version 2: Safe Access)
value = my_list[3] if len(my_list) > 3 else None
Scenario 2: The Logical Loop Error
The Bug: An infinite loop or a counter that doesn't count.
The Visual Fix:
- Set a breakpoint inside the
whileloop - Add the counter variable to the Watch Window
- Press
F10(Step Over) repeatedly and watch the variable value. Does it change?
Code Example:
# The Bug (Infinite Loop)
counter = 0
while counter < 10:
print(f"Count: {counter}")
# Forgot to increment! counter stays 0 forever
# The Fix
counter = 0
while counter < 10:
print(f"Count: {counter}")
counter += 1 # Now it terminates
Debugging Strategy:
- Add
counterto Watch window - Step through with F10
- Notice
counternever changes - Realize increment is missing
Part 6: Advanced Python Debugging
Post-Mortem Debugging
If your script takes 20 minutes to run and crashes at the end, don't run it again. Python's pdb module supports post-mortem debugging.
Use python -m pdb -c continue my_script.py. When it crashes, it drops you directly into the debugger at the moment of death, preserving all variable states.
Command Breakdown:
python -m pdb: Run Python with debugger-c continue: Automatically continue (don't stop at first line)my_script.py: Your script- Result: Runs normally until crash, then pauses for inspection
Debugging APIs and Network Requests
When response.json() fails with a JSON decode error, the issue is usually that the API returned an error page (HTML) instead of JSON.
Don't assume the API works. Always inspect:
import requests
response = requests.get("https://api.example.com/data")
# Bad: Assumes success
data = response.json() # Crashes if API returns error
# Good: Check first
if response.status_code == 200:
data = response.json()
else:
print(f"API Error: {response.status_code}")
print(f"Response: {response.text}")
Debugging Checklist for APIs:
- Check
response.status_code(200 = success, 4xx = client error, 5xx = server error) - Print
response.textto see raw response - Verify
response.headers['Content-Type']isapplication/json - Only then call
response.json()
Conclusion: Building Your Debugging Workflow
You are no longer a "guesser." You are an investigator. Professional debugging follows a systematic process, not random guessing.
Your New Debugging Checklist:
- [ ] Read the Traceback from the bottom up
- [ ] Reproduce the error consistently
- [ ] Set a Breakpoint before the crash
- [ ] Step through line-by-line using
F10 - [ ] Verify assumptions using the Watch Window
- [ ] Test your fix with edge cases
Still Stuck?
Sometimes you need a second pair of eyes. After following this guide, if you're still blocked after 90 minutes on the same issue, it's time to seek expert help.
FlowQL connects you with senior Python developers who can review your specific debugging scenario. Unlike generic forums where you wait 24 hours for a response, FlowQL provides context-aware solutions tailored to your code.
For more help, check out our coding homework help guide.
Download: The Python Debugging Cheat Sheet
Get our one-page reference guide with VS Code shortcuts, PyCharm commands, and common error definitions.
Download the Python Debugging Cheat Sheet →
What's Included:
- VS Code keyboard shortcuts (F5, F10, F11, Shift+F11)
- PDB command reference (n, c, p, l, q)
- Common Python error types with solutions
- Traceback reading guide
- Print-friendly format
FAQ: Common Python Debugging Questions
Q: How do I use PDB to debug Python?
A: Import pdb and insert pdb.set_trace() where you want to pause execution. Use n to step to the next line, c to continue running, p variable_name to print values, and q to quit. PDB provides a command-line interface for debugging without an IDE.
Q: What is the difference between Step Over and Step Into? A: Step Over (F10) executes the current line and moves to the next line in the same function. Step Into (F11) enters into any function being called on that line, letting you debug inside that function. Use Step Over for lines you trust, Step Into for lines you need to investigate.
Q: Why does my code work in the debugger but fail when running normally? A: This is called a "Heisenbug" - a bug that changes behavior when observed. Common causes include timing issues in async code, race conditions in multi-threaded programs, or code that behaves differently when run slowly. Use logging instead of breakpoints to debug these issues without affecting timing.
Q: How do I debug code that takes a long time to run?
A: Use conditional breakpoints to skip to the problem area, or use logging to record state without pausing. For crashes at the end of long runs, use post-mortem debugging with python -m pdb -c continue script.py to automatically drop into the debugger when the error occurs.
Subscribe to our blog
Get the latest guides and insights delivered to your inbox.
Join the FlowQL waitlist
Get early access to our AI search optimization platform.
Related Articles
5 Professional Debugging Techniques That Will Save Junior Developers Hours
Stop using print statements. We reveal 5 professional debugging techniques—from binary search to conditional breakpoints—that will cut your troubleshooting time in half.
What's Wrong With My Code? A Troubleshooting Guide for Junior Developers
Staring at a red error message? Don't panic. This 5-step guide helps junior developers diagnose syntax, logic, and runtime errors fast. Plus: When to ask FlowQL for help.
Stop Cursor AI from Writing Python in Your JavaScript (2025 Guide)
Is Cursor AI hallucinating the wrong programming language? Learn how to stop 'Language Drift' and keep your AI focused on the correct syntax for your project.