📕 Read the full ebook AI Reliability Engineering · 24pages
Your browser cannot display PDFs inline. Open in new tab
This post is a condensed rewrite. The PDF is the full book.

This is for backend engineers and product owners who are wiring AI features into a live product. By the end, you should be convinced that deciding what a user sees the moment a model is wrong, slow, or silent matters more than chasing one more percentage point of accuracy, and you should have a concrete set of patterns for building that decision into your system.

Here is the claim up front: the reliability of an AI system comes from how you designed its failure handling, not from the model’s benchmark score. Chasing accuracy is an open-ended project, and no matter how far you push it, production will still produce wrong answers on a regular basis. Deciding in advance what the system shows when that happens is a bounded project, and once you do it properly, the system keeps serving users no matter what specific way the model breaks. What follows walks through why AI failure behaves differently from traditional software failure, and then works through the concrete practices that follow from that difference.

Illustration of the core idea of AI Feature Reliability Comes From Failure Design, Not Model Accuracy A visual metaphor for the article’s key idea.

Traditional software fails in binary; AI fails on a continuum

An ordinary function either returns a value or throws an exception. Only one of those two things happens, so the calling code knows with certainty whether it just succeeded or failed. When an exception fires, a stack trace tells you exactly where and what went wrong. Decades of software reliability engineering were built on top of this clean boundary.

Code that calls a model breaks this assumption completely. A model can return a fully wrong answer without ever throwing an exception. Latency swings by multiples depending on input length and server load, and the same input can produce a different output the second time you send it. The mere fact that a response arrived tells you nothing about whether that response is usable. It can be correctly formatted but factually wrong, or it can answer a question nobody asked in a tone confident enough to sound right.

This continuity opens a wide gray zone between success and failure. A system that cannot handle that gray zone hides the failure and hands the user a plausible-looking but wrong result, which from the user’s side is indistinguishable from the system quietly lying to them. That is why the first responsibility of an engineer building an AI feature is not raising the hit rate on correct answers. It is deciding, ahead of time, how the system behaves when the answer is wrong, late, or missing entirely.

Start design from one question: what does the user see when this fails?

Anchoring design on this single question pays off in several ways. First, it forces failure handling to be part of the normal code path instead of an afterthought bolted on later. If you are writing the code that produces a correct response, the discipline this question creates means you write the code for the failure case in the same sitting.

Second, this question makes team discussions concrete. “What happens if the model fails?” is abstract enough that nobody can answer it cleanly. “If the summary generation fails, does the user see a blank box, or the first few sentences of the source text?” gets an answer immediately, because it forces everyone to picture an actual screen state. Questions that force you to picture a concrete screen are the ones that turn into code that actually ships.

Third, this question is also how you set priorities. Not every AI feature deserves the same level of failure handling, because time and engineering resources are finite. Separate features whose absence breaks the core value of the product from features whose absence is merely inconvenient before you invest in elaborate fallback machinery. This tiering exercise should not be left to engineers alone; it is safer to review it with someone who actually understands the user’s real workflow, because features that look trivial from inside the codebase often turn out to be load-bearing for how users actually get their work done.

Degrade in three layers: display, behavior, and accuracy

Graceful degradation is the principle of lowering quality in stages instead of turning a feature off entirely. It becomes much easier to manage once you split it into three layers: display, behavior, and accuracy.

The display layer is about what appears on screen, and it is both the easiest layer to implement and the first one you should build. When an AI-generated summary fails, instead of leaving the summary box empty, show the first few sentences of the source text, or at minimum communicate clearly that the summary feature is temporarily unavailable. The behavior layer is about the range of actions still available to the user. If AI-generated autofill suggestions disappear, the path where a user types the value in manually must always stay open. The accuracy layer is the choice to lower the quality of the result rather than remove the result altogether. Falling back to a simpler model or to rule-based logic instead of the primary model means the user never walks away completely empty-handed, even if what they get is a step down in quality.

Deciding these three layers ahead of time means a team does not have to improvise judgment calls in the middle of an actual incident. The question of how far to degrade is already answered, and the code path for that answer already exists.

Circuit breakers: stop piling load onto a server that is already dying

The instinctive first response to a failed model call is to retry it. If the network hiccuped for a moment or the server was briefly busy, a retry is all you need. The real problem shows up when the model server itself is overloaded or fully down. In that situation, retries pile more requests onto a server that is already struggling, which slows recovery instead of helping it. Worse, the caller holds resources open while it waits for a response, so an outage in one model server propagates to every service that calls it.

A circuit breaker exists to cut off exactly that propagation. When failures repeat past a threshold, it stops even attempting the call and routes immediately to a fallback path, sparing the dying server further load while also sparing the caller an indefinite wait. It moves through three states.

