Nobody ships code by running it once on their own laptop and saying "looks fine". Yet that is exactly how most prompts reach production: someone tries three inputs in a playground, likes what they see, and pastes it in.
Then a month later the answers get worse and nobody can say when it started, what changed, or how to get back. This last lesson is about the discipline that prevents that — and it's the topic that turns "I know prompting" into "I've shipped prompting".
Why "I tried it and it looked good" fails
Three reasons, and the third is the one that bites hardest.
Your test inputs are polite. You type clean sentences. Production sends typos, one-word messages, three questions at once, Hinglish, and pasted screenshots described in words.
A prompt change is global. One reworded line affects every user on every request. There is no gradual rollout in a prompt you edited and saved.
You only look at what you were fixing. You changed the prompt to handle refunds better, checked refunds, shipped — and didn't notice that delivery questions now get the refund template. The regression you don't look for is the one you ship.
Building an evaluation set
An evaluation set is a fixed collection of real inputs with expected outputs or checkable criteria. Two properties do all the work: it's drawn from real traffic, and it's frozen, so a score change means the prompt changed and not the test.
| Portion | Share | Why |
|---|---|---|
| Typical traffic | ~60% | sampled to match production's real mix, not an even split |
| Edge cases | ~25% | ambiguous, empty, wrong language, two intents, hostile |
| Past failures | ~15% | every reported bug, added the day it was reported |
Fifty cases is enough to start and infinitely better than none; 150–300 is where a classification set becomes trustworthy. Grow it only by appending — never by editing a case until the model passes it, which is how teams quietly test nothing at all.
💡 The cheapest way to build one: take a week of real traffic, sample 100 conversations, label them by hand. It is a dull afternoon and it is the single highest-return afternoon in an LLM project.
Scoring: three levels
Level 1 — deterministic. Free, instant, and covers more than people expect: does it parse as JSON? Are required fields present? Is it under the word limit? Does the category match? Does it avoid the forbidden phrase? Run these first; a failure here needs no further judgement.
def check(case, output):
failures = []
if not parses_as_json(output): failures.append("not json")
if word_count(output) > 120: failures.append("too long")
if case.expected_label != label(output): failures.append("wrong label")
if invented_order_id(output, case.facts): failures.append("hallucinated id")
return failuresLevel 2 — reference comparison. Useful where a canonical answer exists. Exact match works for labels; for text, semantic similarity against a reference is a rough signal at best, because many good summaries share no wording with yours.
Level 3 — LLM-as-judge. For open-ended quality, where the first two levels run out.
LLM-as-judge — and its catch
You are evaluating a customer-support reply. You are not writing
one and not being helpful to the customer.
<ticket>{{ticket}}</ticket>
<facts>{{order_json}}</facts>
<reply>{{reply}}</reply>
Score each 1-5 with a one-line justification that quotes the reply:
1. GROUNDING - every factual claim appears in <facts>.
Score 1 if any figure, date or id is invented.
2. RESOLUTION - does it answer what <ticket> asked?
3. TONE - appropriate to the customer's evident frustration.
4. ACTION - ends with one concrete next step.
Return JSON only. Set "blocking_issue" when grounding is 1.Named criteria rather than one "quality" number, because you can't act on a 3/5 overall. Justifications that quote the text, because that reduces sloppy grading. And a blocking flag, so the worst failure isn't averaged away by three good scores.
The catch, which is the interview follow-up: the judge is a model, so it needs evaluating too. Have humans label 100 outputs, run the judge on the same 100, measure agreement. If it disagrees with your humans, your rubric is wrong — fix it before believing any of the numbers. Re-check whenever the judge model version changes. Known biases to watch: judges favour longer answers, and favour outputs from the model family they belong to.
Wait — is 91% vs 88% an improvement?
On a 40-case set, 3% is 1.2 cases. That is noise wearing a percentage sign.
What to do instead of celebrating: look at which cases changed direction. A change that fixes two cases and breaks one is a completely different situation from one that fixes one cleanly — and both show as "+1". Then ask whether the broken one matters more than the fixed ones; a regression on a payment case outweighs an improvement on a greeting.
🎯 Selection-round radar: "How would you evaluate a prompt?" is the design question that separates seniors. Full answer: a frozen set of real inputs including edge cases and past failures → deterministic checks first, LLM-as-judge with a written rubric for open-ended quality, calibrated against human labels → run it on every prompt change and every model version change → look at which cases moved, not just the aggregate → version the prompt and log the version with each request so you can roll back.
Versioning prompts like code
A prompt edited in a dashboard with no history is an unreviewed production deploy. The practice is the same as for code, for the same reasons:
- In version control — as files, diffed and reviewed in a pull request. Someone should read a prompt change before every user gets it.
- Version id logged with every request — so "what exactly did this user see?" is answerable weeks later.
- Eval results attached to each version — the score is part of the artefact, not a screenshot in someone's chat.
- Rollback in seconds, not archaeology.
- The model version recorded too — the same prompt on a new model version is a new behaviour, and this is the one people forget.
log.info({
"request_id": request_id,
"prompt_version": PROMPT_VERSION, # "support-reply@v7"
"model": "claude-sonnet-4-5-20250929",
"input_tokens": usage.input_tokens,
"output_tokens": usage.output_tokens,
"latency_ms": elapsed_ms,
})With those five fields you can answer almost every question a production incident raises. Without them you are guessing.
The loop in production
And the trigger people miss: run the eval when the provider ships a new model version. Your prompt didn't change, but its behaviour may have. Teams that only run evals on their own changes discover model updates through customer complaints.
Track alongside quality: cost per request, p95 latency, and the rate of deterministic-check failures. A prompt that scores three points higher and costs twice as much is a trade-off someone should make deliberately, not one you make by accident.
Common mistakes
- Shipping on vibes and three hand-picked inputs.
- Writing the eval set from imagination instead of real traffic.
- Editing a test case until the model passes it.
- Trusting an uncalibrated LLM judge's scores.
- Reading a 3% move on 40 cases as a real improvement.
- Prompts living in a dashboard with no history and no review.
- Not logging the prompt version, so rollback becomes archaeology.
- Never re-running evals when the model version changes.
Quick recap
| Concept | One-liner |
|---|---|
| Why evaluate | a prompt change is global; the regression you don't look for ships |
| Eval set | real inputs, frozen, ~60% typical / 25% edge / 15% past failures |
| Scoring | deterministic checks first, then reference, then LLM-as-judge |
| Judge caveat | calibrate against human labels before believing it |
| Reading results | which cases moved, and which direction — not just the aggregate |
| Versioning | in git, reviewed, logged per request, rollback in seconds |
| Re-run trigger | every prompt change and every model version change |
Practice Zone — PYQs from real selection rounds
Six MCQs, then two design tasks: build an evaluation set for a ticket classifier, and write an LLM-as-judge rubric you could actually trust.
Why is "I tried it a few times and it looked good" not enough before shipping a prompt change?
Asked in

