DevelopmentBasic

Debug

The process of identifying, analyzing, and fixing errors (bugs) in code. A fundamental skill for every developer.

Pronunciation

/diːˈbʌɡ/
"dee-buhg"

What is it

Debug (or debugging) is the systematic process of finding and fixing errors in code. The name comes from “de-bug” = removing bugs.

It includes:

  1. Reproduce the problem
  2. Isolate the root cause
  3. Understand why it happens
  4. Fix the code
  5. Verify the solution works

Common pronunciation mistakes

  • ❌ “deh-bug” (the first ‘e’ is long like in “see”)
  • ❌ “de-boog” (the ‘u’ sounds like the ‘u’ in “cup”)

Variants:

  • Debug (verb): “I need to debug this”
  • Debugging (noun/gerund): “Debugging is an art”
  • Debugger (tool): “Open the debugger”

Debugging Techniques

1. Print Debugging (The most basic)

function calculateTotal(items) {
  console.log('Items received:', items);  // 👈 Debug

  let total = 0;
  for (const item of items) {
    console.log('Processing item:', item);  // 👈 Debug
    total += item.price;
    console.log('Partial total:', total);   // 👈 Debug
  }

  console.log('Final total:', total);  // 👈 Debug
  return total;
}

Pros: Quick, works anywhere Cons: Need to clean up after, no pause capability

2. Debugger with Breakpoints (The most powerful)

function calculateTotal(items) {
  debugger;  // 👈 Code will pause here

  let total = 0;
  for (const item of items) {
    total += item.price;  // You can set breakpoint here
  }

  return total;
}

Pros: Inspect variables live, step through execution Cons: Requires learning the tool

3. Structured Logging (The most professional)

import logger from './logger';

function calculateTotal(items) {
  logger.debug('Starting calculation', { itemCount: items.length });

  try {
    const total = items.reduce((sum, item) => sum + item.price, 0);
    logger.info('Calculation completed', { total });
    return total;
  } catch (error) {
    logger.error('Error in calculation', { error, items });
    throw error;
  }
}

Pros: Persistent, searchable, doesn’t modify behavior Cons: More initial setup

The Debugging Process

┌─────────────────────────────────────────────────────────────┐
│                    DEBUGGING CYCLE                           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│    1. REPRODUCE      2. HYPOTHESIZE    3. TEST              │
│    ┌───────────┐     ┌───────────┐     ┌───────────┐        │
│    │ Does it   │  →  │ What could│  →  │ Add logs  │        │
│    │ always    │     │ cause     │     │ or        │        │
│    │ fail?     │     │ this?     │     │breakpoints│        │
│    └───────────┘     └───────────┘     └───────────┘        │
│          ↑                                    ↓              │
│          │                                    │              │
│    6. VERIFY         5. FIX            4. ANALYZE           │
│    ┌───────────┐     ┌───────────┐     ┌───────────┐        │
│    │ Does fix  │  ←  │ Modify    │  ←  │ Was the   │        │
│    │ work?     │     │ the code  │     │ hypothesis│        │
│    │ No        │     │           │     │ correct?  │        │
│    │ regression│     │           │     │           │        │
│    └───────────┘     └───────────┘     └───────────┘        │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Debug Tools by Language

Language Tool Command/Shortcut
JavaScript Chrome DevTools F12 → Sources
Python pdb / debugpy breakpoint() (Python 3.7+)
Java IntelliJ Debugger Shift+F9
C# Visual Studio F5
Node.js VS Code Debugger F5
React React DevTools Chrome Extension

Real-World Example: Debugging a Login

The problem

“Users can’t log in. The form doesn’t do anything when submitted.”

Step 1: Reproduce

// Try to login and observe in console
form.addEventListener('submit', handleLogin);

Step 2: Add strategic logs

async function handleLogin(event) {
  console.log('1. Submit event fired');  // Does it execute?

  event.preventDefault();
  console.log('2. preventDefault executed');

  const email = document.getElementById('email').value;
  const password = document.getElementById('password').value;
  console.log('3. Data:', { email, password: '***' });

  try {
    console.log('4. Sending request...');
    // The frontend runs on http://localhost:5173 and the API on another origin
    const response = await fetch('http://localhost:3000/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    });
    console.log('5. Response:', response.status);

    const data = await response.json();
    console.log('6. Data:', data);

  } catch (error) {
    console.error('7. Error:', error);  // 🐛 Here it is!
  }
}

Step 3: Find the bug

Console:
1. Submit event fired
2. preventDefault executed
3. Data: { email: "user@test.com", password: "***" }
4. Sending request...
7. Error: TypeError: Failed to fetch  // 🐛 FOUND IT!

Step 4: Investigate further

// "Failed to fetch" points to a network or CORS problem.
// DevTools → Console/Network shows:
// "blocked by CORS policy: No 'Access-Control-Allow-Origin' header"
// The browser blocks the response because the backend (another origin)
// doesn't allow the frontend's origin.

Step 5: Fix

// backend/server.js (Express) - allow the frontend's origin
import cors from 'cors';

app.use(cors({ origin: 'http://localhost:5173' }));  // 🔧 The fix lives in the backend

// Development alternative: a dev-server proxy (e.g. Vite)
// so frontend and API share the same origin.

Advanced Techniques

Rubber Duck Debugging

Explain the problem to an object (like a rubber duck). By verbalizing the problem, you often find the solution.

Binary Search Debugging

If you have a bug in 1000 lines of code:

  1. Comment out half (lines 500-1000)
  2. Does the bug persist? → It’s in lines 1-500
  3. Comment out half of that range
  4. Repeat until you find the exact line

With Git, git bisect applies the same idea to history: it finds the commit that introduced the bug.

Time-Travel Debugging

Some tools allow you to “go back in time”:

  • Redux DevTools (JavaScript)
  • rr (C/C++)
  • WinDbg Time Travel Debugging (Windows)

Debugging Anti-patterns

Anti-pattern Problem Best practice
Random changes “Let me try this and see if it works” Form a hypothesis first
Ignoring stack trace Googling without reading the error Read the complete message
Not reproducing first Assuming you know the problem Always reproduce first
Debugging in production Modifying live code Use staging/local
Not writing tests The bug comes back later Add failing test before the fix

The Debugger’s Mantra

"Don't guess. Observe."
"If you can't reproduce it, you can't fix it."
"The code does exactly what you told it, not what you wanted."
  • Bug - The error we’re looking for
  • Breakpoint - Point where we pause execution
  • Stack Trace - Trail of calls leading to the error
  • Logging - Event recording for analysis

Remember: Debugging is a skill that improves with practice. The best debuggers aren’t those who know the most, but those who ask and observe the best.