← All recipes
  • testing
  • prompting

Ask for tests first: 3 runs with, 3 without

We asked Claude Code for a bill-splitting function six times. Asked plainly, it shipped a rounding bug in 1 of 3 runs. Told to write failing tests first, 3 of 3 passed our hidden edge-case test, for about 2 cents more per run.

Cost per run Each bar is the average cost of the runs, starting at $0.

See every run, check and diff →

“Write the tests first” is common advice for working with Claude Code. We wanted to see what one extra sentence in the prompt actually changes.

The setup

The same small cart module we’ve used before: subtotal(), total() with tax, and a roundCents() helper. The task has a catch you only notice at the edges. Split a bill and the pennies don’t always divide evenly.

We ran this prompt 3 times as written:

Add a splitBill(items, people) function to the cart module that splits the cart total (including tax) between people and returns an array of each person’s share.

And 3 times with one sentence added:

Before you implement it, write tests for it, including edge cases, and run them to see them fail. Then implement it until they pass.

Afterwards, a hidden test Claude never saw checked the edges: the shares must add up to total() exactly, differ by at most one cent, be whole cents, handle an empty cart and one person, and reject 0, negative or fractional head counts.

What happened

Just ask Tests first
Hidden edge-case test passed 2 of 3 3 of 3
npm run check passes 3 of 3 3 of 3
Added its own test for splitBill() 3 of 3 3 of 3
Generated files untouched 3 of 3 3 of 3
Ran tests after its last edit 3 of 3 1 of 3
Average cost per run $0.074 $0.097
Average turns 16.0 14.7
Average time per run 23.5s 34.7s

Claude Code 2.1.302, Sonnet 5.5, 3 runs per column. Every run, check and diff: run data.

What we learned

Claude writes tests either way. Even without being asked, all three plain runs added tests and passed npm run check. The difference was in what those tests looked for.

The failure was a quiet one. In one plain run, Claude split the total evenly and gave the last person whatever was left. Its own tests passed. But on $64.77 split seven ways, one person ends up paying 2 cents more than everyone else, which our hidden test rejects. With tests first, every run spread the leftover pennies one cent at a time.

It costs a little more. Tests first averaged about 2 cents (roughly 30%) more per run and took around 11 seconds longer, with a similar number of turns. That’s cheap insurance for code that handles money.

One row looks worse than it is. “Ran tests after its last edit” dropped to 1 of 3 with tests first. In both misses, the last edit was a CHANGES.md line written after the tests had already passed, not a code change. Still, if you want a final check every time, say so.

Three runs per column is a small sample. One bug in three plain runs is a signal, not a rate.

The recipe

  1. For anything with edge cases (money, dates, parsing), add one line to the prompt: write tests first, including edge cases, and watch them fail.
  2. Name the edges you care about if you know them, such as rounding, empty input or invalid arguments.
  3. Read the tests before the implementation. They show you what Claude thinks “done” means.
  4. If you want the full suite run at the very end, ask for it explicitly.