Ground Truth

Why Your AI Works in Testing but Fails in Production

Mostafa DhouibMostafa Dhouib·15 min read
The short answer

When a model scores 95% in staging and collapses to 44% in production, the model is almost never the cause. The usual fix, rebuilding the model, retrains on the same data and reproduces the same failure. The real problem is what the model learned from the data, and it is usually fixable in days, not months.

Why Your AI Works in Testing but Fails in Production

The short answer. A model that scores 95% in staging and 44% in production a week after launch has almost never broken. It learned a shortcut in your training data that predicts the right answer in the room it was trained in, and that shortcut disappears the moment real users arrive. The common fix, rebuild the model, retrains on the same data and reproduces the same collapse. The real fix is usually the data, and usually days, not months.

A founder called me about a speech to command system running on a small edge device. In staging it tested at 95%. A week after launch, with real customers, it was sitting at 44%. More than half wrong, overnight. That is not a clean number on a dashboard, that is a wall of support tickets and customers saying the product does not understand them, and a founder staring at the end of the company.

Two contractors had already looked at it before me. Both recommended the same thing: rebuild the model. One quoted four months, the other six. The actual fix took five days, and the model was never the problem. I have seen this exact pattern across defense, industrial automation, fintech, healthcare, and more than nine other industries, where getting it wrong ran from six figures to millions. It is the same shape every time, and the interesting question is not why two smart people got the diagnosis wrong. It is why the cause was sitting in plain sight and everyone walked straight past it.

The 95% was never measuring what you think

Start with what a test score actually is. A high accuracy number in staging does not measure whether the system works. It measures how much your test set looks like your training set. If those two overlap, the score is high and it tells you almost nothing about the field.

That is the trap. The number that is supposed to help you find the truth becomes the thing that hides it, because once everyone sees 95% they trust the system and stop digging. The score becomes the reassurance, and the reassurance is what keeps anyone from looking at the layer where the problem actually lives.

First, rule out the boring stuff

Before I touch anything interesting, I check the plumbing, the same way a doctor rules out the common causes before the exotic ones. The model is not even my first suspect. Four boring questions:

  • Is the exact same model running in production as the one that was tested? You would be surprised how often it is not.
  • Is it getting the same input? Same audio format, same sample rate.
  • Is the preprocessing identical? If production cleans the input even slightly differently, the model is effectively hearing a different language.
  • Is anything quietly timing out or falling back to a default?

Most of the time, when a founder tells me the model is broken, the issue dies right here, in something dumb in the plumbing. In this case it was all clean: same model, same inputs, same pipeline. And that tells you something important. The model did not break. It generalized badly. It learned something that worked in the room it was trained in and stopped working the moment it left that room. Which points straight at the layer nobody watches: the data.

The model did not break, it learned the wrong thing

I pulled up the training data. At first glance it looked fine, tens of thousands of samples, every command, every kind of environment. But raw totals tell you nothing. The real question is never how much data you have, it is how the data is distributed inside itself.

So I sliced it, by command, by environment, by device, and by who recorded it. And there it was. Broken down by speaker, one bar covered almost the entire dataset. One person, the engineer who built the first version, had recorded nearly all of the training data. The moment you see that chart, 44% is not surprising anymore. It is the number you would expect.

The engineer who built the first versionthe bulk of ithis voice, his microphone, the hum of his room
Speaker 2a sliver
Speaker 3a sliver
Everyone elseslivers
The model never learned the commands. It learned him. Every one of those was a fingerprint sitting right next to the correct answer, thousands of times.
FigureThe training data sliced by who recorded it. Raw totals told us nothing; this chart made 44 percent stop being surprising and took minutes to produce.
One giant bar, a single speaker
dwarfing many tiny bars
Model, optimizes for the score
Learns the task
hard
Learns the fingerprint next to the answer
easy
The failure point
The easy path is the one the model actually takes.
Data on one side, a score on the other. It finds the shortest path between them.
FigureThe model finds the shortest path to a high score, and if one source dominates the data, that path is the source's fingerprint, not the task.

The model never learned the commands. It learned him. His voice, his pronunciation, his cadence, his specific microphone, the hum of the room he recorded in. Every one of those was a little fingerprint sitting right next to the correct answer, thousands of times. The model did the most natural thing in the world, it latched onto the fingerprint instead of the words.

Why a model takes the shortcut

Strip a model back to what it actually is. A machine learning model is a thing trying to get a good score. That is it. It can learn the actual task, which is hard, or it can grab whatever signal happens to predict the right answer, which is easy. It always takes the easy path, not because it is broken, but because shortcuts work. If one thing in your data lines up with the right answer often enough, the model leans on it, whether you meant it to or not.

The model has no concept of what you want. It does not know there is a task, it does not know you care about the commands rather than the microphone. All it has is data on one side and a score telling it how it is doing on the other, and it finds the shortest path between the two. It optimizes the data you gave it, not the thing you meant to build. Build the dataset out of one person, and one person is what it learns.

Learn the task
Understand the commands themselves
Hard, general, survives new speakers
No gradient advantage during training
Learn the fingerprint
Recognise the voice, the microphone, the room
Easy, and predicts the answer just as well
Rewarded identically by the loss
the model has no concept of what you meant
Data on one side, a score on the other. It finds the shortest path between the two, and one person is what it learned.
FigureA model is a thing trying to get a good score. It can learn the task, which is hard, or grab a signal that happens to predict the answer, which is easy. It takes the easy path, because shortcuts work.

Then real customers showed up. For the first time, the system was meeting strangers instead of the person it was trained on. It fell over, but not for any of the reasons everyone reached for. Not the architecture, not a too-small model, not a missing framework. It had learned the wrong lesson from the data.

Why the test set could not possibly have caught it

There is a mechanical reason the 95 percent was meaningless, and it is worth understanding because it is the most preventable part of the whole story.

The test set was split off the training data at random.

Think about what that does when one speaker dominates the dataset. A random split scatters that speaker's recordings across both sides of the line. He is in the training set, thousands of times, and he is also in the test set. So the test is asking the model to recognize the voice it was built around, on data collected in the same room, on the same microphone, on the same day.

The model's shortcut works perfectly on that test. Of course it scores 95 percent. The test set and the training set are the same distribution because they came from the same recordings, and a random split cannot separate what a group split would have.

This is the part that makes the failure structural rather than unlucky. A random split is incapable of detecting a group-level shortcut. It is not that the test was weak, or too small, or badly chosen. It is that the split strategy guaranteed the shortcut would survive the test, and no amount of adding more test data drawn the same way would have changed it.

Random split
The dominant speaker lands in training and in test
Same room, same microphone, same day, both sides
The shortcut works perfectly on the test
95%
Structurally incapable of detecting a group-level shortcut
Group split
Hold out whole speakers, sites, devices, time periods
The test asks whether it generalizes to a new one
The same system reports the truth on day one
44%
Same data, same model, honest number
Adding more test data drawn the same way would have changed nothing. The split strategy was the problem.
FigureA random split scatters the dominant source across both sides, so the test asks the model to recognize the voice it was built around. Only a group split asks the question production will ask.

The fix is to split by the grouping variable rather than by the sample. Hold out entire speakers, entire sites, entire devices, entire time periods, entire customers. Then the test measures whether the model generalizes to a new one of those, which is the question production is actually going to ask.

Split by group and this system would have reported something close to 44 percent on day one, before the rebuild was ever proposed. Same data, same model, honest number.

How to find out what your model actually learned

If you want to check this on your own system, there are four moves, and none of them requires a research background.

Slice the training data by every metadata axis you have. Who collected it, which device, which site, which source system, which time window. You are looking for a bar that dwarfs the others. This is the chart that made 44 percent stop being surprising, and it took minutes to produce.

Re-split by group and re-evaluate. Hold out whole groups rather than random samples and rerun your existing evaluation. No retraining required for the first look. If the number falls apart, you have your answer, and you got it in an afternoon.

Ablate the suspected shortcut. If you think the model is keying on something incidental, remove or neutralize it and see what happens. Performance that collapses when you strip a feature that should be irrelevant tells you exactly what the model was leaning on.

Test on a source that appears nowhere in training. One new speaker, one new site, one new device. A handful of samples from a genuinely unseen group is worth more than thousands of samples from a seen one, because it is the only measurement that answers the production question.

The reason these are not standard practice is not difficulty. It is that all four require opening the data, and none of them require touching the model, which makes them feel like they are not real work.

This is not a voice problem, it is a shape

Once you have seen this enough times, you stop seeing it as an AI problem and start seeing it as a shape that shows up everywhere:

  • An industrial control system tuned perfectly in the workshop that started making bad calls in the field. It had learned the workshop, not the job.
  • A famous image model everyone thought was recognizing an animal. It was detecting the snow in the background.
  • A fraud model that learned which data source a transaction came from instead of whether it was fraud.

Different industries, different teams, different systems, the same mistake underneath all of them. The AI learned the room it was trained in, and everyone mistook that for the AI understanding what they wanted.

Why smart people walk right past it

These are not dumb people. Usually several smart people miss it at once, and the reason has little to do with intelligence. It is that the model is the part you can see, the code, the architecture, the thing you can open up and argue about. The data just sits in a folder. Nobody gets excited about opening a CSV or analyzing a set of voice recordings, so everyone crowds around the visible part and the real cause sits in the dark.

Look at where the team's attention points. They have dashboards on the model, latency, accuracy, version history, a whole toolchain watching the one thing that was fine. And what is watching the data? Almost always nothing. It gets loaded once and never looked at again. Total visibility pointed at the healthy layer, total darkness around the broken one. That is not a people problem, it is a where-the-instruments-point problem.

The expensive trap: the rebuild reproduces the failure

Here is why this gets expensive. The rebuild got approved. Six months, the whole team, to rebuild a model that was never the problem, and they trained the new model on the same data, because nobody suspected the data. So the new model learned the exact same lesson, took the same shortcut, and collapsed to 44% again. The failure quietly resells itself.

And the money is not the worst part. The rebuild fails, and the company concludes not that they made the same data mistake twice, but that AI does not work for them, and walks away from something that would have worked perfectly if one person had looked at the right chart. That is the real cost. The failure does not just burn six figures and six months. It convinces smart people that something possible is impossible.

Staging
Test set drawn from the same source as training
95%
Measuring test resembles training
Production
Real users who are not that source
44%
Measuring whether it works
Same model. Balanced the data, added real noise, and it passed 99% in five days.
95%, 44%, and the five-day recovery are all measured.
FigureThe staging score measured how much the test set looked like the training set, not whether the system worked, so the same model read very differently in the two worlds.

So why does the rebuild keep getting recommended? Less competence, more psychology. Rebuilding feels like progress, it feels decisive. Saying "go fix the data" sounds too small, too cheap to be the real problem for a system that matters. The data answer also quietly points a finger at whoever collected it, so the safest option is the rebuild, which has no name attached to it. And the part nobody says out loud: nobody gets a six-month contract for telling you it is a five-day data fix. The people recommending a rebuild are usually not being dishonest, but the expensive answer happens to be the one that pays them the most.

The one habit that prevents all of this

The rule underneath all of it: any number you trust is standing in for the real thing. A test score, a KPI, a demo. The gap between the number and reality is exactly where trouble hides, in plain sight. The 95% was not a lie. It was answering a different question than anyone thought they were asking.

So, two habits, and one of them is the whole game.

When you build something, do not let your own team be the test. Hold real users out, different voices, different devices, different rooms, and map that to whatever you are building. If it only works on the people who built it, you want to find that out on a quiet Tuesday, not from customers in week one. And watch the inputs, not just the scores. The score tells you how the model is doing. The inputs tell you whether the real world still looks like your training data, because the day your real users stop looking like your training set is the day the score becomes a liar, and that day always comes.

The one habit: before anyone puts a number or a timeline on a fix, find out what the model actually learned. Not what you meant it to learn, what it picked up from the data you gave it. In practice that is a few boring questions. Who made your data, and does it trace back to one person or one source? Does your test set look anything like your real users, or is your team quietly testing itself? What changed between the demo and real life? That gap is where the story lives.

Once we knew what this model had actually learned, the fix was simple. We balanced the data so it was not all one voice, added real-world noise, retrained the same model, and it went back over 99%. Five days. No rebuild, no six months, no six figures. The model was never broken.

What stuck with me was not that the fix was clever, it was not, it was simple. It was how close the company came to spending six months solving the wrong problem, when the only thing standing between them and the right answer was a willingness to look at the boring things first. You do not need a machine learning PhD to catch this. You need someone willing to open the folder nobody wants to open.

If you have something that worked in testing and fell apart the moment real people touched it, tell me what is going on and I will tell you where I would look first.

FAQ

Why did my model score 95% in testing and fail on real users? Most often because the test set was split off the training data at random, so the source that dominates your training data also appears in your test set. The model's shortcut works perfectly on that test. A random split is structurally incapable of detecting a group-level shortcut, no matter how much test data you add.

How should I split train and test data to catch this? Split by the grouping variable rather than by the sample: hold out entire speakers, sites, devices, customers, or time periods. Then the test measures generalization to a new one of those, which is the question production will ask.

Why does an AI model pass every test in staging and fail in production? Because a staging score measures how much your test set resembles your training set, not whether the system works. If both were drawn from the same narrow source, the score is high and misleading. In production the inputs stop resembling the training data and the model collapses.

Should I rebuild the model when accuracy drops in production? Almost never as the first move. A rebuild trained on the same data reproduces the same failure, because the model relearns the same shortcut. Find out what the model actually learned from the data before committing to a rebuild. The real cause is usually in the data and fixable in days.

How do I tell whether the problem is the model or the data? Rule out the plumbing first, same model, same inputs, same preprocessing, nothing timing out. If that is clean, slice the training data by source, device, environment, and who recorded it. A single dominant source means the model learned that source's fingerprint, not the task.

What is training-serving skew? It is the gap between the conditions a model was trained and tested in and the conditions it meets in production. When that gap is large, offline metrics stay high while real-world performance drops, because the model optimized for the training distribution rather than the task.

Production AI reliability
The Balance Behind Every Screen: Thirteen Modules of Leave Screens Are One Arithmetic Identity
10 min read
Every Third-Party API Fails Differently, So Put One Adapter Between Each One and Your Product
9 min read
If You Cannot Rebuild It, It Is A Source
11 min read
Accept The Work, Not The Claim: An Agent's Own Status Is Not Evidence
10 min read
Accept Is Binding, So Accept Has To Be Idempotent
10 min read
A Prompt Is Not a Lock: Three Ways Row-Level Security Leaks Even When the Rule Is Written Correctly
9 min read
Slow at Scale, Worse When Scaled: It Is Usually a Query-Count Problem
8 min read
Six Drills That Break An Agent: Accept Milestones By Breaking Them, Not By Demonstration
10 min read
Settlement And Reporting Are Projections Of One Record, Not Two Systems That Agree
10 min read
Read-Only Is Not a Safety Boundary: It Protects Your Data From the Agent, Not Your Users From It
7 min read
Build the Gold Set From the Source, Not From Your Own Output
10 min read
Your Hybrid Search Is Silently Dense-Only, and Reciprocal Rank Fusion Will Not Tell You
7 min read
You Cannot Cancel What Does Not Heartbeat: Cooperative Cancellation And The Button That Does Nothing
10 min read
The RLS Trap That Passes Every Test: A Postgres View Runs as Its Definer Unless You Say Otherwise
11 min read
Authorise The Object, Not The Screen
10 min read
Entries Are the Record, the Balance Is a View
10 min read
The Cheapest Option That Does Not Work: Gate on the Physical Constraint Before You Rank on Price
8 min read
Trust the Webhook, Not the Page: Why Payments Go Missing Between Three Systems That All Report Success
9 min read
You Asked for 40 Rows and Got 4: What a Filter Does to an HNSW Vector Search
8 min read
Clear One Real Transaction Before You Build The Second Feature
10 min read
Checkpoints Do Not See What The Shell Did: Partial State Capture Is Worse Than None
10 min read
Your AI Vendor's Terms May Ban Your Users: Read the Population Restrictions Before You Pick a Model
11 min read
What Should Never Be An Agent: Use The Benchmark Gap As Your Decision Criterion
10 min read
When You Automate the Gatekeeper Away, Every Check They Made By Eye Becomes a Rule You Have to Enforce
9 min read
Temperature Zero Is Not Deterministic
10 min read
Score Each Stage or You Are Tuning Blind
10 min read
Schema-Valid and Ten Thousand Times Wrong: Units Are Semantics, Not Syntax
8 min read
Sync The Commercial Record From Day One, And Make The Sync Idempotent
10 min read
The Number Someone Signs: Rebuild It by Rule, Do Not Read It Off the Document
8 min read
Moving An Allocation Is A Transition, Not An Edit To Two Quantities
10 min read
Output Tokens Are The Bill: Why Trimming Your Prompt Did Not Help
10 min read
A Confidence Score That Never Changes Cannot Route Your Review Queue
10 min read
Your Decimal Engine Is Still Eating Floats: Determinism Is Decided at the Parser Boundary
8 min read
You Are Buying Verdicts, Not Prototypes: Price The Decision, Not The Build
9 min read
Who Owns This Fact? Assign Ownership Per Datum Before You Integrate, Not After
11 min read
Three Bugs That Look Like One Miss
10 min read
A Correction Is An Entry, Never An Edit
10 min read
What a Codec Does to a Watermark Is a Ceiling, Not a Bug
10 min read
Two Postgres Queues, Opposite Failures, Same Drill: Know Which Way Yours Breaks
7 min read
Fresh, Entitled And Real Are Three Properties With Three Mechanisms
10 min read
Concurrency Belongs at the Write, Not at the Read
11 min read
The Walls That Stop A Prototype: Signing, SDK Mismatch And The Review Queue
10 min read
Page On Silence, Not Only On The Value
10 min read
OpenAPI Cannot Describe Your Events: Seven Things an Event Contract Must State
11 min read
You Can Be 90 Percent in the Green Zone and Measuring Nothing
7 min read
Model Retirement Is A Regulatory Event
11 min read
Turning a Sensor Off Does Not Turn It Off: Back-Powering Through Shared Bus Pull-Ups
8 min read
One Core, Many Campaigns: Why Forking the Working Flow Per Tenant Always Drifts
11 min read
A Migration Is Not a Build: The Old System Knows Things Nobody Wrote Down
7 min read
Year-End Is a Rule, Not a Reset
10 min read
Spend Your Effort on the Layer With No Reference Path and No Error Case
7 min read
Where Your Inference Actually Runs
10 min read
Phasing Is the Architecture: Lay the Reliability Core at Phase One or Rebuild Four Times
11 min read
Cadence Lock: When Your Heart-Rate Monitor Starts Counting Your Steps
7 min read
Your Budget Cap Resets on Resume, Which Is Exactly When the Cost Arrives
6 min read
The Old System Knows Things Nobody Wrote Down
9 min read
Your Installed Web App Logs Users Out, and Nothing in Your Code Explains It
10 min read
Your Dates Are 543 Years Off, and Every Row Loaded Successfully
7 min read
Your Mel Spectrogram Is Not the Same as Theirs: Six Parameters That Have to Match
7 min read
Prove It By Construction, Not By Testing: Partitioning Away A Failure Class
10 min read
Model the Trade, Not the Screens
11 min read
The Fan-Out Budget: Expand, Filter With Something Cheap, Spend the Paid Call Last
8 min read
The Column List Matters as Much as the Rule: Row Policies Do Not Constrain Which Fields a User Can Write
11 min read
Inventory on a Copy, Touch Nothing Live
8 min read
Every Risk Control Is Verified Twice
10 min read
Put The Acceptance Risk In The Smallest Milestone
10 min read
A Cached Price Is a Gate, Not a Quote: Two Jobs One Number Cannot Do
7 min read
Bound the Concurrency or Your Latency Problem Becomes a Refusal
7 min read
The Shape Of A Heartbeat Is A Firmware Decision
10 min read
Price the Baseline in the Same Pass, or Your Ranking Is Not Answering the Question
8 min read
Positions, Not Stock Levels: What Changes When the Operator Is the Counterparty
10 min read
A Rule With No Instance Is Not Found by Inspection
8 min read
Give The Blocking Tests A Release Of Their Own
10 min read
A Global Minimum Is Wrong in Both Directions: Feasibility Rules Belong Per Location
8 min read
Stopping The Signal Is Not Switching It Off
10 min read
Four Mechanisms, Four Numbers: Why One Reliability Metric Hides the Work
7 min read
You Cannot Deduplicate Before You Normalise: Identity Is a Decision, Not a Property
7 min read
At Least Once With No Replay
8 min read
Your Multimeter Cannot See Your Sleep Current
10 min read
Put the Catalogue in Data and Growth Becomes a Row Instead of a Release
7 min read
The Most Important Screen Is A Database Decision
9 min read
Restaurant AI Fails on Three Numbers
10 min read
Prove You Can Get the Data Before You Build the Thing That Needs It
7 min read
Two Code Paths During a Strangler Migration
9 min read
Ninety Percent Is Not Success: Update Telemetry That Measures The Wrong Stage
10 min read
Milestones for a Buyer Who Cannot Read the Code: Make Each One Independently Checkable
7 min read
The Seam Is Nobody's Job: Why Failures Live Between Systems That Each Work Correctly
7 min read
Who Hears About the Failure First: Five Ways to Buy the Same Integration
6 min read
Rolling Back On A Platform That Forbids Downgrades
10 min read
The Device Decides The Form Factor
9 min read
Exactly-Once in Effect Is Not Exactly-Once, and the Difference Is Where Your Ledger Stops
6 min read
A Queue That Fails Safe
9 min read
Withhold the Insight When It Does Not Reconcile
11 min read
Attribute at Capture, Never Infer Later: An Untagged Record Belongs in a Bucket, Not in an Estimate
7 min read
A Staged Rollout Of A Bad Build Is Still A Bad Build
10 min read
A Reading Is Only as Good as Its Weakest Hop, So Prove the Whole Path Before You Commit the Fleet
7 min read
Read the Metadata Before You Read the Spec
9 min read
Two Paths Through the First Gate
9 min read
One Owner, No Seams
9 min read
Model Confidence or Independent Verification
7 min read
Edge or Cloud Inference: A Decision Table
6 min read
What Happens to Your AI Feature at Ten Times the Traffic
7 min read
Perception Accuracy Is Not Control Safety
6 min read
Perception to Control: The Handoff Nobody Owns
7 min read
Reconciliation Is Not a Nice-to-Have
7 min read
Ship It Once, Reach It Never: Designing for Hardware You Cannot Touch
7 min read
The Update That Bricks the Fleet: OTA Failure Modes
7 min read
Systems You Cannot Take Offline to Fix
7 min read
Fleet Scale Changes the Failure Mode, Not Just the Number
7 min read
PLC to Model: Where Industrial AI Actually Breaks
7 min read
What a Minute of Line Downtime Buys You in Architecture
7 min read
Change the Model, Restart the Submission
7 min read
Why Air-Gapped Changes Every Assumption in Your ML Pipeline
7 min read
AI at the Edge With No Network and No Retry
7 min read
Signs the Failure Is in the Seams, Not the Model
7 min read
What a Two-Week Diagnostic Should Deliver
7 min read
Your Green Dashboard Is Not Evidence
7 min read
How to Tell Your Vendor Is Aiming at the Wrong Thing
7 min read
What It Costs to Fix a Failing AI System
8 min read
The Verifier That Verifies Nothing
8 min read
Rebuild or Repair? How to Tell Before You Approve Six Months
8 min read
The Model Is Five Percent: Where Production AI Actually Fails
10 min read
The Cloud Hides Your AI's Real Failures
7 min read
Wrong vs Load-Bearing Wrong: When One AI Answer Becomes a Foundation
6 min read
Every Metric Is a Proxy: Goodhart's Law for AI Systems
7 min read
How to Build a Test Set That Actually Predicts Production
8 min read
Before You Blame the Model: The Four Boring Checks
8 min read
Cleaning Your Input Can Make Your AI Worse
9 min read
Your AI Is Memory-Bound, Not Compute-Bound (So Tuning the Model Won't Fix Latency)
11 min read
Confidence Is Not Evidence: Why an AI Model Can't Tell You When to Trust It
12 min read
Watch the long-form video this came from
Carrying a program like this one?

Tell us the system, the stakes, and the date that matters. You get a straight technical reply from the person who would lead the work, within 24 hours.

Bring us the program