Test-Driven Development workflow for implementing features with confidence.
Philosophy: Use unit tests to express requirements, then implement to satisfy them.
RED GREEN REFACTOR
[Write Test] ────> [Make it Pass] ────> [Improve Code]
│ │ │
└─────────────────────┴──────────────────────┘
(repeat)
Goal: Write failing tests that describe the desired behavior.
## Requirements Analysis
### Feature Description
[What does this feature do?]
### Acceptance Criteria
1. [Criterion 1]
2. [Criterion 2]
3. [Criterion 3]
### Test Cases
| Input | Expected Output | Edge Case? |
|-------|-----------------|------------|
| X | Y | No |
| Z | Error | Yes |Test Writing Rules:
- Test names describe behavior:
Test<Function>_<Scenario>_<Expected> - Use table-driven tests for multiple cases
- Cover happy path first, then edge cases
- Tests must FAIL initially (proves they test something)
func TestCalculateVitality_NewTask_Returns100(t *testing.T) {
task := Task{CreatedAt: time.Now()}
vitality := CalculateVitality(task)
if vitality != 100 {
t.Errorf("CalculateVitality() = %d, want 100", vitality)
}
}Verification:
go test ./... -run TestNewFeature
# MUST FAIL - if passes, test is wrongGoal: Write the simplest code that makes tests pass.
Rules:
- Only write code to pass the current test
- No premature optimization
- No unnecessary abstractions
- Ugly code is OK (we'll fix in refactor)
## Implementation Plan
### Current Test to Satisfy
[Test name]
### Minimal Implementation
[Simplest code that passes]
### Verification
```bash
go test ./... -run TestNewFeature
# MUST PASS
**STOP**: Do not write more code than needed to pass the test!
### Phase 3: REFACTOR - Improve Code Quality
**Goal**: Clean up code while keeping tests green.
**Refactoring Checklist**:
- [ ] Remove duplication (DRY)
- [ ] Improve naming (clarity)
- [ ] Extract methods (single responsibility)
- [ ] Simplify logic (KISS)
- [ ] Remove dead code
**Code Smells to Fix**:
- Long methods (>20 lines)
- Duplicate code
- Magic numbers
- Deep nesting
- Unclear names
**Rules**:
1. Make one change at a time
2. Run tests after EVERY change
3. Revert immediately if tests fail
4. External behavior MUST NOT change
```bash
# After each refactoring step:
go test ./...
# ALL MUST PASS
Continue the cycle:
- Add next test (RED)
- Implement (GREEN)
- Refactor (REFACTOR)
Until all acceptance criteria are met.
func TestWeeklyStreak_FirstReview_StreakIsOne(t *testing.T) {
svc := setupTestService(t)
svc.OnWeeklyReview()
streak, _ := svc.storage.GetStat("weekly_streak")
if streak != 1 {
t.Errorf("streak = %d, want 1", streak)
}
}go test -run TestWeeklyStreak
# FAIL: streak = 0, want 1func (s *Service) OnWeeklyReview() {
s.storage.SetStat("weekly_streak", 1) // Simplest fix
}go test -run TestWeeklyStreak
# PASSNo refactoring needed yet - code is simple.
func TestWeeklyStreak_ConsecutiveWeeks_Increments(t *testing.T) {
// ... test consecutive weeks
}And continue...
- Core business logic: >90% coverage
- Check coverage after completing feature:
go test -cover ./internal/...- Write implementation before tests
- Write multiple tests before implementing
- Skip refactoring phase
- Commit with failing tests
- Over-engineer in GREEN phase
- Tests that always pass (not testing anything)
- Tests that test implementation details
- Tests without assertions
- Flaky tests
Use this command when user says:
- "TDD"
- "Test driven"
- "Write tests first"
- "Implement with tests"
"Make it work, make it right, make it fast." - Kent Beck
The order matters:
- Work: Tests pass (GREEN)
- Right: Clean code (REFACTOR)
- Fast: Optimize only when needed
Feature is complete when:
- All acceptance criteria have corresponding tests
- All tests pass
- Coverage > 90% for new code
- Code is clean and well-organized
- No code smells
-
go buildandgo testboth succeed