Skip to content

Chiến thuật Debug 🔍

Debugging không phải nghệ thuật; nó là khoa học. Ngừng đoán mò.

Tip 1: Rubber Duck Debugging

Problem

Bạn stuck với bug hàng giờ, không biết bắt đầu từ đâu.

Solution

Rubber Duck Debugging: Giải thích code của bạn, từng dòng một, cho một vật vô tri (con vịt cao su).

Example

javascript
// Bug: Function trả về undefined
function calculateDiscount(price, coupon) {
  if (coupon.type === 'percentage') {
    return price * (coupon.value / 100);  // ← Giải thích: "Nếu coupon là %, tính..."
  } else if (coupon.type === 'fixed') {
    return price - coupon.value;  // ← "Nếu fixed, trừ đi..."
  }
  // ← "Ồ! Không có return cho case khác! Đó là bug!"
}

Why It Works

Bằng cách buộc bản thân phải diễn đạt logic, bạn kích hoạt một phần khác của não bộ. Bạn thường sẽ tìm ra bug trước khi giải thích xong.

Pro Tips

  • Không có vịt? Nói chuyện với màn hình, đồng nghiệp, hoặc thú cưng
  • Viết ra giấy cũng hiệu quả tương tự
  • Giải thích cho junior developer - teaching forces clarity

Tip 2: Binary Search Debugging (Git Bisect)

Problem

Bug không có ở tuần trước. Bây giờ nó ở đây. 50 commits đã xảy ra kể từ đó.

Solution

git bisect: Binary Search cho Git history - tìm commit gây bug trong O(log n).

Example

bash
# Start bisect
git bisect start

# Mark current commit as bad
git bisect bad

# Mark last known good commit
git bisect good abc123

# Git checkouts middle commit
# Test the code → Bug có không?

# If bug exists
git bisect bad

# If bug doesn't exist
git bisect good

# Repeat until Git finds the culprit commit
# Git will output: "abc123 is the first bad commit"

# End bisect
git bisect reset

Pro Tips

  • Automate testing: git bisect run npm test
  • Works with any binary condition (performance regression, etc.)
  • Combine với automated tests cho maximum efficiency

Tip 3: Wolf Fence Algorithm

Problem

Bug ở đâu đó trong file 2000 dòng. Không biết bắt đầu từ đâu.

Solution

Wolf Fence: Đừng tìm sói khắp rừng. Xây hàng rào ở giữa.

Example

javascript
// File 2000 dòng, bug ở đâu đó
function processData(data) {
  // ... 500 dòng code
  
  console.log('CHECKPOINT 1: data =', data);  // ← Hàng rào 1
  
  // ... 500 dòng code
  
  console.log('CHECKPOINT 2: data =', data);  // ← Hàng rào 2
  
  // ... 500 dòng code
}

Logic:

  • Nếu lỗi xảy ra trước CHECKPOINT 1 → Bug ở 500 dòng đầu
  • Nếu giữa CHECKPOINT 1 và 2 → Bug ở 500 dòng giữa
  • Lặp lại cho đến khi tìm thấy dòng chính xác

Pro Tips

  • Dùng debugger breakpoints thay vì console.log
  • Binary search: Luôn chia đôi search space
  • Remove checkpoints sau khi tìm thấy bug

Tip 4: Reproduce First, Fix Later

Problem

Bug xuất hiện ngẫu nhiên, không reproduce được.

Solution

Quy tắc vàng: Không fix bug không reproduce được. Nếu không reproduce được, bạn không biết đã fix chưa.

Example

javascript
// Bad: Fix ngay mà không hiểu
function getUser(id) {
  // Sometimes returns null, add fallback
  return users.find(u => u.id === id) || {};  // ← Band-aid fix
}

// Good: Reproduce first
function getUser(id) {
  const user = users.find(u => u.id === id);
  
  if (!user) {
    // Log để hiểu khi nào bug xảy ra
    console.error('User not found:', { id, availableIds: users.map(u => u.id) });
    throw new Error(`User ${id} not found`);
  }
  
  return user;
}

Pro Tips

  • Write a failing test that reproduces the bug
  • Document reproduction steps
  • If can't reproduce: Add logging và wait for more data
  • Intermittent bugs: Often race conditions hoặc timing issues

Tip 5: Divide and Conquer với Breakpoints

Problem

Code phức tạp với nhiều function calls, không biết function nào gây bug.

Solution

Đặt breakpoints ở các điểm chiến lược, inspect state từng bước.

Example

javascript
function checkout(cart, user, payment) {
  debugger;  // ← Breakpoint 1: Inspect inputs
  
  const total = calculateTotal(cart);
  debugger;  // ← Breakpoint 2: Verify total
  
  const discount = applyDiscount(total, user);
  debugger;  // ← Breakpoint 3: Verify discount
  
  const result = processPayment(discount, payment);
  debugger;  // ← Breakpoint 4: Verify payment
  
  return result;
}

Workflow:

  1. Run code với debugger
  2. Inspect variables tại mỗi breakpoint
  3. Identify nơi data becomes incorrect
  4. Focus vào function đó

Pro Tips

  • Conditional breakpoints: Chỉ break khi condition true
  • Logpoints: Log without stopping execution
  • Watch expressions: Monitor specific variables
  • Call stack: Trace function call chain

Tip 6: Isolate the Problem

Problem

Bug xảy ra trong hệ thống phức tạp với nhiều dependencies.

Solution

Isolate code thành smallest reproducible example.

Example

javascript
// Complex system
class UserService {
  constructor(db, cache, logger, emailService) { ... }
  async updateUser(id, data) {
    // Bug somewhere in 50 lines
  }
}

// Isolated test
function testUpdateUser() {
  // Mock dependencies
  const mockDb = { update: jest.fn() };
  const mockCache = { invalidate: jest.fn() };
  
  const service = new UserService(mockDb, mockCache, null, null);
  
  // Test specific scenario
  service.updateUser(123, { name: 'Test' });
  
  // Verify behavior
  expect(mockDb.update).toHaveBeenCalledWith(123, { name: 'Test' });
}

Pro Tips

  • Remove dependencies one by one
  • Create minimal reproduction in separate file
  • Share minimal repro when asking for help
  • Often the process of isolating reveals the bug

Tip 7: Read Error Messages Carefully

Problem

Panic khi thấy error message dài, không đọc kỹ.

Solution

Đọc từ dưới lên: Stack trace thường có framework code ở trên, YOUR code ở dưới.

Example

Error: Cannot read property 'name' of undefined
    at React.createElement (react.js:123)
    at renderComponent (react-dom.js:456)
    at Component.render (react-dom.js:789)
    at UserProfile.render (UserProfile.jsx:42)  ← START HERE!
    at App.render (App.jsx:15)

Reading Strategy:

  1. Đọc error message: "Cannot read property 'name' of undefined"
  2. Scroll xuống stack trace
  3. Tìm file BẠN viết: UserProfile.jsx:42
  4. Mở file đó, line 42
  5. Inspect: Variable nào là undefined?

Pro Tips

  • Framework errors ở trên: Bỏ qua
  • YOUR code ở dưới: Focus vào đây
  • Line numbers: Exact location của bug
  • Error message: Tells you WHAT went wrong

Tip 8: Use Debugger, Not console.log

Problem

console.log everywhere, phải rebuild và refresh mỗi lần thay đổi.

Solution

Dùng debugger để inspect state real-time, không cần rebuild.

Example

javascript
// Bad: console.log debugging
function processOrder(order) {
  console.log('order:', order);
  const total = calculateTotal(order.items);
  console.log('total:', total);
  const tax = calculateTax(total);
  console.log('tax:', tax);
  return total + tax;
}

// Good: Debugger
function processOrder(order) {
  debugger;  // ← Pause here, inspect everything
  const total = calculateTotal(order.items);
  const tax = calculateTax(total);
  return total + tax;
}

Debugger Advantages:

  • Inspect ALL variables, không chỉ những gì bạn log
  • Step through code line by line
  • Modify variables on the fly
  • No rebuild needed

Pro Tips

  • Browser DevTools: F12 → Sources → Set breakpoints
  • VS Code: F5 → Start debugging
  • Node.js: node --inspect-brk app.js
  • Conditional breakpoints: Break only when condition true

Tip 9: Check Your Assumptions

Problem

Bug tồn tại vì assumptions của bạn sai.

Solution

Question everything: Verify mọi assumption bằng data.

Example

javascript
// Assumption: API always returns array
function displayUsers(users) {
  return users.map(u => u.name);  // ← Crashes if users is null/undefined
}

// Verify assumption
function displayUsers(users) {
  console.assert(Array.isArray(users), 'users must be array', users);
  
  if (!Array.isArray(users)) {
    console.error('Invalid users data:', users);
    return [];
  }
  
  return users.map(u => u.name);
}

Common False Assumptions:

  • "API always returns 200"
  • "User always logged in"
  • "Array always has items"
  • "Database always available"
  • "Network always fast"

Pro Tips

  • Add assertions to verify assumptions
  • Defensive programming: Validate inputs
  • Fail fast: Throw errors early
  • Document assumptions in comments

Tip 10: Take a Break

Problem

Stuck với bug hàng giờ, càng debug càng confused.

Solution

Step away: Đi dạo, uống cà phê, làm việc khác 15-30 phút.

Why It Works

  • Einstellung Effect: Fixation on wrong solution blocks correct solution
  • Fresh perspective: Brain processes information subconsciously
  • Reduced stress: Stress impairs problem-solving
  • Diffuse thinking: Allows creative connections

Example

10:00 AM - Start debugging
11:30 AM - Still stuck, frustrated
12:00 PM - Take lunch break  ← CRITICAL
12:30 PM - Return to code
12:35 PM - "Oh! The bug is obvious now!"

Pro Tips

  • Set time limit: If stuck > 1 hour, take break
  • Physical activity: Walk, stretch, exercise
  • Context switch: Work on different problem
  • Sleep on it: Overnight often brings clarity
  • Pair programming: Fresh eyes see what you miss

📋 Quick Reference

StrategyWhen to UseBenefit
Rubber DuckStuck, don't know where to startForces clear thinking
Git BisectBug appeared recentlyFind culprit commit fast
Wolf FenceLarge codebaseNarrow down location
Reproduce FirstIntermittent bugsEnsure fix works
BreakpointsComplex logicInspect state step-by-step
IsolateMany dependenciesSimplify problem
Read ErrorsStack tracesFind exact location
DebuggerAny bugBetter than console.log
Check AssumptionsMysterious bugsFind false beliefs
Take BreakStuck > 1 hourFresh perspective

🎯 Luyện tập ngay