State Behavior Transition condition
Closed Every call goes through to the model server normally Failure rate crossing a threshold moves it to Open
Open Calls are never attempted; requests route straight to fallback After a fixed timeout, it moves to Half-Open
Half-Open A small number of trial requests go to the model server Success returns to Closed; failure sends it back to Open

This loop lets the circuit breaker isolate a failure automatically without a human in the loop, and it lets the system find its way back to the normal path on its own once the server recovers. One thing worth watching for: if you gate the circuit purely on error rate, you will miss a failure mode where the model never returns an error but starts responding ten times slower than usual. That kind of degradation is invisible to error-rate metrics alone, so latency distribution needs to be part of what trips the breaker too.

A fallback should be a ladder, not a single alternative

If you only prepare one fallback, you hit a dead end the moment that fallback also fails. That is why features with significant user impact deserve a fallback ladder with several rungs instead of a single backup plan.

The first thing worth trying is retrying the same model with a tighter timeout. A momentary delay resolves at this stage most of the time. The next rung is switching to a lighter, faster substitute model; quality drops somewhat, but the user still gets a model-generated result. If that fails too, the system falls back to rule-based logic, producing a result computed from predefined conditions or statistics instead of the model. The last rung is pulling the most recently successful result from cache and showing that instead. It is not real-time, but it beats leaving the user staring at an empty screen.

What matters about this ladder is not the specific order so much as the fact that each rung must be strictly simpler and harder to fail than the one above it. Rule-based logic and cached results survive even when the model server is completely dead, which is exactly why the bottom rung of any fallback ladder must be a path with zero dependency on the model.

You need to be able to roll back instantly, without a deployment

Ordinary software bugs get fixed by patching the code and shipping a new deployment. That loop is far too slow for AI features. When the model server starts misbehaving, or a specific prompt is being coaxed into producing harmful output, or a newly rolled-out model version turns out to be lower quality than expected, users keep having a bad experience for the entire time it takes to patch, build, and deploy.

A feature flag exists to skip that loop and change a running system’s behavior immediately. Pull the model version, the prompt content, and even whether the call happens at all out into a configuration value that lives outside the code, and flipping that value takes effect instantly. When introducing a new model or a new prompt, the standard practice is never to apply it to all traffic at once. Start with a tiny fraction of traffic, confirm nothing is broken, and widen the rollout gradually. The same user should keep receiving the same version for the duration of the observation window; randomly assigning a different version on every request breaks consistency of results and makes it much harder to actually measure the new version’s effect.

The part teams most often miss is automated rollback. Nobody can watch a dashboard every second while a rollout percentage climbs. If the error rate for the group on the new model rises noticeably above the group on the old model, the system needs to reset the rollout percentage to zero and page the owner before a human notices, because the pace at which a human watches metrics and the pace at which an incident spreads are simply not the same unit of time.

Write the incident script before the incident happens

Trying to define severity levels the moment an incident is actually unfolding slows down both the judgment call and the response. Severity has to be defined ahead of time. A core feature going fully down, or wrong results reaching a large volume of users, is a top-severity incident that pages the response team immediately. An important feature degrading while a fallback keeps the service running is a mid-severity incident that an owner needs to check within a defined window. A minor feature having issues, or the fallback path firing more often than usual, is a low-severity incident that gets reviewed at the next regular checkpoint. Severity criteria should never rest on a single error-rate number; they need to account for how many users are affected, how risky the exposed result is, and how long the condition persists.

Incident response itself becomes far less likely to miss something once you lay it out as five stages: detect, isolate, mitigate, recover, and postmortem. Detection is where metrics or alerts first surface the problem, and the circuit breakers and automated rollbacks discussed above already carry most of this stage. Isolation is drawing a boundary so the problem cannot spread into otherwise-healthy features, which is what forcing a circuit open or disabling a specific feature flag accomplishes. Mitigation is reducing the damage users actually experience, whether that means forcing the fallback ladder down to a lower rung or rolling a bad model version back to the previous one. Recovery is bringing traffic back to normal in stages, following the same gradual-rollout principle used to introduce changes in the first place. Postmortem is writing down what happened, why, and what needs to change to keep it from happening again. Once these five stages are already documented, a team facing a live incident does not have to debate what to do; it just follows the script that was already written.

Graceful degradation, circuit breakers, fallback ladders, feature flags, and incident severity levels look like five separate techniques, but they are really five answers to a single question: what does the user see when this fails? A system that has already answered that question quietly shows a degraded version of itself no matter how the model breaks underneath it, instead of showing a broken screen. Model accuracy will keep climbing, and it should, but it is never going to reach a hundred percent. The people who build reliable AI systems are the ones who accept that fact and put their design effort into shaping the failure, not chasing the accuracy number. The remaining checklists and code-level implementation examples for each chapter continue in the full ebook.

Chapter Illustrations

Chapter 1 illustration

Tags: ai-reliability, circuit-breaker, fallback-design, feature-flags, graceful-degradation, incident-response, llmops, production-ai

Categories:

Updated: