Failing Test First
Makes the agent prove the bug exists — with a test that fails for the right reason — before it is allowed to fix anything.
When to reach for it
The moment a bug report, a stack trace or a “this sometimes does the wrong thing” lands, and the pull is to jump straight to a plausible fix.
What changes
- You watch the test fail before any fix exists, so “it is fixed” can never quietly mean “the test never ran the broken path”.
- The failure message is matched against the reported symptom; a test that fails for a different reason is rewritten rather than counted.
- The fix stays small, because the red test defines its edges — an edit that does not move the failure is not part of it.
- Undoing the fix brings back the exact recorded failure, which is the only real evidence the test is watching the change.
- Every closed bug leaves behind one test that would have caught it, so the same regression cannot return unnoticed.
Pairs with
- Flake HuntFinds out why a test is intermittent, instead of retrying it until the pipeline turns green.
- Behaviour, Not CoverageTurns “add some tests” into tests that state what the code promises, instead of tests that exist to move a percentage.
- Fixture DietCuts test setup down to the few facts the test actually depends on, so the test says what it means.
Failing Test First
A fix you never saw fail is a fix you never saw work. Get to red before you get to green.
1. Reproduce it in one runnable case
Reduce the report to the smallest thing that runs: one test, one script, one request with fixed input. Strip anything the failure does not need — no fixture that is not load-bearing, no random values, no live network.
2. Write the assertion for the correct behaviour
State what should happen, not what happens now, in the words of the report:
- "a full refund leaves a zero balance"
- "a second submit with the same key creates one order, not two"
3. Run it and read the failure
The test must fail for the reason you are claiming:
- An assertion failure showing the wrong value — this is red.
- An import error, a typo, a missing fixture, a timeout — this is broken, not red. Repair the test and run it again.
Copy the exact failure message somewhere. That string is now your definition of done.
4. Fix, and change nothing else
Write the smallest change that turns that message green. Renames, cleanups and improvements to the code next door go in a separate pass. If an edit does not move the failure, it is not part of the fix.
5. Confirm the test is the reason it passes
Undo the fix and run the test: the recorded failure should come back. Put the fix back and run the whole suite. Now you know the test watches this behaviour, and the fix broke nothing else.
6. Widen once
Ask which neighbour has the same shape — the same off-by-one, the same unguarded null, the same missing check on a second route — and add the cases that would catch it there.
Rules
- Do not write the fix first and the test after. A test written against fixed code proves the code is unchanged, not that it is right. If it already happened, undo the fix, run the test, and see it fail before putting the fix back.
- Do not accept a crash, an error or an empty run as a red test.
- Do not loosen the assertion until it passes. Softening a matcher to reach green is deleting the test without saying so.
- Do not close a bug with its test skipped, quarantined or commented out.
- Do not stop at the exact reported input when the same defect plainly has siblings.