L6 · AI as your instrument
L6Core3 min read

Spec-first automation

When a machine writes the test, the specification becomes the artefact that matters. Learning to write one precisely enough to generate from — and to review against — is the highest-leverage writing skill in modern QA.

Spec-first automation means writing the specification precisely enough that the test code can be generated from it, and treating that specification as the thing you maintain.

The spec, or brief, states what must be true. The generated code is how it is checked this month, with this framework, on this stack. When either changes, you regenerate rather than rewrite.

This inverts an old habit. For twenty years the test code was the asset and the documentation rotted. When generating code is cheap and precise writing is not, the value moves to the writing.

The terms you will hear

  • Spec, or brief. The written statement of what must be true and how it is checked.
  • Generated code. The implementation produced from that spec. Disposable by design.
  • Source of truth. Whichever artefact you change first. In this approach, the spec.
  • Round trip. Spec to code, then code reviewed back against the spec.
  • Drift. Code and spec disagreeing. Preventable when one of them is clearly authoritative.

Why it matters

Because the alternative is a suite nobody can safely change.

For example, a team inherits 400 generated tests with no spec. Each one asserts something, nobody knows whether the assertion was intended or invented, and every change is a guess. Regenerating is impossible, because there is nothing to regenerate from. That suite is now a liability that looks like an asset.

A team with 40 specs and generated code from them is in a completely different position. Framework change, model change or a new pattern all become a regeneration rather than a rewrite.

How to write a brief you can generate from

  1. State the behaviour, not the steps. "A gift card is consumed before the payment method is charged" rather than a click-by-click walkthrough.
  2. Name the data explicitly. Real values, including the awkward ones, as in a case somebody else can run. Invented data hides boundaries.
  3. Name the oracle. Where the expected value comes from, as in test oracles. Without it, the generator asserts current behaviour and locks in whatever the bug is.
  4. State the boundaries. The limits, the empty case, the failure case. These are the three things generation skips.
  5. Say what must not happen. Negative conditions are as testable as positive ones and far more often forgotten.
  6. Keep it in the repository. Next to the tests, reviewed in the same pull request, changed first.
  7. Regenerate rather than patch. When behaviour changes, edit the spec and generate again. Patching generated code quietly makes the spec a lie.

A worked brief

For example, here is a brief for one behaviour, and what it produced.

spec-partial-redemption.md
SPEC  partial gift-card redemption            owner: Priya   v3, 2026-09-01
STORY WB-1802, criterion 2 and 4

BEHAVIOUR
  Where a gift card balance is less than the order total, the full
  balance is applied, the card balance becomes 0.00, and the remainder
  is charged to the payment method.

ORACLE
  The 14-day and rounding rules come from the finance policy document
  (policy/refunds-2026.md, section 4). Any arithmetic in this spec is
  checkable against that document, not against current behaviour.

DATA (use exactly these)
  card GIFT-7QK4-2210 balance 25.00
  basket 32.49  (Piranesi 9.99, Mrs Dalloway 22.50)
  delivery 3.50, order total 35.99
  card 4242 4242 4242 4242, 12/29, CVC 123

MUST BE TRUE
  1  amount to pay after applying the card = 10.99
  2  card balance after applying = 0.00
  3  amount charged to the payment method = 10.99 exactly
  4  order confirmation lists two payment lines: 25.00 and 10.99
  5  gift card is consumed before the payment method is charged

MUST NOT HAPPEN
  6  the payment method is never charged more than 10.99
  7  no penny is lost. 25.00 + 10.99 must equal 35.99
  8  the card cannot be applied twice, including from a second tab

BOUNDARIES TO COVER
  totals ending .49 and .99 (rounding risk)
  balance exactly equal to the total (card pays all, nothing charged)
  balance greater than the total (remainder stays on the card)

GENERATED FROM THIS SPEC
  api/partial-redemption.spec.ts   9 cases, all 8 rules asserted
  reviewed: rules 7 and 8 were asserted weakly on the first pass and
  regenerated with the wording made explicit. Rule 7 is the one that
  caught the 1p defect.

Rule 7 is the point. "No penny is lost" written in the brief produced an assertion that found a real money defect. Nothing in the interface would have suggested it, and no generator would have invented it.

How to show you know it

  • A brief that produced code. With the generated file named beside it.
  • A regeneration. "The framework changed, so I edited the spec and generated again." That is the whole argument in one sentence.
  • A must-not-happen rule that caught something. The most persuasive artefact on this page.
  • A deletion test. Being able to recreate a test file from your written material proves the spec is real.

Questions

Is this the same as behaviour-driven development?

It shares the instinct that the written behaviour matters most. The difference is what the writing is for. Gherkin was written for a parser and a shared vocabulary. A brief here is written to be precise enough to generate from and to review against.

Do I keep the generated code in version control?

Yes, so reviews and history work normally. What changes is which artefact you edit first when behaviour moves.

What if the generated code is better than my spec?

Then your spec is missing something the generator inferred. Add it. That gap is worth finding, because it is a behaviour nobody had written down.

Does this work for exploratory testing too?

Not directly, and it is not meant to. Exploring is where you discover what should be in a spec, which is why session charters feed this rather than compete with it.