Why LLMs Broke Testing
For fifty years, testing software meant running the code and checking that the output matched what you expected. Put a language model in the loop and that check stops telling you anything useful. This chapter shows exactly how it fails, because everything else in the book is built on the replacement.
Quill celebrates a regression
Quill, a small writing-assistant startup, scored its summarizer with a quality model and got 0.80 on a set of 20 documents. An engineer rewrote the prompt, ran the same 20 documents, and got 0.85. The team called it a five-point win and shipped it.
A few months later someone reran both prompts on 500 documents. The new prompt was slightly worse. With only 20 cases, the margin of error was about 18 points in either direction, so the 5-point gap was just noise. After that, Quill moved to larger fixed eval sets and started reporting a range with every score.
The test that stops working
Say you build a summarizer and write the test you've written a thousand times:
def test_summary():
out = summarize("The cat sat on the mat in the warm afternoon sun.")
assert out == "A cat rested on a mat in the sun." # one blessed answer
Now run it five times:
| Run | Output | Good? | == expected |
|---|---|---|---|
| 1 | "A cat rested on a mat in the afternoon sun." | Yes | FAIL |
| 2 | "A cat sat on a mat, warmed by the sun." | Yes | FAIL |
| 3 | "In the afternoon sun, a cat sat on a mat." | Yes | FAIL |
| 4 | "A cat lounged on a mat in warm sunlight." | Yes | FAIL |
| 5 | "A cat rested on a mat in the sun." | Yes | PASS |
All five summaries are fine, and the test passes once, by luck. Exact-match accuracy is 20%. What the test measures is whether the model happened to pick your wording. There are thousands of acceptable summaries of any paragraph, so you have to stop asking "is this the right answer?" and start asking "how good are the answers, on average, across many inputs?"
Your score is noisy too
Suppose you switch to a quality scorer that gives each case a number between 0 and 1. Twenty cases come back at 0.80. You change the prompt, run twenty cases again and get 0.85. It's tempting to call that progress, but a score over n cases has a margin of error. The rough 95% interval for a pass rate p is 1.96 × √(p(1−p)/n), and here's what that looks like at a few sizes:
| n | Score | 95% interval | What you can fairly say |
|---|---|---|---|
| 20 | 0.80 | ±0.18 (0.62 to 0.98) | Very little |
| 200 | 0.80 | ±0.055 (0.74 to 0.85) | "Around 80%" |
| 2000 | 0.80 | ±0.018 (0.78 to 0.82) | "80%, and we're confident" |
At n=20, a 5-point gap sits well inside the noise. Engineers who come from deterministic testing tend to trip here, because they're used to thinking in individual test cases. Evaluation asks you to think in measurements, and a measurement always comes with a sample size.
Traditional QA asks whether an output is correct. AI evaluation asks how good the system is on average across the cases you care about, how sure you are, and whether it's getting better. Every technique in this book follows from that shift.
Watch a confidence interval shrink
This is a pure simulation. Pretend a system's true quality is 80%, "evaluate" it at three sample sizes and look at the margin of error each time. Run it first. Then ask yourself whether a 0.85 and a 0.80 at n=20 could come from the same system. Finally, set TRUE_QUALITY = 0.95, run it again and work out why the intervals got tighter.
Lab code · runs free
import random
from math import sqrt
TRUE_QUALITY = 0.80 # the system's real pass-rate (unknown in real life)
def simulated_eval(n):
passes = sum(random.random() < TRUE_QUALITY for _ in range(n))
p = passes / n
ci = 1.96 * sqrt(p * (1 - p) / n) # 95% confidence interval
return p, ci
random.seed(0)
for n in (20, 200, 2000):
p, ci = simulated_eval(n)
print(f"n={n:>4}: score={p:.2f} 95% CI = +/- {ci:.3f} "
f"({p-ci:.2f} to {p+ci:.2f})")
Output with seed(0):
n= 20: score=0.85 95% CI = +/- 0.156 (0.69 to 1.01)
n= 200: score=0.80 95% CI = +/- 0.055 (0.74 to 0.85)
n=2000: score=0.81 95% CI = +/- 0.017 (0.79 to 0.82)
At n=20 the interval runs from 0.69 to 1.01, which covers almost any difference you'd care about. A 0.80 and a 0.85 can easily be the same system on two different days. Notice the pattern as n grows: the interval shrinks with the square root of the sample size, so to cut it in half you need four times as many cases. That rule decides how big every eval set in this book needs to be. On the last question, intervals tighten at 0.95 because p(1−p) is largest at 0.5 and gets smaller as p moves toward 0 or 1.
Meridian is a Series B fintech building Remi, an assistant that answers billing questions for customers. Remi's first test was an exact-match check against a reference reply. Four of five correct answers failed because Remi phrased them differently. The team threw out the check and agreed to measure Remi over a large set of real questions instead. Building that set is where Chapter 3 picks up.
If the interval formula is new to you, don't worry about memorizing it. What you need is the intuition behind it: small samples swing a lot, and the swing shrinks slowly as you add data. You'll use that intuition in almost every interview question about evaluation, usually without anyone mentioning statistics.
A good first habit is to never write a score without its sample size next to it. "0.82 on 300 cases" reads as someone who knows what they're doing. "0.82" on its own reads as a guess. Interviewers notice the difference right away.
The expensive mistake at this stage is trusting a demo. A demo is a handful of inputs the team already knows work, run on a system that answers differently each time. It tells you the happy path exists. It says nothing about what happens across the ten thousand questions your customers will ask.
When an engineer says the new assistant is "90% accurate" and wants to launch on Friday, ask three things. How many cases is that, and were they taken from real traffic? Does it hold for every customer segment, or is a strong average hiding a weak one? And after launch, how would we find out if it got worse? If there are no answers yet, the 90% is a feeling, and the launch can wait until it's a measurement.
Quiz · Chapter 1
- You score 20 cases at 85% this week; a different 20 scored 80% last week. Did quality improve?
- A summarizer that writes good summaries scores almost 0% on exact match. Why?
- To cut an eval's confidence interval in half, you should roughly:
- Which report of accuracy is the most honest?
- Quill's 5-point "win" turned out to be: