Software Engineering11 min read·

Debugging Techniques Every Developer Should Know

Systematic approaches to finding and fixing bugs — from print statements to debuggers, logging strategies, and the mindset that makes debugging efficient.

Debugging Is a Skill, Not a Talent

Some developers seem to find bugs effortlessly. It is not magic — it is a systematic approach combined with experience. The good news is that the systematic part can be learned, and the experience part comes naturally with practice.

The single most important debugging principle: reproduce the bug reliably before trying to fix it. If you cannot make the bug happen on demand, any fix you apply is a guess.


Start Simple: Print Debugging

There is no shame in print debugging. It is fast, it works everywhere, and for many problems it is all you need.

def calculate_portfolio_value(positions, prices): total = 0 for symbol, qty in positions.items(): price = prices.get(symbol, 0) value = qty * price print(f"DEBUG: {symbol} qty={qty} price={price} value={value}") total += value print(f"DEBUG: total={total}") return total

The key is being strategic about what you print. Do not just dump everything — print the values at the boundaries where you think the bug might be. Narrow the search space with each iteration.

Upgrade: Use Logging

For anything beyond quick debugging, use Python's logging module instead of print:

import logging logger = logging.getLogger(__name__) def calculate_portfolio_value(positions, prices): total = 0 for symbol, qty in positions.items(): price = prices.get(symbol) if price is None: logger.warning(f"No price found for {symbol}, skipping") continue value = qty * price logger.debug(f"{symbol}: qty={qty} price={price} value={value}") total += value logger.info(f"Portfolio value calculated: {total}") return total

Logging has levels (DEBUG, INFO, WARNING, ERROR, CRITICAL) so you can control verbosity without removing code. In production, you run at INFO or WARNING level. When debugging, switch to DEBUG and get the full picture.


The Scientific Method of Debugging

Effective debugging follows a predictable process:

  1. Observe — what exactly is wrong? "It is broken" is not specific enough. "The VWAP calculation returns 0.0 when given valid trades" is specific.

  2. Hypothesise — based on the symptoms, what could cause this? "Maybe the volume data is zero" or "maybe the prices are strings instead of floats."

  3. Test — design an experiment to confirm or reject your hypothesis. Add a print statement, check the data, write a test case.

  4. Repeat — if your hypothesis was wrong, form a new one based on what you learned. Each failed hypothesis narrows the search space.

This sounds formal, but experienced developers do it instinctively. The discipline of making the process explicit helps when you are stuck.


Using a Debugger

Python's built-in debugger lets you pause execution and inspect the state of your program interactively:

def process_trades(trades): results = [] for trade in trades: breakpoint() # Execution pauses here processed = apply_rules(trade) results.append(processed) return results

When execution hits breakpoint(), you drop into an interactive prompt where you can:

(Pdb) p trade          # Print a variable
(Pdb) p type(trade)    # Check its type
(Pdb) n                # Execute next line
(Pdb) s                # Step into a function call
(Pdb) c                # Continue until next breakpoint
(Pdb) l                # Show surrounding code
(Pdb) pp vars()        # Pretty-print all local variables

For IDE users, VS Code and PyCharm have visual debuggers that show variables, call stacks, and let you set breakpoints by clicking in the margin. If you are not using these tools, you are making debugging harder than it needs to be.


Common Bug Patterns in Financial Code

Knowing the usual suspects saves time:

Off-by-One Errors

# Bug: should this be < or <= ? for i in range(len(prices) - 1): returns.append(prices[i+1] / prices[i] - 1) # Does this include the last return or not?

Floating Point Issues

# This will fail: assert 0.1 + 0.2 == 0.3 # False! # Use approximate comparison: assert abs((0.1 + 0.2) - 0.3) < 1e-10 # Or use the Decimal module for financial calculations from decimal import Decimal assert Decimal("0.1") + Decimal("0.2") == Decimal("0.3") # True

Mutable Default Arguments

# Bug: the default list is shared across all calls def add_trade(trade, trades=[]): trades.append(trade) return trades # Fix: use None as default def add_trade(trade, trades=None): if trades is None: trades = [] trades.append(trade) return trades

Silent Failures

# Dangerous: errors are silently ignored try: price = float(raw_price) except: pass # price is now undefined! # Better: handle specifically and log try: price = float(raw_price) except (ValueError, TypeError) as e: logger.error(f"Invalid price value: {raw_price!r} - {e}") raise

Rubber Duck Debugging

This is not a joke — it genuinely works. Explain the problem out loud (to a rubber duck, a colleague, or even just yourself). The act of articulating the problem in words forces you to think through each step systematically, and you will often spot the issue mid-explanation.

Many experienced developers have had the experience of walking to a colleague's desk to ask for help, explaining the problem, and realising the answer before the colleague says a word.


When You Are Truly Stuck

  1. Take a break. Seriously. Walk away for 15 minutes. Your subconscious keeps working on the problem.

  2. Simplify. Strip the problem down to the minimum reproduction case. Remove everything that is not relevant until you have the simplest possible code that exhibits the bug.

  3. Check your assumptions. The bug is often not where you think it is. Verify that your inputs are what you expect. Check that the function you are calling is the function you think it is.

  4. Read the error message carefully. This sounds obvious but is surprisingly often skipped. Python's tracebacks tell you exactly which line failed and why.

Good debugging skills pair naturally with good testing practices — tests that fail give you a precise starting point. And a well-structured development workflow with proper logging makes production bugs much easier to diagnose.

Want to go deeper on Debugging Techniques Every Developer Should Know?

This article covers the essentials, but there's a lot more to learn. Inside Quantt, you'll find hands-on coding exercises, interactive quizzes, and structured lessons that take you from fundamentals to production-ready skills — across 50+ courses in technology, finance, and mathematics.

Free to get started · No credit card required