Skip to main content
โšก Calmops

Debugging Techniques: Find and Fix Bugs Effectively

Debugging is a critical skill for developers. This comprehensive guide covers systematic approaches and tools for finding and fixing bugs effectively.

Debugging Process

The Scientific Method

flowchart TD
    A[Observe Problem] --> B[Form Hypothesis]
    B --> C[Predict Outcome]
    C --> D[Test Prediction]
    D --> E{Matches Prediction?}
    E -->|Yes| F[Confirm Fix]
    E -->|No| G[Form New Hypothesis]
    F --> H[Document Solution]
    G --> A

Browser DevTools

Console Methods

// Basic logging
console.log('Value:', value);
console.info('Info message');
console.warn('Warning');
console.error('Error!');

// Styled logging
console.log('%cStyled text', 'color: blue; font-size: 14px');

// Table output
console.table([
  { name: 'John', age: 30 },
  { name: 'Jane', age: 25 }
]);

// Grouping
console.group('User Details');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.groupEnd();

// Timing
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData');

// Stack trace
console.trace('How did we get here?');

// Assertions
console.assert(user.isValid, 'User is invalid!');

Breakpoints

// In browser DevTools:
// 1. Set breakpoint in Sources panel
// 2. Or use debugger keyword

function processOrder(order) {
  debugger;  // Execution pauses here
  
  // Check variables
  const total = calculateTotal(order.items);
  
  // Step through code
  return applyDiscount(total);
}

// Conditional breakpoint
// Right-click breakpoint in DevTools
// Condition: user.id === null

Network Debugging

// Check API calls in Network tab
// Filter by: XHR, Fetch, WS

// Common issues to find:
// 1. Failed requests (red)
// 2. Wrong status codes
// 3. Slow responses
// 4. Wrong request/response format

// Copy as cURL
// Right-click request > Copy > Copy as cURL

Debugging JavaScript

Console Debugging

function findBug(data) {
  console.log('Input:', data);
  
  const result = data.items
    .filter(item => item.active)
    .map(item => item.value);
  
  console.log('Filtered:', result);
  
  const sum = result.reduce((a, b) => a + b, 0);
  console.log('Sum:', sum);
  
  return sum;
}

Using debugger

function debugFunction(data) {
  const processed = data.map(item => {
    debugger;  // Pause on each iteration
    
    return transform(item);
  });
  
  return processed;
}

Error Stack Traces

// Read stack traces carefully
Error: Cannot read property 'name' of undefined
    at processUser (user.js:45)
    at handleRequest (handler.js:12)
    at app.get (app.js:30)
    
// Line numbers point to the bug:
// user.js:45 - where error occurred
// user.js:30 - called from here
// ...

Node.js Debugging

Built-in Debugger

# Start with debugger
node inspect app.js

# Commands in debugger:
# cont, c - Continue execution
# next, n - Step next
# step, s - Step in
# out, o - Step out
# repl - Evaluate code

Debug in VSCode

// .vscode/launch.json
{
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug",
      "skipFiles": ["<node_internals>/**"],
      "program": "${workspaceFolder}/app.js"
    },
    {
      "type": "node",
      "request": "attach",
      "name": "Attach to Process",
      "port": 9229
    }
  ]
}

Node.js Logging

const pino = require('pino');
const logger = pino({
  level: process.env.LOG_LEVEL || 'info'
});

logger.info('Application started');
logger.error({ err }, 'Failed to connect');
logger.debug({ query }, 'Database query');

Logging Strategies

Structured Logging

// Good structured logging
logger.info({
  event: 'user_created',
  userId: user.id,
  email: user.email,
  source: 'registration_form',
  duration: Date.now() - startTime
}, 'User created successfully');

// Bad - hard to parse
console.log('User created: ' + user.id);

Log Levels

// Trace - detailed tracing
logger.trace({ fn: 'calculate' }, 'entering function');

// Debug - debugging info
logger.debug({ items: cart.length }, 'Processing cart');

// Info - important events
logger.info({ orderId: order.id }, 'Order placed');

// Warn - warning
logger.warn({ retry: attempt }, 'Retrying request');

// Error - errors
logger.error({ err, orderId: order.id }, 'Payment failed');

// Fatal - critical
logger.fatal({ err }, 'Database connection lost');

