$npx -y skills add evanklem/evanflow --skill evanflow-tddVertical-slice TDD for any production code. One test → one impl → repeat. Tests verify behavior through public interfaces, not internals. Use when implementing any feature, bugfix, or behavior change.
| 1 | # EvanFlow: TDD |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | ## Vocabulary |
| 7 | |
| 8 | See `evanflow` meta-skill. Key terms: **vertical slice**, **behavior through public interface**, **deep module**. |
| 9 | |
| 10 | ## Core Principle |
| 11 | |
| 12 | Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't break unless behavior changes. |
| 13 | |
| 14 | **Good test:** "user can perform action X within their weekly rate limit" — describes capability. |
| 15 | |
| 16 | **Bad test:** "calls `createX()` with status `'QUEUED'` then queues a job" — describes mechanics. Renames break it. |
| 17 | |
| 18 | ## Anti-Pattern: Horizontal Slices |
| 19 | |
| 20 | **DO NOT** write all tests first then all implementation. That produces tests of *imagined* behavior, not *actual* behavior. They become insensitive to real changes. |
| 21 | |
| 22 | **DO** vertical slices: one test → one implementation → repeat. Each test responds to what you learned from the previous cycle. |
| 23 | |
| 24 | ## When to Use |
| 25 | |
| 26 | - Any production code change (new feature, bug fix, behavior change, refactor with behavior implications) |
| 27 | - All new code in your backend's routers and services |
| 28 | - All new code in your frontend that has testable logic (not pure-presentation components) |
| 29 | |
| 30 | ## When to Skip (with explicit user approval) |
| 31 | |
| 32 | - Throwaway prototypes |
| 33 | - Generated code (e.g., `database.types.ts`) |
| 34 | - Configuration files |
| 35 | - Pure-presentation React components with no logic |
| 36 | |
| 37 | ## The Flow |
| 38 | |
| 39 | ### 1. Embedded Grill — "What to Test" |
| 40 | |
| 41 | Before writing any test, confirm with the user: |
| 42 | |
| 43 | - "Which behaviors matter most? We can't test everything." |
| 44 | - "What's the public interface — what will callers actually use?" |
| 45 | - "Are there opportunities to make this a deep module (small interface, complex internals)?" |
| 46 | - "Where do tests need to integrate with real services (DB, payment provider, email provider) vs. where can we test in isolation?" |
| 47 | |
| 48 | **Anti-tailoring check (vertical slicing's biggest risk):** before each new test, ask: |
| 49 | |
| 50 | - "Am I pinning *behavior the spec/contract names*, or am I pinning *the impl I've already imagined*?" |
| 51 | - "Could I write this next test knowing only the public contract, before reading any of the impl I just wrote?" |
| 52 | - "If a different impl satisfied the same contract, would this test still pass?" |
| 53 | |
| 54 | If the test only makes sense given your specific impl, it's an internals test wearing a behavior costume. Rewrite it against the contract, or drop it. |
| 55 | |
| 56 | **Default to integration-style tests against real services** (real DB, real queue, real cache) where feasible. Mocked dependencies frequently mask divergence between test and production behavior. Document any project-specific exception in your CLAUDE.md. |
| 57 | |
| 58 | ### 2. Tracer Bullet |
| 59 | |
| 60 | Write ONE test for ONE behavior end-to-end. Prove the path works. |
| 61 | |
| 62 | ``` |
| 63 | RED: Write test → run → confirm it fails for the RIGHT reason |
| 64 | GREEN: Write minimal code → run → confirm it passes |
| 65 | REFACTOR: Clean the impl + the test you just wrote, while it's fresh and green |
| 66 | ``` |
| 67 | |
| 68 | The REFACTOR step is non-optional and **per-cycle** — it happens with the test you just wrote as your safety net, not after all tests are done. Refactoring cold code days later is a different (weaker) activity; that lives in `evanflow-iterate`. |
| 69 | |
| 70 | ### 3. Incremental Loop |
| 71 | |
| 72 | For each remaining behavior, repeat the full RED-GREEN-REFACTOR cycle: |
| 73 | |
| 74 | ``` |
| 75 | RED: Write next test → fails for the right reason |
| 76 | GREEN: Minimal code to pass → passes |
| 77 | REFACTOR: Clean before moving on (see checklist below) |
| 78 | ``` |
| 79 | |
| 80 | Rules: |
| 81 | - One test at a time |
| 82 | - Only enough code to pass the current test |
| 83 | - Don't anticipate future tests |
| 84 | - Tests focus on observable behavior, not internals |
| 85 | - **Never skip REFACTOR.** "I'll clean it up later" is how dead code, duplication, and shallow modules accumulate. |
| 86 | |
| 87 | ### 4. Per-Cycle Refactor Checklist |
| 88 | |
| 89 | After each GREEN, before writing the next failing test, scan the just-touched code: |
| 90 | |
| 91 | - **Duplication** — extract if used twice with the same intent (not just structurally similar) |
| 92 | - **Naming** — does the new name match what the code does? Rename now, while the test pins behavior |
| 93 | - **Deletion test** — does the new module/function earn its existence, or did GREEN add bloat? |
| 94 | - **Deep-module check** — small interface hiding the complexity, or shallow wrapper leaking it? |
| 95 | - **Test cleanliness** — does the test still describe behavior crisply? Names, setup, assertion all clear? |
| 96 | |
| 97 | Run tests after each refactor step. **Never refactor while RED** — get to GREEN first. |
| 98 | |
| 99 | If a refactor would change behavior, stop: that's a new test, not a refactor. |
| 100 | |
| 101 | ### 5. Macro Refactor (deferred to `evanflow-iterate`) |
| 102 | |
| 103 | Cross-cutting refactors that span the whole feature (extracting a shared module across multiple cycles, pulling out a deeper abstraction, restructuring the file layout) belong in `evanflow-iterate`'s self-review pass — *after* all per-cycle refactors are done. Don't conflate |