AgentSkillsCN

bug-fixing

系统化的调试与 Bug 修复方法

SKILL.md
--- frontmatter
name: bug-fixing
description: Systematic approach to debugging and fixing bugs
license: MIT
compatibility: opencode

Bug Fixing Skill

Overview

This skill provides a systematic approach to debugging and fixing bugs efficiently while preventing regressions.

The Bug Fixing Process

Phase 1: Reproduction (Critical!)

Goal: Create a reliable reproduction case.

1.1 Gather Information

code
□ Bug report details: What happened vs expected
□ Environment: OS, browser, version, config
□ Steps to reproduce (user's description)
□ Error messages and stack traces
□ Logs (application, system, browser console)
□ Recent changes (git history, deployments)
□ Frequency: Always, sometimes, once?

1.2 Reproduce Locally

code
□ Set up same environment
□ Follow reported steps exactly
□ Try variations of steps
□ Test on different browsers/devices (if applicable)
□ Test with different data/inputs

If you can't reproduce:

  • Ask for more details
  • Check production logs more carefully
  • Add more logging
  • Consider race conditions or timing issues
  • Check for environment-specific issues

1.3 Document Reproduction

markdown
## Bug: [Brief description]

### Reproduction Steps
1. [Step 1]
2. [Step 2]
3. [Step 3]

### Expected
[What should happen]

### Actual
[What actually happens]

### Environment
- OS: [e.g., Ubuntu 22.04]
- Browser: [e.g., Chrome 120]
- Version: [commit hash or version]
- Config: [relevant settings]

Phase 2: Investigation & Isolation

Goal: Find the root cause, not just the symptom.

2.1 Binary Search Method

code
1. Identify entry point (user action/API call)
2. Add logging/checkpoints throughout code path
3. Run reproduction
4. Narrow down to specific function/line
5. Repeat until root cause found

2.2 Debugging Techniques

Logging Strategy:

go
// Add strategic logging
func processOrder(order *Order) error {
    log.Printf("[DEBUG] Processing order: %+v", order)
    
    if order.Total <= 0 {
        log.Printf("[DEBUG] Invalid order total: %f", order.Total)
        return fmt.Errorf("invalid order total: %f", order.Total)
    }
    
    user, err := userRepo.GetByID(order.UserID)
    if err != nil {
        log.Printf("[ERROR] Failed to get user %s: %v", order.UserID, err)
        return fmt.Errorf("get user: %w", err)
    }
    
    log.Printf("[DEBUG] Found user: %+v", user)
    // ...
}

Debugger Usage:

code
□ Set breakpoints at suspected locations
□ Inspect variable values
□ Step through execution
□ Watch expressions
□ Call stack analysis

2.3 Root Cause Analysis

Ask "Why" 5 Times:

code
Problem: Application crashes on checkout

Why 1: Why does it crash?
→ Null pointer exception on user.email

Why 2: Why is user.email nil?
→ User was created without email validation

Why 3: Why was validation skipped?
→ OAuth signup doesn't require email

Why 4: Why doesn't OAuth require email?
→ We assumed all OAuth providers return email

Why 5: Why did we assume that?
→ We didn't test with GitHub accounts that hide email

Root Cause: Missing email handling for OAuth providers

2.4 Common Bug Patterns

PatternCauseSolution
Null/undefinedMissing checksAdd validation
Race conditionConcurrencySynchronization, transactions
Off-by-oneIndex errorsBoundary testing
State mismatchStale dataCache invalidation, state sync
Type confusionDynamic typingType checking, validation
Resource leakMissing cleanupdefer, finally, cleanup functions

Phase 3: Fix Implementation

Goal: Fix the root cause, not the symptom.

3.1 Fix Guidelines

code
□ Fix the root cause, not the symptom
□ Minimal change principle
□ Don't break existing functionality
□ Add regression test first (TDD style)
□ Consider edge cases
□ Document why the fix works

3.2 Fix Structure

go
// 1. Regression test (write first)
func TestCheckout_WithGitHubOAuthUser(t *testing.T) {
    user := &User{
        ID:    "github-123",
        Name:  "Test User",
        Email: "", // GitHub OAuth with hidden email
    }
    
    err := checkoutService.ProcessOrder(user, order)
    assert.NoError(t, err) // Should not crash
}

// 2. The fix
func (s *CheckoutService) ProcessOrder(user *User, order *Order) error {
    // Handle missing email
    if user.Email == "" {
        // Option 1: Use default/fallback
        user.Email = s.generateEmailPlaceholder(user.ID)
        
        // Option 2: Prompt user (better UX)
        return ErrEmailRequired
        
        // Option 3: Use OAuth provider's API to fetch
        email, err := s.oauthService.FetchEmail(user.ID)
        if err != nil {
            return fmt.Errorf("fetch email: %w", err)
        }
        user.Email = email
    }
    
    // ... rest of checkout logic
}

3.3 Validation Checklist

code
□ Does it fix the reported issue?
□ Does it fix the root cause?
□ Are there side effects?
□ Does it work for all cases?
□ Is the code clean and maintainable?

Phase 4: Verification

Goal: Confirm the fix works and doesn't break anything.

4.1 Testing

code
□ Reproduction test passes
□ New regression test passes
□ Existing tests still pass
□ Edge cases tested
□ Integration tests pass
□ Manual verification

4.2 Test Commands

bash
# Run specific test
go test -run TestCheckout_WithGitHubOAuthUser ./...

# Run all tests
go test ./...

# Run with race detection
go test -race ./...

# Run integration tests
go test -tags=integration ./...

Phase 5: Deployment

Goal: Deploy safely with monitoring.

5.1 Pre-deployment

code
□ Code reviewed
□ Tests passing
□ Documentation updated
□ Monitoring/alerting in place

5.2 Deployment Strategy

code
□ Deploy to staging first
□ Smoke tests in staging
□ Deploy to production (gradual if possible)
□ Monitor error rates
□ Monitor performance

5.3 Post-deployment

code
□ Watch for 24-48 hours
□ Check error logs
□ Verify metrics are healthy
□ Communicate fix to stakeholders

Debugging Tools by Language

Go

bash
# Debug with delve
dlv debug main.go
dlv test
dlv attach <pid>

# Race detection
go run -race main.go

# Profile
import "runtime/pprof"

Rust

bash
# Debug with gdb/lldb
cargo build
gdb target/debug/myapp

# Backtrace on panic
RUST_BACKTRACE=1 cargo run

# Use log crate
log::debug!("Variable x = {}", x);

TypeScript/JavaScript

bash
# Node.js debugging
node --inspect-brk server.ts

# Chrome DevTools
# 1. Add 'debugger;' statements
# 2. Open DevTools
# 3. Sources tab → find file

# Console logging strategies
console.log("Debug:", { user, order, config });
console.table(data);
console.trace("Called from:");

Best Practices

1. Add Logging Before Fixing

go
// Temporary logging
log.Printf("[BUG] Variable state: x=%v, y=%v", x, y)
log.Printf("[BUG] Stack trace: %s", debug.Stack())

2. Create Minimal Reproduction

go
// Create test case that reproduces the bug
func TestBug_MissingEmail(t *testing.T) {
    // Minimal setup to reproduce
    user := &User{ID: "1"}
    err := sendEmail(user, "Hello")
    
    // Before fix: should fail
    // assert.Error(t, err)
    
    // After fix: should handle gracefully
    assert.NoError(t, err)
}

3. Document the Fix

go
// Fix for GitHub issue #123
// Users created via OAuth without email were causing nil pointer
// on checkout. Now we gracefully handle missing emails.
func (s *Service) ProcessOrder(user *User, order *Order) error {
    if user.Email == "" {
        return ErrEmailRequired
    }
    // ...
}

4. Prevent Future Bugs

go
// Add validation at creation time
type User struct {
    Email string `validate:"required,email"`
}

// Or database constraint
// ALTER TABLE users ADD CONSTRAINT email_required CHECK (email IS NOT NULL AND email != '');

When to Use

Use this skill when:

  • Debugging a reported bug
  • Investigating production issues
  • Writing regression tests
  • Reviewing bug fixes
  • Preventing bugs through design

Common Anti-patterns to Avoid

go
// DON'T: Fix the symptom
func process(user *User) {
    if user == nil {
        return // Silent failure - bad!
    }
}

// DO: Fix the root cause
func process(user *User) error {
    if user == nil {
        return fmt.Errorf("user is nil")
    }
    // ...
}

// DON'T: Comment out code instead of fixing
// if (buggyCondition) { ... }

// DO: Fix properly or delete entirely