Common Bug Patterns

Null/Undefined

// Bug
const name = user.profile.name;  // Crash if profile is null

// Fix - optional chaining
const name = user?.profile?.name;

// Fix - default value
const name = user?.profile?.name ?? 'Unknown';

Async Issues

// Bug - not awaiting
async function getData() {
  fetch('/api/data');  // Forgot await!
  return 'done';       // Wrong!
}

// Fix
async function getData() {
  const result = await fetch('/api/data');
  return result.json();
}

Scope Issues

// Bug - closure issue in loop
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);  // Prints 3, 3, 3!
}

// Fix - use let
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);  // Prints 0, 1, 2
}

// Fix - use closure
for (var i = 0; i < 3; i++) {
  ((index) => {
    setTimeout(() => console.log(index), 100);
  })(i);
}

Debugging Production

Remote Debugging

# Enable debugging on remote server
node --inspect=0.0.0.0:9229 app.js

# Connect from local
chrome://inspect
# Click "Configure" and add remote server

Health Checks

// Add health check endpoint
app.get('/health', (req, res) => {
  const healthcheck = {
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    cpu: process.cpuUsage(),
    database: db.isConnected() ? 'ok' : 'error'
  };
  
  const status = healthcheck.database === 'ok' ? 200 : 503;
  res.status(status).json(healthcheck);
});

Error Tracking

// Sentry integration
const Sentry = require('@sentry/node');
Sentry.init({ dsn: process.env.SENTRY_DSN });

// Capture errors
app.use(Sentry.Handlers.errorHandler());

// Manual capture
try {
  riskyOperation();
} catch (err) {
  Sentry.captureException(err);
}

Performance Debugging

Chrome Performance Tab

// 1. Open DevTools > Performance
// 2. Click Record
// 3. Perform actions
// 4. Stop and analyze

// Look for:
// - Long tasks (main thread blocking)
// - Large layout shifts
// - Expensive reflows
// - Memory leaks

Memory Leaks

// Check for memory leaks:
// 1. Performance > Memory > Take heap snapshot
// 2. Do action multiple times
// 3. Take another snapshot
// 4. Compare snapshots

// Common causes:
const cache = {};
function addToCache(key, value) {
  cache[key] = value;  // Growing unbounded!
}

// Fix with limits
const cache = new Map();
const MAX_SIZE = 100;
function addToCache(key, value) {
  if (cache.size >= MAX_SIZE) {
    cache.delete(cache.keys().next().value);
  }
  cache.set(key, value);
}

Testing to Debug

Writing Reproducing Tests

// Write a failing test that reproduces the bug
describe('Bug: cart total calculation', () => {
  it('should handle empty cart', () => {
    const cart = { items: [] };
    expect(calculateTotal(cart)).toBe(0);
  });
  
  it('should apply discount correctly', () => {
    const cart = { 
      items: [{ price: 100 }],
      discount: 0.1
    };
    expect(calculateTotal(cart)).toBe(90);
  });
  
  it('should handle negative prices', () => {
    const cart = { 
      items: [{ price: -10 }]  // Bug: doesn't handle negative!
    };
    // This test will fail, revealing the bug
    expect(calculateTotal(cart)).toBe(0);
  });
});

Debugging Checklist

## Debugging Checklist

- [ ] Understand the error message
- [ ] Find the exact line causing the error
- [ ] Check input values at that point
- [ ] Look at function call stack
- [ ] Reproduce the issue consistently
- [ ] Check for race conditions
- [ ] Verify assumptions
- [ ] Test edge cases
- [ ] Add logging to trace values
- [ ] Write a test to reproduce

Tools

Tool Purpose
Chrome DevTools Frontend debugging
VSCode Debugger Step-through debugging
Node Inspector Node.js debugging
Sentry Error tracking
Datadog APM
Wireshark Network debugging
Postman API testing

External Resources

Conclusion

Effective debugging requires:

  • Systematic approach
  • Understanding of the codebase
  • Proper tools
  • Good logging
  • Ability to reproduce issues

Practice makes perfect - the more you debug, the faster you become at finding bugs.

Key techniques:

  • Use breakpoints strategically
  • Add logging to trace values
  • Write tests to reproduce issues
  • Check assumptions
  • Use error tracking tools

Comments