Test-Driven Development workflow with mandatory Red-Green-Refactor cycle. Use when writing new features, fixing bugs, or refactoring code. Enforces "test first" discipline - no code without failing test.
Core Principle: "If you didn't watch the test fail, you don't know if it tests the right thing."
| Situation | TDD Required |
|---|---|
| New feature | ✅ Always |
| Bug fix | ✅ Always |
| Refactoring | ✅ Always |
| Behavior change | ✅ Always |
| Throwaway prototype | ⚠️ User approval needed |
| Config files only | ❌ Skip |
Create ONE minimal test demonstrating required behavior.
Quality Standards:
// Example: Testing retry logic
test('retries failed operations 3 times', async () => {
let attempts = 0;
const operation = () => {
attempts++;
if (attempts < 3) throw new Error('fail');
return 'success';
};
const result = await retryOperation(operation);
expect(result).toBe('success');
expect(attempts).toBe(3);
});
Must verify:
⚠️ If test passes immediately → You're testing existing code. Revise the test.
Write the simplest code that makes the test pass.
DO NOT:
// Minimal implementation for the test above
async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
for (let i = 0; i < 3; i++) {
try {
return await fn();
} catch (e) {
if (i === 2) throw e;
}
}
throw new Error('unreachable');
}
Must verify:
⚠️ If tests fail → Fix CODE, never the test.
Keep tests GREEN throughout refactoring.
NEVER fix a bug without writing a failing test first.
// Scenario: Empty email incorrectly accepted
// 1. RED: Write failing test
test('rejects empty email', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('Email required');
});
// 2. VERIFY RED: Run test, confirm it fails
// 3. GREEN: Minimal fix
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: 'Email required' };
}
// existing logic
}
// 4. VERIFY GREEN: All tests pass
| Excuse | Reality |
|---|---|
| "Tests added later achieve same goals" | Tests-after verify implementation; tests-first define requirements |
| "Manual testing suffices" | Ad-hoc testing lacks systematic rigor and reproducibility |
| "Deleting X hours of code is wasteful" | Sunk cost fallacy; unverified code creates technical debt |
| "Just this once" | This rationalization itself signals you MUST use TDD |
| "Spirit over ritual" | The ritual IS the spirit - process ensures quality |
Stop immediately and restart if:
Before marking task complete:
Incomplete checklist = TDD process was skipped → RESTART
# Frontend (Next.js)
cd frontend && npm test
# Backend (Python)
cd backend && pytest
# Watch mode
npm test -- --watch
pytest --watch
| Type | Location |
|---|---|
| Frontend unit | frontend/__tests__/ |
| Frontend component | frontend/components/**/*.test.tsx |
| Backend unit | backend/tests/ |
| E2E | frontend/e2e/ |
┌─────────────────────────────────────────┐
│ TDD CYCLE │
├─────────────────────────────────────────┤
│ 1. RED → Write failing test │
│ 2. VERIFY → Confirm it fails │
│ 3. GREEN → Minimal code to pass │
│ 4. VERIFY → Confirm all tests pass │
│ 5. REFACTOR → Improve, keep green │
├─────────────────────────────────────────┤
│ ⚠️ NO CODE WITHOUT FAILING TEST FIRST │
│ ⚠️ NO FIX WITHOUT UNDERSTANDING ROOT │
│ ⚠️ NO SKIP "JUST THIS ONCE" │
└─────────────────────────────────────────┘