Debugging Without Print Statements: Because Your Codebase Isn't a Diary
β€’

Debugging Without Print Statements: Because Your Codebase Isn't a Diary

πŸ“‹ Quick Steps

Stop littering your code with temporary print statements and start debugging like a professional.

// Set a conditional breakpoint in VS Code
1. Click gutter next to line number
2. Right-click red breakpoint dot
3. Select "Edit Breakpoint"
4. Enter condition: `user.id === 42 && cart.total > 1000`

// Add a watch expression
1. Open Debug sidebar
2. Click "+" in Watch section
3. Enter: `Array.from(cart.items).map(i => i.price)`

// Python logging setup (one-time)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

Your Codebase Isn't a Diary

Every time you add a print("HERE 1") statement, you're writing a debug memoir. Future archaeologists will study your code and wonder: "Why did they need to know the value was 42 at line 137, but only on Tuesdays?"

Print statements are the debugging equivalent of leaving sticky notes everywhere. They clutter your code, get committed by accident, and require manual cleanup. Meanwhile, professional debuggers have been solving problems without touching their source code for decades.

TL;DR

  • Conditional breakpoints let you pause execution only when specific conditions occur (like when user_id = 42 and total > $1000)
  • Watch expressions monitor variables without printing, updating in real-time as you step through code
  • Logging frameworks give you toggleable debug levels without code changes or redeploys

1. Conditional Breakpoints: The Sniper Rifle of Debugging

Instead of printing "LOOP ITERATION 47" and scrolling through 46 irrelevant outputs, set a breakpoint that only triggers when your bug occurs. Modern debuggers let you specify conditions using the same logic as your code.

Example: You have a bug where users with ID 42 see incorrect cart totals over $1000.

// Instead of:
if (user.id === 42 && cart.total > 1000) {
console.log("BUG CONDITION MET!");
console.log(cart.items);
}

// Set a conditional breakpoint with:
user.id === 42 && cart.total > 1000

The breakpoint only pauses execution when both conditions are true. You can inspect all variables at that moment without any console noise.

2. Watch Expressions: Your Real-Time Dashboard

Watch expressions are like having a live dashboard for your variables. They update automatically as you step through code, showing you exactly what's changing without a single print statement.

Pro move: Watch complex expressions, not just variables. Want to see how your data transforms through a pipeline? Add a watch for data.map(x => x.value).filter(v => v > 0).reduce((a,b) => a+b) and watch it update with each step.

3. Logging Frameworks: Debugging with a Volume Knob

Logging frameworks let you categorize messages by severity (DEBUG, INFO, WARN, ERROR) and change what gets logged without touching code. Running in production? Set level to WARN. Debugging locally? Crank it to DEBUG.

import logging

logger = logging.getLogger(__name__)

def process_order(order):
logger.debug(f"Processing order {order.id}") # Only shows in dev
# ... logic ...
logger.info(f"Order {order.id} completed") # Shows in prod too
if order.total > 10000:
logger.warning(f"Large order: {order.total}") # Always logs

Configure the logging level once (in a config file or environment variable), and your debug statements automatically appear or disappear.

4. Structured Logging: Making Logs Actually Searchable

Instead of print(f"User {id} purchased {item}"), structured logging outputs machine-readable JSON. Suddenly, you can search for all purchases by user 42, or find all errors that occurred between 2-3 AM.

// Instead of:
console.log(`Error: User ${userId} failed login`)

// Use structured logging:
logger.error("User login failed", {
userId: userId,
timestamp: new Date().toISOString(),
ip: request.ip,
userAgent: request.headers['user-agent']
})

Now your logging system can index these fields, and you can query them like a database. No more grepping through thousands of lines.

5. Debugger Navigation: The Step Family

Learn the difference between step-over, step-into, and step-out. They're not just random buttonsβ€”they're precision tools.

  • Step Over: Execute this line, but don't go into any functions it calls. Use when you trust the function works.
  • Step Into: Go inside the function being called. Use when you need to debug that specific function.
  • Step Out: Finish the current function and return to the caller. Use when you've seen enough and want to get out.

Common mistake: Stepping into library functions or framework code. You'll end up in a rabbit hole of someone else's code. Step over those unless you suspect the bug is in the library itself.

Pro Tips Section

πŸ’‘ Debug Like a Pro

1. The Rubber Duck Method 2.0: Explain your bug to the debugger instead of a duck. Set breakpoints at key decision points and walk through the logic step-by-step. The act of setting meaningful breakpoints often reveals the bug.

2. Reverse Debugging: Some debuggers (like rr for C/C++ or the Python debugger with certain setups) let you run backward. Hit a crash? Step backward to see what led to it.

3. Memory Breakpoints: Set a breakpoint that triggers when a specific variable changes value. Perfect for tracking down who's modifying data they shouldn't.

4. Remote Debugging: Debug code running on servers, containers, or even users' machines. VS Code and IntelliJ both support attaching to remote processes.

5. Tracepoints: Like breakpoints, but they log and continue instead of pausing. Perfect for adding temporary logging without code changes.

Conclusion: Stop Writing, Start Debugging

Print statements are the training wheels of debugging. They served you well when you were learning, but now it's time to ride without them. Professional debugging tools give you more power with less code pollution.

Your challenge: Next time you reach for a print statement, use a conditional breakpoint instead. Then try a watch expression. Then set up proper logging. Within a week, you'll wonder how you ever debugged any other way. Your codebase will thank you by not looking like a diary of your debugging struggles.

Action item: Open your current project right now and replace three print statements with proper debugger techniques. The first one will feel awkward. The third will feel like a superpower.

⚑

Quick Summary

  • What: Developers waste countless hours adding and removing print statements, creating temporary debugging code that clutters their codebase and slows down development cycles.

πŸ“š Sources & Attribution

Author: Code Sensei
Published: 26.02.2026 14:38

⚠️ AI-Generated Content
This article was created by our AI Writer Agent using advanced language models. The content is based on verified sources and undergoes quality review, but readers should verify critical information independently.

πŸ’¬ Discussion

Add a Comment

0/5000
Loading comments...