Debugging, Testing & Troubleshooting Mastery¶
Professional debugging and testing strategies with Claude Code CLI.
What You'll Learn¶
- Systematic debugging methodology
- Test-driven development with Claude CLI
- Production troubleshooting patterns
- Performance debugging
- Error analysis and resolution
The Debugging Mindset¶
Effective debugging with Claude Code CLI follows a systematic approach:
┌─────────────────────────────────────────────────────────────┐
│ DEBUGGING METHODOLOGY │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. OBSERVE → 2. HYPOTHESIZE → 3. TEST │
│ What exactly What could cause Verify with │
│ is happening? this behavior? evidence │
│ │
│ 4. ISOLATE → 5. FIX → 6. VERIFY │
│ Narrow down Implement the Confirm fix, │
│ the source solution no regressions │
│ │
└─────────────────────────────────────────────────────────────┘
Providing Context to Claude CLI¶
The Gold Standard Error Report¶
When debugging, give Claude CLI complete information:
> I'm getting this error:
TypeError: Cannot read property 'id' of undefined
at UserService.getProfile (src/services/user.ts:45:23)
at UserController.show (src/controllers/user.ts:23:30)
at Router.handle (/node_modules/express/lib/router/index.js:174:3)
This happens when:
- User is logged in
- They visit /profile
- But only sometimes (maybe 1 in 10 requests)
Expected: Profile page loads
Actual: 500 error
Recent changes:
- Added caching to user lookup yesterday
- No changes to the profile route
Structured Debugging Session¶
> Debug mode: I'll share information step by step.
Error: User profile fails to load
Frequency: Intermittent (10% of requests)
Started: After yesterday's deploy
Let's investigate systematically.
Common Bug Patterns¶
Pattern 1: Null/Undefined Errors¶
> This function throws "Cannot read property 'email' of undefined":
async function sendWelcomeEmail(userId: string) {
const user = await db.users.findById(userId);
await mailer.send(user.email, 'Welcome!');
}
The userId is always valid. Why might user be undefined?
Claude CLI will:
1. Check if findById can return undefined
2. Examine database queries
3. Look for race conditions
4. Suggest defensive coding
Pattern 2: Async/Timing Issues¶
> This test passes locally but fails in CI:
test('should update user', async () => {
await updateUser(userId, { name: 'New Name' });
const user = await getUser(userId);
expect(user.name).toBe('New Name');
});
Error: Expected "New Name", got "Old Name"
Claude CLI checks for:
- Missing await statements
- Database transaction timing
- Cache invalidation issues
- Test isolation problems
Pattern 3: State Management Bugs¶
> React component shows stale data:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, []); // <-- dependency array
return <div>{user?.name};
}
When userId changes, it still shows the old user.
Pattern 4: Race Conditions¶
> Sometimes get duplicate entries in the database:
async function createOrder(userId, items) {
const existing = await db.orders.findPending(userId);
if (!existing) {
return await db.orders.create({ userId, items });
}
return existing;
}
Two orders appear when user double-clicks submit.
Test-Driven Development with Claude CLI¶
TDD Workflow¶
> I'm implementing a password validator. Let's do TDD.
Requirements:
- Minimum 8 characters
- At least one uppercase
- At least one lowercase
- At least one number
- At least one special character
Start by writing the tests.
Claude CLI creates comprehensive tests first:
// password-validator.test.ts
describe('validatePassword', () => {
describe('length requirements', () => {
it('should reject passwords shorter than 8 characters', () => {
expect(validatePassword('Abc1!@#')).toBe(false); // 7 chars
});
it('should accept passwords of exactly 8 characters', () => {
expect(validatePassword('Abcd1!@#')).toBe(true); // 8 chars
});
});
describe('character requirements', () => {
it('should require at least one uppercase letter', () => {
expect(validatePassword('abcd1!@#9')).toBe(false);
});
// ... more tests
});
describe('edge cases', () => {
it('should handle empty string', () => {
expect(validatePassword('')).toBe(false);
});
it('should handle unicode characters', () => {
expect(validatePassword('Über1!@#')).toBe(true);
});
});
});
Then implement:
Test Coverage Analysis¶
> Analyze test coverage for the auth module.
- Which functions are untested?
- Which branches are not covered?
- What edge cases are missing?
Writing Tests for Existing Code¶
> Write comprehensive tests for src/services/payment.ts
- Cover all public methods
- Include error scenarios
- Mock external dependencies
- Follow existing test patterns in this project
Debugging Strategies¶
Strategy 1: Binary Search¶
> The bug was introduced sometime in the last 20 commits.
Let's use git bisect to find when.
Good commit: abc123 (2 weeks ago, release 1.5)
Bad commit: HEAD
Guide me through the bisect process.
Strategy 2: Isolation¶
> This API endpoint is slow. Let's isolate the bottleneck.
Current time breakdown:
- Total request: 2.5 seconds
Help me add timing to each step:
1. Request parsing
2. Authentication
3. Database queries
4. Business logic
5. Response serialization
Strategy 3: Reproduction¶
> I can't reproduce this bug locally.
Error from production logs:
[error message]
Environment differences:
- Prod: PostgreSQL 14, Node 18, Redis cluster
- Local: PostgreSQL 13, Node 20, Redis single
Help me create a reproduction case.
Strategy 4: Rubber Duck Debugging¶
> Let me explain this bug to you step by step.
Stop me when you spot the issue.
1. User clicks "Submit Order"
2. Frontend sends POST to /api/orders
3. Backend validates the cart...
[continue explaining]
Production Troubleshooting¶
Log Analysis¶
> Analyze these production logs:
[paste logs]
Find:
1. Error patterns
2. Timing anomalies
3. User impact scope
4. Root cause indicators
Memory Leak Investigation¶
> The application's memory usage grows over time:
Hour 1: 250MB
Hour 4: 450MB
Hour 8: 800MB
Hour 12: Crash (OOM)
Here's a heap snapshot comparison: [paste]
Identify the leak source.
Performance Profiling¶
> This query is slow in production:
SELECT * FROM orders
WHERE user_id = ?
AND status = 'pending'
ORDER BY created_at DESC;
Execution time: 2.3 seconds
Table has 5 million rows
Analyze and suggest indexes.
Incident Response¶
> Production incident: API returning 503 errors
Current status:
- Error rate: 45%
- Started: 10 minutes ago
- No recent deploys
Recent events:
- Traffic spike from marketing campaign
- Database CPU at 95%
Guide me through triage and mitigation.
Testing Best Practices¶
Test Structure¶
> Review our test structure and suggest improvements:
tests/
├── unit/
├── integration/
└── e2e/
Current issues:
- Tests are slow (5 minutes)
- Flaky tests in CI
- Hard to run single tests
Mocking Strategy¶
> What's the right mocking strategy for this service?
UserService depends on:
- Database (Prisma)
- Email service (SendGrid)
- Cache (Redis)
- External API (Stripe)
Help me create proper mocks that:
1. Are maintainable
2. Don't hide real bugs
3. Run fast
Snapshot Testing¶
> Should we use snapshot tests for our React components?
Current situation:
- 50 components
- Frequent UI changes
- Small team
Give me pros/cons for our case.
Integration Test Design¶
> Design integration tests for the checkout flow:
1. Add items to cart
2. Apply discount code
3. Enter shipping info
4. Process payment
5. Send confirmation
Consider:
- Test data setup
- External service mocking
- Cleanup after tests
Debugging CLI Commands¶
Quick Debug Helpers¶
# Add to your shell config
# Quick error lookup
debug-error() {
claude -p "Explain this error and common solutions: $*"
}
# Test failure analysis
debug-test() {
npm test 2>&1 | claude -p "Analyze these test results and suggest fixes for failures"
}
# Log analysis
debug-logs() {
tail -100 "$1" | claude -p "Analyze these logs for errors and issues"
}
# Stack trace analysis
debug-stack() {
claude -p "Analyze this stack trace and identify the root cause: $(cat)"
}
# Usage:
# debug-error "TypeError: Cannot read property 'foo' of undefined"
# debug-test
# debug-logs /var/log/app.log
# cat error.log | debug-stack
Interactive Debugging Session¶
# Start a focused debugging session
alias debug-session='claude "I need to debug an issue. I will provide information step by step. Ask me questions to help isolate the problem."'
Error Handling Patterns¶
Defensive Coding¶
> Review error handling in src/services and suggest improvements:
Look for:
1. Unhandled promise rejections
2. Missing null checks
3. Swallowed errors
4. Improper error propagation
5. Missing error context
Error Boundaries¶
> Design an error handling strategy for our Express API:
Requirements:
- Consistent error response format
- Proper HTTP status codes
- Error logging with context
- No sensitive data leakage
- Client-friendly messages
Graceful Degradation¶
> The payment service occasionally times out.
Design graceful degradation:
- User should see helpful message
- Order should be recoverable
- Support should be notified
- Metrics should track failures
Troubleshooting Checklist¶
Before Asking Claude CLI¶
## Debug Information Checklist
- [ ] Exact error message and stack trace
- [ ] Steps to reproduce
- [ ] Expected vs actual behavior
- [ ] Environment details (OS, Node version, etc.)
- [ ] Recent changes (code, config, infrastructure)
- [ ] Frequency (always, sometimes, once)
- [ ] Scope (all users, specific users, specific data)
Systematic Investigation¶
> Help me investigate systematically. Don't jump to conclusions.
Issue: [describe]
Let's:
1. List all possible causes
2. Rank by likelihood
3. Design tests for each hypothesis
4. Eliminate causes one by one
Try It Yourself¶
Exercise 1: Debug a Tricky Bug¶
Create a file with a subtle bug and practice debugging:
# Create buggy code
claude "Create a JavaScript file with a subtle async race condition bug that only manifests sometimes"
# Try to find it
claude "Debug this file - there's a race condition somewhere"
Exercise 2: TDD Practice¶
# Start with requirements
claude "Let's practice TDD. Give me requirements for a URL shortener. Don't write any implementation yet."
# Write tests first
claude "Write comprehensive tests for these requirements"
# Implement to pass tests
claude "Now implement the URL shortener to pass all tests"
Exercise 3: Production Debugging¶
# Simulate production logs
claude "Generate realistic production error logs for a Node.js API with various issues"
# Practice analysis
claude "Analyze these logs and identify all issues, ranked by severity"
What's Next?¶
Learn advanced CLI techniques in 08-cli-power-user.
Summary: - Follow systematic methodology: Observe → Hypothesize → Test → Isolate → Fix → Verify - Provide complete context: error, stack trace, steps, environment, recent changes - Use TDD: Write tests first, implement to pass, refactor - Common patterns: null checks, async issues, race conditions, state bugs - Production debugging: Log analysis, profiling, incident response - Create CLI helpers for common debugging tasks - Always verify fixes don't introduce regressions
Learning Resources¶
Featured Video¶
GitHub: TDD & Debugging with AI (Official GitHub channel)
Advanced debugging - systematic methodology, TDD workflows, and production troubleshooting.
Additional Resources¶
| Type | Resource | Description |
|---|---|---|
| 🎬 Video | Claude Code Debug Mastery | Edmund Yong - Advanced debugging |
| 📚 Official Docs | Troubleshooting | Official debugging guide |
| 📖 Tutorial | TDD with AI | AI-powered TDD patterns |
| 🎓 Free Course | Microsoft Debug Course | Free 10-hour course |
| 💼 Commercial | GitHub Copilot Pro | Advanced debug techniques |