TDD rhythm
Test-driven development is a rhythm, not a ritual you perform for the commit
log. The value comes from writing the test first and watching it fail for the
right reason, which forces you to state the behavior before you build it.
Writing the code first and backfilling tests that pass on the first run is
theater: those tests echo the code's own mistakes and are blind to what it got
wrong.
Method
- Red: write the smallest test that fails for a missing behavior. One
assertion about one new capability,
assert total(empty_cart) == 0 before
any pricing logic exists. Small steps keep the feedback tight and the design
honest.
- Confirm the failure is the one you expect. A
NameError or import error
is a broken test, not a red bar. Drive to a real assertion failure, "expected
0, got None", so you know the test exercises the behavior and will later pass
for the right reason.
- Green: write the least code that makes it pass. A hardcoded return is
allowed here. The point is to close the loop and prove the test can pass; you
earn generality by adding the next failing test, not by guessing ahead.
- Refactor only on green. With the bar green, remove duplication, rename,
and extract, running the suite after each change. No new behavior enters
during a refactor; a new case is a fresh red test first, so you never mix "is
it broken" with "is it messy".
- Take small steps: one test, one reason to change. Keep each cycle to
minutes. If you are writing five tests before any code, or a hundred lines to
pass one assertion, the step is too big and the tight loop is already gone.
- Never backfill test-after and call it TDD. Code first, then tests that
mirror it, yields assertions that describe what the code does, not what it
should do. If a test must come after, prove it can fail by breaking the code
once, and do not fake a red-green history that never happened.
Litmus tests
- Did you watch every test fail, for the intended reason, before making it pass?
- Is each cycle small enough that the last green state is only minutes behind?
- Do the tests describe required behavior, or merely echo the current code?
Boundaries
TDD suits code with a behavioral spec you can state as a test. Exploratory
spikes where you do not yet know the interface are better thrown away and
rebuilt test-first, not forced through the loop. The rhythm does not replace
integration-testing or higher-level checks. To grade whether the resulting tests
actually catch bugs, defer to mutation-testing.
1---2name: tdd-rhythm3description: Run red-green-refactor honestly with the smallest failing test first, refusing test-after backfill dressed up as TDD. Use when building code with a clear behavioral spec and you want the tests to drive the design.4---56# TDD rhythm78Test-driven development is a rhythm, not a ritual you perform for the commit9log. The value comes from writing the test first and watching it fail for the10right reason, which forces you to state the behavior before you build it.11Writing the code first and backfilling tests that pass on the first run is12theater: those tests echo the code's own mistakes and are blind to what it got13wrong.1415## Method16171. **Red: write the smallest test that fails for a missing behavior.** One18 assertion about one new capability, `assert total(empty_cart) == 0` before19 any pricing logic exists. Small steps keep the feedback tight and the design20 honest.212. **Confirm the failure is the one you expect.** A `NameError` or import error22 is a broken test, not a red bar. Drive to a real assertion failure, "expected23 0, got None", so you know the test exercises the behavior and will later pass24 for the right reason.253. **Green: write the least code that makes it pass.** A hardcoded return is26 allowed here. The point is to close the loop and prove the test can pass; you27 earn generality by adding the next failing test, not by guessing ahead.284. **Refactor only on green.** With the bar green, remove duplication, rename,29 and extract, running the suite after each change. No new behavior enters30 during a refactor; a new case is a fresh red test first, so you never mix "is31 it broken" with "is it messy".325. **Take small steps: one test, one reason to change.** Keep each cycle to33 minutes. If you are writing five tests before any code, or a hundred lines to34 pass one assertion, the step is too big and the tight loop is already gone.356. **Never backfill test-after and call it TDD.** Code first, then tests that36 mirror it, yields assertions that describe what the code does, not what it37 should do. If a test must come after, prove it can fail by breaking the code38 once, and do not fake a red-green history that never happened.3940## Litmus tests4142- Did you watch every test fail, for the intended reason, before making it pass?43- Is each cycle small enough that the last green state is only minutes behind?44- Do the tests describe required behavior, or merely echo the current code?4546## Boundaries4748TDD suits code with a behavioral spec you can state as a test. Exploratory49spikes where you do not yet know the interface are better thrown away and50rebuilt test-first, not forced through the loop. The rhythm does not replace51integration-testing or higher-level checks. To grade whether the resulting tests52actually catch bugs, defer to mutation-testing.