Prove First
Write the test before writing the code. Watch the test fail. Write the minimal code to make it pass. Verify. Commit. This is not a suggestion. This is the discipline that separates working code from code that happens to work right now.
This is the Iron Law of prove-first. There are no exceptions worth discussing.
Wrote code before the test? Delete it. Do not keep it as "reference." Do not "adapt" it. Do not look at it while writing the test. Delete means delete. Start with the test.
The only exceptions are:
- Configuration files (no testable behavior)
- Type definitions (verified by the compiler)
- Static assets (images, fonts, etc.) Everything else gets a test first.
Red Flags
If any of these are true, you have gone off track. Stop and course-correct:
- Test passes on first run -- you are testing existing behavior, not new behavior. Fix the test.
- Cannot explain why the test fails -- you do not understand the requirement. Go back to the task description.
- Multiple test failures you cannot explain -- you are changing too much at once. Revert and take smaller steps.
- Feeling tempted to "just get it working first" -- that is rationalization. The test comes first. Always.
- Code exists before any test file -- you skipped the discipline entirely. Delete the code. Start over.
- Test mirrors the implementation instead of the requirement -- you wrote the test after or while looking at the code. Delete both.
Process Flow
digraph prove_first {
"Understand task requirements" [shape=box];
"Write failing test" [shape=box];
"Run test, confirm RED" [shape=diamond];
"Test fails for right reason?" [shape=diamond];
"Write minimal implementation" [shape=box];
"Run test, confirm GREEN" [shape=diamond];
"Refactor if needed" [shape=box];
"Still GREEN?" [shape=diamond];
"Run full verification" [shape=box];
"Commit" [shape=doublecircle];
"Understand task requirements" -> "Write failing test";
"Write failing test" -> "Run test, confirm RED";
"Run test, confirm RED" -> "Test fails for right reason?" [label="fails"];
"Run test, confirm RED" -> "Write failing test" [label="passes (test is wrong)"];
"Test fails for right reason?" -> "Write minimal implementation" [label="yes"];
"Test fails for right reason?" -> "Write failing test" [label="no, fix test"];
"Write minimal implementation" -> "Run test, confirm GREEN";
"Run test, confirm GREEN" -> "Refactor if needed" [label="passes"];
"Run test, confirm GREEN" -> "Write minimal implementation" [label="fails"];
"Refactor if needed" -> "Still GREEN?";
"Still GREEN?" -> "Run full verification" [label="yes"];
"Still GREEN?" -> "Refactor if needed" [label="no, fix"];
"Run full verification" -> "Commit";
}
Checklist
- Understand the task -- read the task description, file paths, and verification criteria
- Write the test first -- describe the expected behavior in test code. The test encodes what the code SHOULD do, not what it currently does.
- Run the test and confirm it fails (RED) -- a test that passes immediately proves nothing. Verify it fails for the expected reason (feature missing, not syntax error).
- Write the minimal implementation -- just enough code to make the test pass. No extra features, no premature optimization, no "while I'm here" additions.
- Run the test and confirm it passes (GREEN) -- if it fails, fix the implementation, not the test (unless the test is wrong).
- Refactor if needed -- clean up without changing behavior. Run tests after each change to confirm GREEN.
- Run the task's full verification command -- confirm everything works together
- Commit with a clear message describing what was implemented
Anti-Patterns
"Too simple to test" Simple code breaks. Simple tests take 30 seconds to write. The discipline is the point, not the complexity.
"I'll write tests after" Tests written after implementation verify what the code does, not what it should do. They encode bugs as expected behavior. Tests written first encode requirements.
"The test is too hard to write without seeing the implementation" If you cannot describe the expected behavior without seeing the code, you do not understand the requirement. Go back to the task description.
"I'll keep the code I already wrote" Sunk cost. Code written without a test has no proof of correctness. It may be right. It may be subtly wrong in ways you will discover in production. Delete it.
For the full rationalization table (15 common excuses and their rebuttals), see rationalization-table.md. For extended Iron Law guidance including the verification checklist and delete-and-start-over protocol, see iron-law.md.
Evidence Requirements
- Test file exists and was created/modified BEFORE the implementation file (git timestamps are evidence)
- Test suite runs and all tests pass (command output is evidence)
- Verification criteria from the plan are met
Transition
When implementation is complete and tests pass, the task returns to drive-execution for quality review via inspect-work.