What belongs in a prompt evaluation set?
Asked in

For an open-ended task like "summarise this ticket", how do you score outputs at scale?
Asked in

Why version prompts the same way you version code?
Asked in

A new prompt scores 91% vs 88% on your 40-case eval set. What is the responsible conclusion?
Asked in

Your evaluation passes but users still complain. Most likely explanation?
Asked in

Hands-on tasks:
You own the prompt that classifies incoming support tickets into 6 categories. Design the evaluation set and the process around it: what cases, how many, how scored, and what happens when the score drops.
Asked in

You need to score 500 generated support replies per day for quality. Write the judge prompt, and state how you'd know whether to trust its scores.
Asked in

FAQ
How big should an evaluation set be?
Start with 50 — it catches obvious regressions and is far better than nothing. 150–300 makes small differences readable for classification. Past that, coverage of the failure modes matters more than raw count: 200 cases that include your five real edge cases beat 1,000 that don't.
Do I need a tool for this?
No. A CSV and a Python script that loops through it and prints a table is a perfectly good version-one, and it removes every excuse not to start. Dedicated evaluation and tracing tools help once you have several prompts and want history and dashboards.
What do I say in an interview if I've never done this at work?
Describe it on a project of your own — even a small one. "I built a ticket classifier, made a 60-case set from real Reddit posts including ten ambiguous ones, and iterated the prompt from 78% to 91% by fixing the two categories it confused" is a stronger answer than most people with the job title give.
That's the course. Next: the company pages — real questions from each company's rounds, with full answers. Start with TCS Prompt Engineering Interview Questions →
Or continue the AI track: RAG, AI Agents and MCP all build directly on what you just learned.


