Skip to content
article

We let AI write our tests for a quarter. Coverage went up, confidence did not

Honest numbers from a 12-person B2B SaaS: TypeScript monorepo, Vitest, Postgres

September 10, 2026 · 3 min read

Quick context: 12 people, 5 engineers including me, TypeScript monorepo, Postgres, Vitest. In Q2 we had a mandate (from me, honestly 😅) to "use AI to fix our test debt". Coverage was 41% and our investors' tech DD person had made a face.

What we did

  • Every engineer used an AI assistant to generate tests for untested modules, about 30 minutes a day.
  • Rule: tests must pass and get reviewed in the PR like any other code.
  • We tracked line coverage, mutation score (Stryker), and how many bugs a test caught before release.

The numbers after 12 weeks

line coverage             41%  ->  78%
test files                212  ->  604
CI time (tests)          3m40  ->  9m10
mutation score (billing)  52%  ->  55%
flaky tests per week       ~1  ->  ~6
bugs caught in CI        no real change

Coverage almost doubled. Mutation score barely moved. That is the whole story honestly.

Why

When we actually read the generated tests, a pattern: they test what the code does, not what it should do. The assistant reads the implementation and writes assertions that match it. If the code has a bug, the test enshrines the bug. Simplified example:

// implementation (bug: charges 19% VAT on reverse-charge invoices, which should have none)
export function invoiceTotal(inv: Invoice): number { /* ... */ }

// generated test: green, confident, wrong
it('calculates total for reverse charge invoice', () => {
  expect(invoiceTotal(reverseChargeFixture)).toBe(1428)
})

The correct number was 1200. Nobody checked 1428 by hand, because the test was green and there were 40 of them in that PR.

Mutation testing made it obvious: Stryker changes operators and values in your code and checks whether some test fails. Most generated tests survived mutations in the logic they "covered", because they asserted on snapshots or on whatever the function happened to return.

What actually worked

  • Asking for test cases, not tests. "List the edge cases for reverse-charge invoices across EU countries" gave us a good checklist. Then a human wrote the expected values from the spec, not from the code.
  • Boilerplate. Factories, fixtures, mocking our Postgres layer. Real time saved here, maybe 2 hours per engineer per week.
  • Property-based tests with a human-written property. We write the invariant ("total is never negative", "sum of line items equals subtotal"), the AI writes the fast-check boilerplate.

What we changed

We deleted about 140 of the generated test files (mostly snapshot tests of things that change every week, which caused the flakiness). Coverage is 69% now, mutation score on billing is 71%. Lower coverage, much more confidence. CI is back to 5m20.

Tbh the lesson is old: a test is only as good as the person who decided what the right answer is. The AI cannot decide that for your business. Feierabend.

Written by

Deniz Yıldırım

Co-founder & CTO of a 12-person B2B SaaS in Berlin (Kreuzberg, obviously). TypeScript, Postgres, hiring. Yes we are hiring. No we cannot sponsor visas yet 😅

Get the best of #ai_hype_check weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

9

Sign in to join the conversation.

Sign in to comment
Siobhan O'Rourke@siobhan_tests1 rep in AI Hype Check
'The test enshrines the bug' is the sentence. I've been trying to explain this to my director for months. Coverage measures execution, not verification. Mutation score is the honest number, so glad you tracked it.
7 net votes
Deniz Yıldırım@deniz_cto#6 in AI Hype Check
Honestly mutation testing was the only reason we noticed. Without Stryker we would have celebrated 78% and moved on.
6 net votes
Trần Phương Thảo@thao_qa1 rep in AI Hype Check
The flaky snapshot tests from AI are so real. Snapshot of a date, snapshot of a random ID, snapshot of a whole API response with timestamps. Flaky by design.
2 net votes
Wolfgang Engel@dsgvo_wolf2 rep in AI Hype Check
I would like to point out, purely as a German, that a reverse-charge VAT bug in a Billing System is not a small matter. I am glad it was caught before the Finanzamt caught it.
6 net votes
Deniz Yıldırım@deniz_cto#6 in AI Hype Check
Wolf, it was caught in staging, the Finanzamt can stay calm 😅 and yes we will discuss this over beer, not in comments
0 net votes
Dana Kowalski@dana_ml#1 in AI Hype Check
Asking for test cases rather than tests is the same move as asking a model for candidate answers and having a human set the label. The judgement stays with the person accountable for it. Good write-up, and thank you for including the mutation score.
5 net votes
Ryan Goh@ai_maxi_ryan#3 in AI Hype Check
so use a better model that reads the spec not the code? skill issue
4 net votes
Deniz Yıldırım@deniz_cto#6 in AI Hype Check
We are a 12-person startup Ryan, our 'spec' is half in Notion and half in our head of finance's brain 😅 that's kind of the point
2 net votes
Luis Treviño@luis_qa_mty2 rep in AI Hype Check
como QA esto me dio paz interior. lo comparto con mi equipo, siempre nos piden "subir el coverage" como si esa fuera la meta
4 net votes