~/articles/why-llm-eval-is-hard
Beginner

Why Eval Is Hard: The Measurement Problem in LLM Applications

Standard software metrics miss quality regressions in LLM apps. Learn why subjectivity, Goodhart traps, and silent failures demand a dedicated eval discipline.

16 min readupdated 2026-07-02Ironclad Academy
// DEPTH
the full breakdown — requirements, capacity, evolution, trade-offs

Your RAG demo answered every question in the demo. Two weeks after launch, support tickets say the assistant confidently cites an outdated policy. Your error rate is 0.0%. Your p99 latency is 180ms. Your uptime is 99.98%. Every SLO is green. The product is broken.

This is the measurement problem in LLM applications, and it's why eval is genuinely hard — not because the tooling is immature (though it has room to grow), but because the entire mental model engineers use to assess software quality fails on probabilistic, subjective outputs. Fixing it requires building a discipline from scratch.

The silence of a quality regression

In traditional software, a regression usually crashes something or returns a wrong status code. Either way, it shows up. In LLM applications, quality degrades silently. The model returns a 200 with well-formed JSON. The answer is confident. It's just wrong in a way that requires domain knowledge to detect.

Here are three scenarios where your dashboards stay green while quality falls apart:

Scenario 1: the model provider update. You're calling the un-dated gpt-4o alias via OpenAI's API. OpenAI repoints the alias to a new snapshot — documented behavior, not a bug. And even if you pin a dated snapshot like gpt-4o-2024-11-20, pinning only defers the problem: snapshots get deprecated on the provider's timeline, and the forced migration lands the same behavioral shift on a date you don't choose. Suppose the new checkpoint is more cautious about medical claims. Your health information assistant starts deflecting questions it previously answered. No errors, no latency change, just a behavioral shift your monitoring never sees.

Scenario 2: the prompt change. A junior engineer adds a sentence to the system prompt to fix a tone issue a customer complained about. The new sentence causes the model to be more verbose. Your summarization feature now produces summaries that are 40% longer. Users stop using them. Nothing in your metrics registers the change.

Scenario 3: input distribution shift. Your product was built and tested on English queries from US users. A partnership deal brings in a cohort of non-native English speakers. Their phrasing doesn't match your few-shot examples. Output quality drops 15 points by any human measure. New user cohort, same code, same model, silent failure.

None of these show up in error rates, latency histograms, or token counts. You need a different kind of measurement.

Why asserting on LLM outputs is hard

The root difficulty is that LLM outputs are high-dimensional and subjective. Consider asking your customer support bot "How do I return a product?" There are a hundred valid answers — different phrasings, different levels of detail, different tones — and there are hundreds of subtly wrong answers that all look like correct text to a parser.

String matching fails immediately. You cannot assert output == expected_answer because any two valid phrasings will fail the equality check. Even assert expected_phrase in output fails the moment the model paraphrases correctly.

Subjectivity is real, not a cop-out. Human annotators asked to rate the same outputs agree with each other only about 81% of the time on quality judgments. This isn't because the annotators are bad — it's because quality is genuinely multi-dimensional (accuracy, clarity, tone, completeness, brevity) and reasonable people weight those dimensions differently. Your eval process will not solve subjectivity; it will manage it by being explicit about which dimensions you're measuring and how you're weighting them.

LLM outputs are non-deterministic. The same prompt, same model, same temperature will produce slightly different outputs on repeated calls. A test that passes once is not guaranteed to pass on the next run. This forces a shift from pass/fail assertions to statistical comparisons over distributions — which means you need more than one or two examples to draw any conclusion.

The five evaluation stages

flowchart TD
    U["Dev unit tests\n(seconds, cheap)"] --> A["Pre-release adversarial\n(targeted edge cases)"]
    A --> B["CI regression\n(golden dataset on every PR)"]
    B --> C["Production sampling\n(1% of live traffic, async)"]
    C --> D["Runtime guardrails\n(online, blocking)"]

    U -.->|"catches"| U1["Format errors\nhard constraints\nschema violations"]
    B -.->|"catches"| B1["Prompt regressions\nmodel behavior changes"]
    C -.->|"catches"| C1["Distribution shifts\nprovider updates\nnew user patterns"]
    D -.->|"catches"| D1["Harmful outputs\npolicy violations"]

    style U fill:#0e7490,color:#fff
    style A fill:#15803d,color:#fff
    style B fill:#00e5ff,color:#0a0a0f
    style C fill:#a855f7,color:#fff
    style D fill:#c2410c,color:#fff

Stage 1 — Dev unit tests. These run in seconds and check the things you can assert deterministically: output parses as valid JSON, required fields are present, a phone number field actually contains digits, the response length is within bounds. They also check hard behavioral constraints: "never mention a competitor by name" is verifiable. This stage doesn't measure quality — it catches the obvious failures before anything else runs.

Stage 2 — Pre-release adversarial evaluation. Before merging a prompt change or a model upgrade, run targeted probes at the known hard cases: jailbreak attempts relevant to your use case, edge cases from previous production failures, inputs from underrepresented user patterns. This is a human-curated adversarial suite, not a general benchmark. It catches the failures that are likely to surface in your specific application domain.

Stage 3 — CI regression on the golden dataset. This is the core of your eval discipline. A curated set of 50 to 200 input/output pairs (or input/rubric pairs if you don't have reference outputs) runs on every pull request. An automated judge scores each output. The PR blocks if the pass rate drops below threshold. This is the layer that catches regressions you introduce — prompt changes, retrieval changes, model version bumps. The golden dataset article covers how to build and maintain this corpus.

Stage 4 — Production sampling. Score 1% of live traffic asynchronously with an LLM judge, or use user feedback signals where available. This is the layer that catches what happens to you — model provider silent updates, new user behavior patterns, queries from cohorts you didn't test. The offline vs online evaluation article goes deep on the mechanics of each layer and when each fails.

Stage 5 — Runtime guardrails. Online classifiers, rule-based filters, and output validators that run synchronously before responses reach users. These are not quality metrics — they're safety gates. Slow, but the only layer that actually blocks a harmful response before delivery.

{ "type": "eval-pipeline", "title": "The five-stage eval safety net" }

The Goodhart trap

The classic pattern, seen repeatedly in content moderation systems: a classifier does well on every metric while systematically failing on a class of content nobody ever explicitly labeled. The metric is fine. The job is not getting done. The team has been optimizing for the score.

Goodhart's Law: when a measure becomes a target, it ceases to be a good measure.

In LLM eval, this looks like: you add an LLM judge that scores outputs 1–10 on helpfulness. The judge becomes a PR gate. Engineers optimize prompts to score well on the judge. The judge has a verbosity preference (a known bias). Prompts get tuned to produce longer responses. Helpfulness scores go up. User satisfaction goes down.

The fix is not to stop using automated eval — it's to treat scores as proxies you audit regularly. Two concrete defenses:

  1. Keep a held-out human review process. Every two weeks, manually review a random sample of 20 production outputs. Do not let the automated eval ever see these examples. If the human scores diverge from your automated judge, investigate.

  2. Measure user outcomes alongside eval scores. Task completion rate, follow-up question rate, escalation rate, thumbs-down ratio — these are noisier but harder to Goodhart. If your eval score climbs while your follow-up question rate also climbs, your "helpful" responses aren't actually resolving user needs.

Why benchmark scores don't transfer

MMLU, HumanEval, MATH, GPQA — these benchmarks measure a model's general capability on standardized tasks. They are inputs to model selection decisions, not substitutes for application-level eval.

The gap is fundamental. A benchmark tests whether a model knows the French Revolution's dates. Your customer support application needs to know whether the model correctly interprets a customer's ambiguous return request for a product category with unusual policies. These are completely different tasks, and nothing about the benchmark score predicts success on yours.

In practice: a smaller model fine-tuned on your domain often outperforms a higher-benchmark-scoring frontier model on your specific task. A model that tops the coding benchmark may underperform on code extraction from messy PDFs because your task involves noisy inputs the benchmark never covered.

The model landscape article covers how to read benchmarks without getting played. The key eval takeaway: run your golden dataset on any candidate model before making a selection decision, because benchmark rankings routinely invert on specialized tasks.

Subjectivity by the numbers

Because the subjectivity problem comes up constantly when justifying eval investment to stakeholders, it's worth grounding it concretely.

Human inter-annotator agreement on chat-response quality: ~81%
(Source: Zheng et al. 2023, "Judging LLM-as-a-Judge with MT-Bench and
Chatbot Arena" — one well-studied setting; agreement varies widely by task)

LLM-as-judge agreement with humans in the same study: ~85%
(GPT-4 judging chat responses; treat as an anchor, not a universal constant)

Uncalibrated LLM judge (no calibration against domain experts): highly variable,
often < 0.5 Pearson correlation — measuring noise

LLM judge on binary "pass/fail" decisions: more reliable than 1-10 Likert
LLM judge on fine-grained Likert (e.g., 1-10): variance often exceeds the signal

Recommended calibration target: Pearson > 0.7 on 30-50 expert-labeled examples

The implication: you cannot skip calibration and trust your automated judge. A judge that correlates at 0.4 with your domain experts is telling you nothing useful about quality. But in the best-known study of chat-response quality (Zheng et al. 2023), a GPT-4 judge reached ~85% agreement with humans — better than the ~81% agreement between two humans in the same study. These numbers are task-dependent anchors, not constants, but the ordering — a validated judge rivals a second annotator — has held up. This makes LLM-as-judge the production default at scale; the LLM-as-judge article covers how to build and calibrate one.

The eval metric choice problem

Which dimensions of quality should you actually measure? The answer is specific to your application, but there are three common patterns:

Accuracy / correctness. Does the output contain the right information? For factual domains, this is often the primary dimension. For RAG applications, it decomposes further: faithfulness (does the answer contradict the retrieved context?) vs. context quality (did the retriever surface the right chunks?). Collapsing these into a single score hides which component is failing — see RAG evaluation with Ragas for how to keep them separate.

Task completion. For agents and multi-turn conversations, does the interaction achieve the user's goal? This often requires end-to-end evaluation, not per-turn evaluation. But end-to-end eval on agents is its own hazard: a correct final output can mask broken intermediate reasoning that will compound catastrophically in longer chains. The agent evaluation article covers trajectory-level eval.

Policy adherence. Does the output comply with your stated constraints — tone, format, competitive mentions, safety policies? These are often the most automatable dimensions, closest to deterministic assertions.

The failure mode is using a single composite score that averages all three. A low composite score is unactionable — you cannot tell whether to fix the retriever, the prompt, or the output filter. Keep metrics separate and localize failures before trying to fix them.

What breaks: the failure modes of eval systems

flowchart TD
    E["Eval system"] --> B1["Narcissistic bias\n(GPT-4 self-favors ~10%,\nClaude ~25%)"]
    E --> B2["Verbosity bias\n(longer = better\nin uncalibrated judges)"]
    E --> B3["Position bias\n(first answer wins\nif no randomization)"]
    E --> B4["Goodhart regression\n(score optimized\nquality ignored)"]
    E --> B5["Static dataset drift\n(golden set never updated,\nnew user patterns uncovered)"]
    E --> B6["Coverage gaps\n(offline eval misses\nprovider updates)"]

    style E fill:#00e5ff,color:#0a0a0f
    style B1 fill:#c2410c,color:#fff
    style B2 fill:#c2410c,color:#fff
    style B3 fill:#c2410c,color:#fff
    style B4 fill:#c2410c,color:#fff
    style B5 fill:#c2410c,color:#fff
    style B6 fill:#c2410c,color:#fff

Narcissistic bias. GPT-4 self-prefers its own outputs by about 10%; Claude v1 narcissistic bias was measured at around 25% in cross-model evaluation studies — meaning if you use Claude to judge outputs from a Claude-powered application, scores will be systematically inflated. Always use a judge from a different model family than your application model.

Verbosity bias. Uncalibrated LLM judges consistently rate longer responses as higher quality, even when the length adds no value. This is the most common reason why teams see eval scores climb while users complain about bloated responses.

Position bias. In pairwise comparison setups (which is better, A or B?), LLM judges show strong preference for whichever answer appears first in the prompt. Always randomize order and average over both orderings, or use a chain-of-thought rubric that forces evaluation on criteria rather than on overall impression.

Static dataset drift. Your 50-example golden dataset was perfect on launch day. Nine months later, you have new product features, new user segments, and new failure modes that the original dataset never covers. New patterns become invisible blind spots. The feedback loop — surfacing failures from production sampling back into the golden dataset — is what keeps coverage current.

Coverage gaps between offline and online eval. Offline CI evals catch regressions you introduce through code and prompt changes. They cannot catch a model provider silent update that shifts behavior, or a new user cohort that queries your product in unexpected ways. Production sampling is the only layer that sees the actual distribution of inputs your product receives.

Specification-driven evaluation

The most mature eval practice emerging in 2026 is specification-driven evaluation: before writing a single eval prompt, you write a formal product specification — what the system should do, what it should never do, what quality looks like on each task type — and derive your evaluation criteria from that specification.

This sounds bureaucratic, but it has a concrete payoff: when your eval criteria are grounded in your product spec, they stay stable as the model or implementation changes, and your team has a shared vocabulary for what "good" means before they disagree about it in a PR review. It also creates a direct audit trail — if an eval criterion doesn't map back to a spec requirement, you can question whether you're measuring something that matters.

Contrast this with the common anti-pattern: a team adds an eval criterion because a specific failure annoyed a specific engineer, without ever asking whether that criterion generalizes, conflicts with other criteria, or matches what the product is supposed to do.

The cost argument

A persistent objection to building proper eval infrastructure is cost — both engineering time and inference cost. The numbers argue against the objection.

Monthly eval infrastructure cost estimate (medium-traffic LLM app):

Golden dataset eval (CI, every PR):
  Assume 20 PRs/month, 200 examples each
  200 × 20 = 4,000 judge calls/month
  ~800 tokens each × GPT-4o-mini pricing (~$0.15/1M input tokens)
  = 3.2M tokens ≈ $0.48/month

Production sampling (async, 1% of 10k daily requests):
  100 calls/day × 30 days = 3,000 judge calls/month
  3,000 × 800 tokens = 2.4M tokens ≈ $0.36/month

Total automated eval: ~$1/month

Engineering to build initial eval infrastructure: 3-5 days
Engineering to maintain ongoing: ~2 hours/month

Cost of a single quality regression shipping to production
and requiring a rollback: 1-3 days of engineering + user trust damage

The math is not subtle. The engineering time to build eval infrastructure is recovered the first time it catches a regression before production.

The practical starting point

If you have an LLM application with no eval infrastructure today, the right sequence is:

  1. Write down what your application is supposed to do, what it should never do, and what "good" looks like for your three most common task types. This is your spec.

  2. Curate 50 examples from your existing usage (or hand-craft them if you're pre-launch) that cover the spec requirements and the obvious failure modes. These are your golden dataset.

  3. Pick a judge from a different model family than your application and write a rubric — not a holistic scoring prompt, but a list of atomic criteria derived from your spec. Validate it against 30-50 human-labeled examples (20 is a bare-minimum first pass — grow it); if Pearson correlation is below 0.7, revise the rubric.

  4. Wire the 50-example dataset into your CI pipeline as a quality gate. Block PRs that drop below threshold.

    A note on flakiness, because step 4 is where non-determinism bites. Run the judge at temperature 0, and score each example over three generations rather than one — average the scores, or require a pass on at least 2 of 3. Set the gate as a tolerance band below your baseline (block at, say, 5 points under the trailing average) rather than an exact score, because a 50-example suite has enough sampling noise that an unchanged PR can legitimately land a point or two lower. When a run misses the threshold by a hair, retry once; if it fails twice, treat it as real and investigate instead of re-rolling until green.

  5. Add a 1%-sampled production scoring job that runs nightly and alerts on trend degradation.

That's the minimum viable eval system. It takes a week to build and costs under two dollars a month to run. Everything else — expanded datasets, adversarial probes, multi-dimensional metrics, trajectory-level agent eval — is built on top of this foundation.

The sibling articles in this section go deep on each component: offline vs online evaluation, LLM-as-judge calibration, building golden datasets, and for more complex systems, RAG evaluation with Ragas and agent trajectory evaluation. The observability tooling that ties it all together — LangSmith, Langfuse, Braintrust, Arize — is covered in the tooling comparison article.

When eval investment level matches product risk

Not every LLM application needs the same eval depth. Here's the honest decision framework:

Application typeMinimum viable evalWhen to go deeper
Internal tool, low stakes20-example golden set, manual spot-checksWhen a regression causes wasted work
Customer-facing feature, medium stakes100-example CI gate + 1% production samplingAt launch; before every model upgrade
High-stakes domain (medical, legal, financial)Full 5-stage pipeline, human review loop, specification-driven criteriaBefore launch; continuous
Agents making real-world actionsTrajectory-level eval mandatory, not optionalFrom the start; end-to-end success is not enough
RAG over regulated contentSeparate retrieval and generation metricsFailure localization is a compliance requirement

The pattern: eval investment should track the blast radius of a quality regression. An internal tool that annoys an employee is a different risk profile than a medical information assistant that gives a patient wrong dosage information. Neither justifies no eval infrastructure, but they justify very different levels.

The field has finally reached the point where the tooling works and the cost is trivial, so "we don't have time to build eval" is not a credible engineering position. It's a risk choice, made explicitly or by default.

// FAQ

Frequently asked questions

Why is evaluating LLM applications harder than evaluating traditional software?

Traditional software has deterministic outputs you can assert against. LLM outputs resist assertions — the same question can have dozens of valid answers and dozens of subtly wrong ones that all parse as valid text. Standard metrics like error rate and latency stay green while answer quality silently degrades after a model provider update or a prompt change. You need a dedicated eval layer to catch what uptime dashboards cannot see.

What is the Goodhart trap in LLM evaluation?

Goodhart's Law states that when a measure becomes a target, it ceases to be a good measure. In LLM eval, this means teams that optimize directly for their eval score — tuning prompts to pass the test rather than to do the task — routinely improve the score while degrading real user experience. The fix is to treat eval scores as a proxy that you audit regularly, not as the goal itself, and to keep a held-out human review process that the automated eval never touches.

How many examples do I need in a golden dataset to catch regressions?

50 examples is enough to detect major regressions — a drop of 10 percentage points or more in quality scores. To detect smaller shifts with statistical confidence, you need around 200 examples. Start with 50 hand-curated edge cases that cover your product spec, and grow the dataset as new failure modes surface from production traffic. A 50-example suite that runs on every PR is worth far more than a 2,000-example suite that runs quarterly.

Why don't benchmark scores like MMLU or HumanEval tell me if my application is working?

Benchmarks measure general model capability on standardized tasks. Your application has a specific task, specific users, and specific failure modes that no general benchmark covers. A model that tops the MMLU leaderboard may answer your customer support queries worse than a smaller model you fine-tuned on your domain. Benchmark scores are useful for model selection, not for measuring whether your specific product is doing its specific job well.

What is LLM-as-judge and how accurate is it?

LLM-as-judge means using a capable LLM to score or compare outputs from your application automatically. In the best-known study (MT-Bench, Zheng et al. 2023), a GPT-4 judge reached about 85% agreement with human reviewers on chat-response quality — higher than the ~81% agreement between two humans in the same study, though agreement varies by task. The catch is calibration: a judge validated at above 0.7 Pearson correlation against expert verdicts on your domain is reliable; one that has not been validated is measuring noise.

What are the five stages of an LLM evaluation safety net?

The five stages are: (1) dev unit tests — deterministic checks on format, constraints, and obvious failures that run in seconds; (2) pre-release adversarial evaluation — targeted probes for edge cases and known failure modes before any PR merges; (3) CI regression — your golden dataset runs on every pull request as a quality gate; (4) production sampling — scoring a fraction of live traffic with LLM-as-judge to catch distribution shifts and model provider updates; (5) runtime guardrails — online classifiers or rule checks that catch harmful or off-policy outputs before they reach users.

// RELATED

You may also like