He deleted one "useless" line from a live prompt. Three days later, a support ticket showed the damage. This is the version control guide he needed.
- A developer removed a line that looked useless from a live prompt. At deploy time, nothing flagged it. Three days later, a support ticket revealed the output had gotten noticeably worse.
- Across 1,018 scored prompts, the weakest area was handling bad input. The average score: 31.5 out of 100. Ironically, those instructions are often the first to get cut as "obvious filler."
- The fix is a four-step workflow. One of those gates would have caught this change before it went live. If you don't want to buy a tool, git and a scoring script are enough.
He deleted a few lines that looked useless. Nothing alerted him at deploy time.
A developer was editing a prompt that was actively serving users. A few lines seemed pointless, so he deleted them, saving a few tokens in the process. It was a routine "quick cleanup" done without much thought before a deploy. After the deploy: no errors, no alerts, all monitoring green. Three days later, a support ticket arrived. Users were getting vague, off-topic answers, sometimes confidently wrong.
This is from a developer's post-mortem on Reddit's r/PromptEngineering. He describes the prompt versioning system this incident forced him to build. If you have any prompt serving real users — an API-backed app, an automated daily pipeline — this is a trap you'll hit eventually. And you might not even know you've hit it.
For three days, that degraded prompt served real traffic. It quietly produced worse answers, and the system gave zero signals.
Why "broken prompt" is a concept your monitoring can't see
Here's the key difference between breaking code and breaking a prompt: when it breaks, will anyone tell you?
| Broken code | Broken prompt | |
|---|---|---|
| Immediately visible? | Exception, compile error, or crash | Still returns a plausible-looking string of text |
| What the API returns | A failure | A "normal" result |
| Who tells you? | Tests, the compiler, or monitoring alerts | No one |
| When do you find out? | Right away | Three days later, via a support ticket |
The root cause is that prompts are natural language. No matter how badly you mangle one, the model still tries to return text. It won't throw an error or signal a failure. It doesn't break all at once. It just gets slightly worse each iteration, until "slightly worse" becomes "the customers notice."
What about eyeballing the output before deploy? That only catches the obvious: a returned error, a broken format, an answer that's clearly off-topic. It won't catch a slow quality decline. There are two reasons. First, the output still looks like a legitimate answer, just a worse one. Second, you're reading with expectations. You know what the prompt is supposed to do, so you interpret everything through that lens. Real regression only surfaces when diverse, real-world users stress-test it, and its form is usually a support ticket.
Out of 1,018 scored prompts, the weakest dimension is handling bad input
This isn't a one-off mistake. After scoring 1,018 prompts, the same weakness showed up consistently. Each prompt gets a score out of 100, broken down into four dimensions. Together, they form a checklist for "how can a prompt go wrong?"
| Dimension | What it asks for | How it typically fails |
|---|---|---|
| Clarity | The task has exactly one reasonable interpretation; the model shouldn't have to guess | Vague verbs: "help me with this," "make this better" |
| Specificity | The requirements for the output are measurable, not just adjectives; the model shouldn't have to guess "what does done look like?" | "Write a concise summary" instead of "Write a 3-sentence summary in plain language" |
| Structure | Instructions in logical order: role first, context second, task third, format last | Format requirements buried after the task, role missing entirely, constraints scattered |
| Robustness | The prompt specifies what to do for the most likely edge cases | The prompt assumes clean, well-formed input; real users throw everything at it |
The lowest of the four is the last one: RobustnessThe original English term. It asks: does this prompt say what to do with bad input? What to return when the user's request is unclear? When it's empty? When the format is completely wrong? Each case needs a specific, written-in-stone action. A vague "please handle it well" doesn't count. — in other words, does this prompt say what to do when the input is bad? The average score across 1,018 prompts: 31.5.
The instructions for handling edge cases only matter when they're needed. So they're never tested.
Why is this the weakest dimension? Because these instructions never actually get exercised in your testing.
This dimension deals with specifying what to return for unusual inputs. The classic example is "the user didn't say what they wanted." Other cases include empty input or a completely malformed format. But when you test, you usually feed your prompt well-formed content. You rarely trigger these edge cases.
So when it's time to trim a prompt, these lines are the first to go: they use up tokens, and they never seem to do anything. Delete them, the prompt gets shorter, you test it, everything looks fine. These two facts share a root cause: the fact that it's the weakest is the same reason it's the most likely to be deleted.
A seatbelt and an airbag. You don't need them on a normal drive. They look like extra weight and cost. But they're the only thing that helps when something goes wrong. You won't feel their absence the day you remove them. The bill comes due on the day you actually need them.
Deleting that one line dropped the score by 14 points
After implementing this workflow, it caught a "cleanup" edit in the act. The change looked harmless. It dropped the score by 14 points. The reason: it removed one fallback line.
The original post describes that line as "load-bearing." In this context: it looked like a redundant extra sentence, but it was actually the structural support. It specified what to return when the user's input is malformed. When you take it out, nothing else catches those cases.
After the deletion, normal input worked perfectly. Tests passed. Logs were clean. Only the malformed inputs started getting vague answers — the same users who later filed the support ticket.
Ten seconds to see it, instead of another support ticket.
Ten seconds to see it, instead of another support ticket.
To be clear, there's nothing wrong with deleting things from a prompt. Anthropic's official prompting guide, which we've covered, is basically a manual on what to cut. The problem arises only when you delete without a safety net.
Code assertions don't work here. So we score instead.
The top comment on the post was this:
Smells like repackaged ancient software engineering practice. What's new here?
So changing the code changes the result... isn't that just regression testing? Also ancient. We threw away all the old practices because of AI, and now we're rediscovering them one by one :D
Comment on r/PromptEngineering
That's a fair point. Version control isn't new. What is genuinely new for prompts is this one layer:
In code, you can write assert output == expected — a machine-readable check that says "the result must equal this." That works because there's exactly one right answer.
Model-generated text has no single right answer. Run the same prompt twice and you'll get different wording. This kind of assertionAn "assertion" in automated testing is a check that says "the result must equal this." It's like grading a multiple-choice test: you can check it against an answer key. An essay has no exact answer key — you can only score it with a rubric. just doesn't work on prompts.
So the approach is to give your prompt a fixed set of test questions. Use the same batch of inputs every time, then examine the output. But the check is a score, not a true or false. The output is evaluated against a rubric and gets a number from 0 to 100. This runs automatically before any new version is promoted to production.
And because scores are broken down by dimension, you can see which part broke. The report comes back as "Robustness dropped from 61 back to 17." It points to the specific dimension, not just a single overall number. You can fix it with precision: change line 3, and that dimension goes from 17 back to 61. Nothing else moves.
In one sentence: it's regression testing for things that don't have a single right answer. Compare the new version against the old one before it goes live. If it's worse, block it.
A four-step workflow: versioning, diffing, scoring, and an endpoint
The skeleton of these four steps comes from the original post. The "how to implement it" for each step is our own synthesis, not a quote from the original.
Before the incident, these prompts lived in a Google Doc: "Version A," "Version A-final," "Version A-final-2." You know the pattern. It works until it doesn't. Once he identified the root cause, the workflow had to change. Here are the four steps, explained without tying them to any specific tool:
1 · Version every prompt
Version numbers are just "v4," "v5." You can pull a diff between any two versions — a line-by-line comparison that makes every change explicit. It's the same granularity as reviewing code changes line-by-line before merging. "final_v2" naming can't do this.
- One file per prompt. Don't cram several prompts into one doc — you can't diff them individually.
- The file should also store the version number, what changed in this version, and the last test results.
- Put changes in git (or anything that produces a diff). The single criterion: you can always answer "which lines changed between v4 and v5?"
2 · Mark one version as the "production" version
"Which version is live right now?" has one answer. Everything else is a draft. It doesn't go live unless you deliberately promote it. No exceptions, even for "obviously fine" small changes.
- Have a single place that says "current production version = v4." One field is enough. Don't rely on memory or chat logs.
- Drafts can change as much as you want. They don't affect production until you change that flag.
3 · Before you promote the new version, score both old and new. Compare.
Set a line in advance. If the new version drops below it, it's red-flagged and can't be promoted. This is the exact gate that would have caught that original deletion before it reached a real user.
- Build a set of test questions first: use real user inputs. Include several malformed ones (empty, missing info, broken format) — these exercise the edge-case handling, which is exactly what gets deleted first. The original post doesn't specify how many questions; set that based on your own scenario.
- Run the same test questions through both versions. Each one gets a score.
- Compare two things: Did the total score fall below your line? And how much did each dimension drop? The total tells you it broke; the dimension tells you *where* it broke.
- If it falls below the line, stop. Don't just "note it and proceed." If this gate doesn't halt things, the first three steps are pointless.
4 · Serve the production version from a slug endpoint
Don't hardcode the live prompt into your code. When you want to promote the new version, the app just picks it up automatically. No redeploy. If you need to roll back, just re-promote the old version. No more digging through git history at 2 AM while tickets pile up.
- Your code references a name (e.g.,
support-agent), not the full prompt text. - The app looks up the current production version by that name. Changing the version only changes that "current production version = ?" flag.
- What you get is rollback speed: in an incident, re-flag the old version and you're done. No redeploy.
- Copy the current production version, v4. Make your edits and save it as v5.
- Pull the diff. Look at exactly which lines changed.
- Run the same batch of test questions through both v4 and v5. Get a score for each.
- Compare. If the total is below the line, stop. Look at the dimension breakdown — which one dropped? This is where that 14-point drop surfaced, in ten seconds.
- If it passed, update the "current production version" flag from v4 to v5.
- Next time the app fetches the prompt, it gets v5 automatically. No redeploy.
- If something goes wrong in production anyway, switch the flag back to v4. Rolled back in seconds.
Set the pass line per prompt, not with a global number
So where do you draw the line? You use two layers at once:
A hard gate: it must be at least X points to go live. And that X is set per prompt, not as a global number. The reasoning: generation, extraction, and classification tasks aren't all at the same baseline. Using one number for everything will mis-measure something.
On top of that, add a relative drop check — a regression check for "passing but worse." It doesn't look at whether the absolute score is good enough. It looks at how much it dropped from the previous version. That 14-point drop was caught by this layer.
And one more thing about attitude: a red flag is meant to make you look, not to be a dogma. Sometimes a 5-point drop is a good trade — maybe the new version is simpler and cheaper to run. The pass line is a guardrail, but a human still makes the call. The real value is that you know what you changed before it goes live.
What if the score drops, but you think the new output is clearly better? The rewrite gives one diagnosis: your test batch probably doesn't reflect real usage. The things your real users ask aren't in your test inputs. That's from the rewrite; the original author never answered this one.
No budget? git plus a scoring script will do
This workflow doesn't depend on any specific product. git, a scoring script, and a Makefile will cover most of it. The specifics: store each prompt in its own file in git, with the version number and last test results inside. Write a small script that runs the same batch of inputs through both versions and prints the difference. Store those outputs too, so you can see which version quality started to dip.
As for when to run it: every time before you promote a change — that's the gate that stops bad ones. If you're iterating frequently, score as you go to avoid rework. You can also set up periodic spot-checks on the live version. Some regressions aren't your fault: the model version changed, or users started asking different things.
Frequency is smoke test, not final exam. Run it on every substantial change. Don't save it for the big version release. This is what decides whether you find problems in hours — or in three days, like our opening story.
Clarity The task has exactly one reasonable interpretation; no guessing
Fail: vague verbs — "help me with this," "make this better"
Specificity Output requirements must be measurable, not adjectives
Fail: "write a concise summary" vs. "write a 3-sentence summary in plain language"
Structure Role first, context second, task third, format last
Fail: format specs buried after the task, role missing, constraints scattered
Robustness Specify what to do for likely edge cases
Fail: assumes users give clean, well-formed input
Before (18/100) Help the customer with their issue and be professional. After (79/100) You are a support specialist. Read the message and return: summary (1 sentence), customer_intent (only what's stated), urgency (urgent / normal / low). If no clear request: intent = 'unclear', urgency = 'normal'.
- uses: FranciscoFerreiraff/prompteval-action@v1
with:
api_key: ${{ secrets.PROMPTEVAL_API_KEY }}
prompt_file: prompts/support-agent.md
baseline_slug: support-agent
min_score: 75
fail_on_conflict: true
A broken prompt never tells you. You find out from a support ticket.
A developer learned this the hard way. His fix: a four-gate workflow, explained in one page.
↓ One page to read · Includes an animated figure
Deleted one "useless" line. Three days later, a ticket.
A developer was editing a live prompt. A few lines looked like filler, so he cut them — also saving a few tokens. This is his post-mortem from r/PromptEngineering. If you have any prompt serving real users, this applies to you.
After the deploy: no errors, no alerts, all monitoring green. Three days later, a support ticket: answers were vague, off-topic, sometimes confidently wrong.
Broken code yells. A broken prompt is silent.
Same "break," different outcome: code throws an exception, fails to compile, or crashes. A prompt still returns a plausible string of text. The API gets a "success." Logs are clean. Tests pass.
Your own eyes can't catch it either. It's still a legitimate answer — just a worse one. And you're reading with expectations. It all reads fine. The real slide only shows up when diverse real users stress-test it — and the form it takes then is a ticket.
1,018 prompts scored: 'handle bad input' is the weakest
This isn't one person's mistake. Across 1,018 scored prompts, the weakest of four dimensions is Robustness (does the prompt say what to do with bad input — unclear request, empty input, wrong format?). Average: 31.5/100.
These two things share a root: Robustness covers edge cases, but normal inputs never hit them. Your tests follow the normal path, so you never see the impact. Scores are from the author's own scoring platform, no third-party verification.
This isn't regression testing: no single right answer, so you score instead
The top comment asked: "Isn't this just regression testing?" In code, you can write "the result must equal this" (an assert). That works because there's one right answer. Model-generated text has no single right answer — run it twice, get different wording. The assert fails.
Scores are given per dimension, so you can see exactly *what* broke. The workflow has four steps:
- Version your prompts. Keep a line-by-line diff record.
- Tag one version as "production." Everything else is a draft.
- Score old vs. new before promoting. Below the line, block it. This is the gate that would have caught the original edit.
- Serve the production version from a slug endpoint. Don't hardcode it. Rollback = re-flag the old version.
Set the pass line per prompt — not a global number. Add a relative drop check against the previous version. No budget? git plus a scoring script is enough. The full article has a copy-paste GitHub Action config.
- × What if user is unclear…
- × What if input is empty…
Members can copy or download the full Markdown, and let their AI subscribe and ask.
Found this useful? Leave your email — every new explainer, the moment it's out. Unsubscribe anytime.