Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

AI System Design

Design a Model Registry

Step 1 / 9

Learn AI system design by building the platform that versions, evaluates, promotes and can instantly roll back trained ML models — not training itself (see Fine-Tuning Pipeline) or feature serving…

The numbers to beatsame metricscandidate vs current baselinedefault: blockedon a meaningful regression0 manual stepsto skip

The whole design, in writing

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

The gap between "it trained" and "it's safely serving"

A model finishes training. Turning that into a file that answers real production requests safely is a genuinely different problem than training it — versioning, validating, gradually rolling out, and being able to instantly undo it if something's wrong. Skip this layer and "which model is actually in production, and can we prove it's the one we validated" becomes an unanswerable question.

ML Engineertrains, promotes
New in this step: ML Engineer.

We'll build the governance layer: immutable versioning with full lineage, staged promotion through validation gates, shadow/canary deployment because offline metrics alone aren't enough, instant rollback, and ongoing production drift monitoring.

What the new pieces do

ML Engineerclient
Trains a candidate model and wants a safe, auditable path from "it trained" to "it's serving real traffic" — with a fast way back out if it isn't.

Step 1 · The baseline

A file gets deployed

Simplest version: someone trains a model, copies the resulting weights file to wherever the serving system reads from, and it's live. What breaks down as this scales beyond one person, one model?

ML EngineerProduction Serving
New in this step: Production Serving. · swipe to pan the diagram

Model files get deployed ad-hoc, with no registry or tracked lineage. What's the core problem?

  1. "It works right now" says nothing about whether it can be reproduced, audited, rolled back to a known-good state, or even identified later — the problem shows up the moment something goes wrong or someone needs to answer "what exactly is running."

  2. Without a registry and tracked lineage, the model in production is effectively an untraceable artifact: if it starts behaving badly, there's no reliable record of what to roll back TO, and no way to reproduce or audit exactly how it was trained.

  3. File format efficiency is a minor, separate concern — the real problem is the complete absence of versioning, lineage, and a reliable rollback path.

Without a registry, nobody can reliably answer which model version is live, what produced it, or how to get back to a known-good state if it fails — a reproducibility and rollback blind spot that gets worse with every additional model and every additional engineer.

Why this piece earns its place

Worth being honest that this baseline is fine for a while. One model, one owner, a weekly batch job — a registry is pure overhead, and the trigger to build one is the second engineer or the second model, not a rule. What makes it stop working is quieter than it looks. Serving does not load a version, it loads a path, and a path like prod/model.pkl is a mutable pointer with no history. Replace the file during a rolling restart and half the fleet keeps the old weights in memory while the other half picks up the new ones, so for the length of the rollout you are serving two different models behind one endpoint and your dashboards show the average of two populations — a metric shift with no deploy event to blame it on. Nothing errors, and nothing in the telemetry names a version, because there are no versions to name. The cheap first move is a checksum and a version string that serving reports on startup and stamps on every prediction; everything later in this design is what you get once that string means something.

What the new pieces do

Production Servingservice
What actually answers real requests — starts as whatever file someone happened to deploy; becomes governed by every stage this design adds.

Step 2 · Immutable versioning and lineage

Every model, tagged with what produced it

Fix the blind spot with a registry.

Model Registryversions, stagesVersioned Artifactsweights + lineage
New in this step: Model Registry, Versioned Artifacts.

Introduce a Model Registry backed by Versioned Artifacts storage: every trained model is registered as an immutable version, tagged with exactly which training data version, code commit, and hyperparameters produced it. Past versions are never overwritten — a new training run is always a NEW version, never a mutation of an old one.

Why this piece earns its place

Immutable has to be enforced by the store, not agreed in a code review. Write-once keys, object versioning, a checksum verified on load — otherwise the day someone re-uploads to fix a packaging bug is the day every rollback target quietly points at different bytes than the run that was evaluated, and nothing tells you. Split what you keep, too: the version row is small, queryable metadata and belongs in a database you can index by model, stage and time; the weights are large blobs and belong in object storage keyed by version id. The reason is not that a listing would read them — every serious engine stores large values out of line — it is that blobs ride along in every backup, every restore, every replica stream and every table rewrite, so the cheapest thing in the registry ends up hostage to the largest. The consequence teams hit is storage. Every training run registers a version and most are never promoted, so the set grows with no obvious owner. Retention has to be stage-aware rather than age-based: dev versions can be collected aggressively, while a version that ever served production traffic carries a floor — the longer of the audit period you have committed to and the window in which it is still a plausible rollback target. Both are finite. Writing keep production versions forever is how a storage problem becomes a permanent one.

What the new pieces do

Model Registrygateway
The source of truth for every model version, its lineage, and which stage (dev/staging/prod) it currently occupies.
Versioned Artifactsdata
Immutable storage for every model version ever registered, tagged with exactly what training data, code commit, and hyperparameters produced it.

Step 3 · Staged promotion

Dev → staging → production, not a straight line

A newly-registered model version exists. Should it be eligible to serve production traffic immediately?

ML EngineerProduction ServingModel RegistryVersioned Artifacts
The system as it stands at this step. · swipe to pan the diagram

A model just finished training and got registered. Should it be immediately eligible to serve production traffic?

  1. Training succeeding (the loss converged, the process completed) says nothing about whether the model actually performs well enough, safely enough, and without regression relative to what's currently serving real users.

  2. Staged promotion means a model has to earn its way to production through explicit checkpoints, rather than a successful training run alone being sufficient — this mirrors how a code change progresses through environments before reaching users.

  3. A single offline holdout test, while useful (see Step 4), doesn't capture everything a staged rollout with real production-adjacent validation (Step 5) can catch — one static check isn't equivalent to a staged process.

Give every model version an explicit stage tracked by the registry — dev, staging, production — and require an explicit, gated promotion action to advance between them. A model doesn't default into serving production traffic just by existing; it has to be deliberately promoted, with checks along the way.

Why this piece earns its place

Hold a stage as a slot rather than a label: for a given model name, exactly one version occupies production at a time, and promoting is a swap. That constraint is what lets serving ask one question instead of running a search, and it is what makes "what was production during that bad week" answerable later — provided you keep the history of the slot and not only its current value, which is the column most teams discover they never wrote. The decision that actually bites is when serving resolves the slot. Resolve it once at process start and a promotion does nothing until someone restarts the fleet, so the registry holds an intent that disagrees with what is answering requests — the deploy-shaped problem this step exists to end. Resolve it live and the change lands on every replica at once, which is what you want in one direction and not the other: on the way down, the whole fleet should leave a bad version together; on the way up, a candidate should reach users gradually. So put the rollout on promotion and let a reversal skip it. Skip the ladder entirely for a model nobody serves live — a batch scorer has one stage that matters, the one the job reads — but keep the explicit promotion, because that is what dates the change.

Step 4 · Automated evaluation gate

Block promotion on regression, automatically

Before a candidate model can be promoted, it should be validated against the currently-serving model. Should that validation be a manual, ad-hoc judgment call each time?

Model Registryversions, stagesVersioned Artifactsweights + lineageEval Gateautomated, fails closed
New in this step: Eval Gate.

A candidate model needs validation before promotion. Manual judgment call, or something else?

  1. Manual review doesn't scale as promotion frequency grows, is inconsistent between reviewers, and — critically — is easy to skip under deadline pressure, which is exactly when a regression is most likely to slip through unnoticed.

  2. Automating the gate makes validation consistent, fast, and impossible to accidentally skip — every promotion attempt runs the same objective comparison against the current production baseline, and a regression is blocked by default rather than relying on someone remembering to check.

  3. Evaluating a candidate's absolute metrics without comparing to the CURRENT production baseline misses the actual question that matters for a promotion decision: is this better or worse than what's live right now?

Add an automated Eval Gate: every promotion attempt runs the candidate against a held-out evaluation set, computes the same metrics for the candidate and the current production baseline, and blocks promotion by default on any meaningful regression. This makes validation consistent and impossible to skip under pressure.

Why this piece earns its place

The gate is only as good as its evaluation set, and the way that set goes bad is invisible. If it is refreshed from the same pipeline that produces training data, a candidate trained on overlapping rows scores beautifully and is promoted into a regression. Freeze the set and version it apart from the training data, and you get a second benefit: verdicts stay comparable over months, because a candidate measured in March and one measured in September only mean anything against each other if the ruler did not move. Then define the word doing the work in the solution. Meaningful has to be a measured number, not zero: two evaluations of the same model differ — sampling, nondeterministic kernels, batch effects — so a threshold set at nothing blocks on noise, and a gate that blocks on noise is a gate people learn routes around. Measure the run-to-run spread of one version against one set, set the block threshold outside that band, and set it per metric, because accuracy and tail latency do not wobble by the same amount. Make an override a recorded action with a named reason attached to the version, so the gate bends somewhere the next audit can see rather than being widened in a config file.

  • same metricscandidate vs current baseline
  • default: blockedon a meaningful regression
  • 0 manual stepsto skip

What the new pieces do

Eval Gateservice
Runs a candidate against held-out data and compares it to the currently-serving model — blocks promotion on a meaningful regression.

Step 5 · Shadow and canary deployment

Offline metrics aren't the whole story

The candidate passed the Eval Gate against a held-out dataset. Is that sufficient confidence to fully replace the production model right now?

Production ServingModel RegistryEval GateShadow / Canary
New in this step: Shadow / Canary. · swipe to pan the diagram

A candidate passed offline evaluation against a held-out set. Is that enough to fully deploy it to 100% of production traffic?

  1. A held-out evaluation set is necessarily a SNAPSHOT of past data — real production traffic can have a meaningfully different distribution (new user behavior, edge cases the eval set didn't capture, seasonal shifts), which offline metrics alone can't reveal.

  2. Shadow/canary deployment exposes the candidate to actual current production traffic — the real distribution, not a static historical snapshot — while limiting user-facing risk to zero (shadow) or a small, bounded slice (canary). This catches real-world behavior gaps that a held-out offline eval set, by definition, can't.

  3. Re-running the identical offline evaluation against the identical static dataset produces the identical result — it can't reveal anything about how the model behaves on data it hasn't been evaluated against, which is exactly the gap shadow/canary deployment closes.

Run the candidate through Shadow or Canary deployment before full promotion: shadow mode mirrors live traffic to the candidate with results never shown to users (zero risk, real data); canary mode actually serves a small percentage of real traffic (bounded risk, real user-facing signal). Only after this real-world validation does the candidate earn full production promotion.

Why this piece earns its place

Shadow and canary are not two strengths of the same thing, and knowing why you need both is the answer to the follow-up. Shadow measures everything that lives inside the prediction — agreement with the current model, latency, error rate, how the output distribution shifts. It cannot measure anything that requires a user to act on the prediction, because nobody ever sees it: a ranking the shadow model produces is never clicked, so click-through, conversion and escalation rate are unmeasurable there by construction. Those need a canary, where a real slice of traffic is actually served. Shadow is also not free. Mirroring means paying for a second full inference on every mirrored request, so mirror a sample rather than the whole stream unless the model is cheap. And the shadow path has to be side-effect free. If inference writes anywhere — a cache, a computed feature, an event stream that feeds the next training set — the mirrored copy writes too, and you have let an unpromoted model edit production state, or train its own successor on its output.

What the new pieces do

Shadow / Canaryservice
Runs the candidate on live (or a slice of live) traffic without it actually serving responses users depend on — catches what offline eval can't.

Step 6 · Rollback

A version pointer, not a fire drill

The new model is fully promoted and serving. Something's wrong in production that wasn't caught earlier. How fast can you get back to the previous version?

Production Servingregistry-governedVersioned Artifactsweights + lineage
New in this step: Versioned Artifacts → Production Serving.

Because every past version is immutably stored in Versioned Artifacts, rollback is simply repointing production serving at the previous version id — no retraining, no re-deriving what the old model was, because it was never overwritten in the first place. This is the direct payoff of the immutability decision made back in Step 2.

Why this piece earns its place

Repointing serving is the fast part, and it is not the recovery. What a rollback restores is the model; what it cannot restore is whatever the bad version already caused while it was live. So the number worth managing here is detection time, not switch-over time — the pointer move costs the same either way, and the size of the correction scales only with how long the regression ran. A rollback plan that names a version id and stops is half a plan; the other half names who owns the correction and how its scope gets bounded. The second trap is that rollback compatibility expires. The old version expects the input schema, feature set and preprocessing it was trained against, and all of those keep moving after it stops serving, so the version you assumed was your floor quietly stops loading — or worse, loads and scores confidently on a field that has since been renamed. Pin the input contract with the version rather than only the weights, and rehearse a rollback on a schedule instead of discovering the mismatch mid-incident. Then mark the version you rolled away from as bad in the registry: move only the pointer and nothing stops the next promotion walking straight back into it.

Step 7 · Reproducibility and audit

Prove exactly what produced this model

Months later, someone needs to know exactly how the currently-serving model was produced — for debugging an issue, or for a compliance/audit requirement.

ML EngineerProduction ServingModel RegistryVersioned ArtifactsEval GateShadow / Canary
The system as it stands at this step. · swipe to pan the diagram

Because every version's lineage (training data version, code commit, hyperparameters) was recorded at registration time, the registry can answer exactly what produced any given model version — and, with the same data and code pinned by hash/version rather than "latest," the training run can genuinely be reproduced if needed. This lineage record is often as important operationally as the model weights themselves.

Why this piece earns its place

Be precise about what reproducibility promises, because bit-exact retraining is mostly not achievable and claiming it will get you caught. Nondeterministic GPU kernels, library and driver versions, data ordering, thread counts — the same inputs rarely produce identical weights. So pin the container image by digest alongside the data and the code, and state the claim as a model statistically equivalent on the frozen evaluation set, not one that hashes the same. Lineage is also only worth its weakest reference. Pinning the code commit while recording the training data as a query against a live warehouse table is decorative: the commit is fixed, the table is not, and the run is unreproducible through the one input that matters most. Materialize a snapshot, or use a store that can address a table as of a timestamp. And the lineage has to be written by the training job itself, from the values it actually ran with, at registration — the field a human fills in afterwards is the field that is wrong, and it is wrong in the direction of what someone believed they had configured.

Step 8 · Production drift

Valid at deploy time isn't valid forever

A model passed every gate and is performing well in production. Six months later, has anything changed?

Production ServingModel RegistryEval GateShadow / CanaryDrift Monitor
New in this step: Drift Monitor. · swipe to pan the diagram

A model passed every validation gate at deploy time. Does that guarantee it stays good indefinitely?

  1. Real-world data distributions shift over time (user behavior changes, new categories of input appear, the world itself changes) — a model trained and validated on data from six months ago can quietly degrade as the live distribution drifts away from what it learned, with no code or model change required to trigger it.

  2. Deploy-time validation only proves the model was good AT THAT MOMENT, against data available then. A Drift Monitor watching the SERVING model's real-world performance continuously is what catches gradual degradation that no amount of pre-deployment gating could have predicted.

  3. Model performance can degrade with ZERO code or model changes at all, purely because the real-world data distribution shifted — waiting for a code change to trigger re-validation misses this entirely.

Add a Drift Monitor watching the currently-serving model's real-world performance continuously — not just at deploy time. When production metrics degrade meaningfully from their validated baseline, it flags the registry, triggering either an automatic rollback to the previous known-good version (Step 6) or an alert for human review, depending on the severity and the organization's risk tolerance for that model.

Why this piece earns its place

The awkward part of watching a model in production is that the metric you care about needs labels, and labels are late or absent. A fraud call is settled by a chargeback weeks out; a recommendation may never get ground truth at all. So monitoring is layered by latency, and the layers say different things. Input distributions and the model’s own output distribution are available on the next request, and they are proxies — they tell you the world moved, not that you got worse. Accuracy against real outcomes is the measurement that decides, and it lands on a delay you design around: a fast path that alerts on the proxies, a slower one that confirms or clears each alert once labels arrive. That delay is the part people skip, and it sets two things. It sets the shortest regression you can detect at all, because a window narrower than the label lag will always look empty. And it forces the baseline to be recomputed on the same delay, from a cohort as fully settled as the one being judged — otherwise you compare a finished month against an unfinished week and call the gap drift. Compare against what the canary actually produced rather than the offline score, since that is the last measurement taken on real traffic.

What the new pieces do

Drift Monitordata
Watches the SERVING model's real-world performance continuously — because a model validated at deploy time can still degrade later as real data shifts.

You did it

You just designed a model registry.

ML EngineerProduction ServingModel RegistryVersioned ArtifactsEval GateShadow / CanaryDrift Monitor
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • A model artifact alone is nearly meaningless without lineage — exactly what data, code, and hyperparameters produced it.
  • Immutable versioning is the foundation everything else depends on: rollback, reproducibility, and safe evaluation all require past versions to be permanent.
  • Models progress through explicit, gated stages (dev/staging/production) rather than defaulting to serving traffic just by existing.
  • An automated Eval Gate compares a candidate against the current production baseline and blocks promotion by default on a meaningful regression — removing reliance on manual review.
  • Shadow/canary deployment validates against real, current production traffic, catching what a static offline evaluation set structurally can't.
  • Rollback is a version-pointer change, not a fire drill, because past versions were never overwritten.
  • Ongoing production drift monitoring catches degradation that happens with zero code or model change, purely from real-world data shifting over time.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. How is this different from a Fine-Tuning Pipeline design?

    A fine-tuning pipeline is about the TRAINING process itself — data preparation, the training loop, hyperparameter choices, checkpointing during a run. A model registry starts at the point training already finished: what happens to that resulting artifact between "training completed" and "safely serving real production traffic," including versioning, validation, staged rollout, and rollback. A real MLOps stack needs both, but they solve genuinely different problems — one produces a model, the other governs how a produced model gets safely deployed and operated.

  2. Why does the eval gate fail CLOSED (block promotion) rather than fail OPEN (allow it) when the gate itself is unavailable?

    This is the same reasoning as a spam filter or a security permission check failing closed rather than open: the cost of the two failure directions is asymmetric. If the gate fails closed and is briefly unavailable, the cost is a delayed promotion — annoying, but safe. If it failed open, ANY promotion attempted during that window would skip validation entirely, potentially deploying a regressed or broken model straight to production with zero safety check. Given that asymmetry, blocking (and simply queuing the promotion until the gate is healthy again) is the correct default for a safety-critical check.

  3. Could shadow deployment alone replace the offline Eval Gate, since it validates against real traffic anyway?

    They catch different things and both matter. The offline Eval Gate is fast, cheap, and catches OBVIOUS regressions immediately, before the candidate is exposed to any real traffic at all — useful as a fast first filter that doesn't require a live deployment window. Shadow deployment is slower (it needs real traffic flowing in over some period to gather a meaningful sample) and catches SUBTLER real-world behavior gaps offline eval can't. Skipping the offline gate and going straight to shadow would mean every candidate, including obviously broken ones, consumes shadow infrastructure and time before being caught — the offline gate is a cheap first filter that makes the more expensive shadow stage more efficient, not redundant with it.

  4. How would you decide whether a drift alert should trigger an automatic rollback versus just alerting a human?

    This is fundamentally a risk-tolerance decision that depends on the specific model and how confidently the drift signal indicates a genuine, actionable regression versus noise. For a model where a wrong prediction has severe consequences (say, a fraud-detection threshold) and where drift metrics are well-understood and reliable, automatic rollback on a clear signal minimizes exposure time. For a model where drift signals are noisier or the cost of an UNNECESSARY rollback (interrupting a model that was actually fine) is itself costly, alerting a human for judgment before acting is more appropriate. Many real systems set this per-model, often starting with alert-only until enough operational history builds confidence in the drift signal's reliability for automatic action.

  5. What's the risk of only tracking lineage for the MODEL, but not for the evaluation/comparison decision that promoted it?

    If the registry records only "version 5 was produced by this data/code/hyperparameters" but not "version 5 was promoted because it beat version 4 by X% on these specific metrics, evaluated on this specific held-out set," you lose the ability to later audit WHY a promotion decision was made, not just what the resulting model was. This matters for debugging (was the eval set itself flawed?), for compliance in regulated domains (proving the promotion decision followed the required process), and for retrospective analysis (did our gate's threshold turn out to be well-calibrated?). A complete registry tracks lineage for both the artifact AND the promotion decisions made about it.

Check yourself — the answers, and why

Nine steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. A model artifact (just the weights file) without lineage is problematic because…

    The metadata around a model is often as operationally important as the model itself.

  2. Rollback is fast in this design because…

    Immutability (decided early) is what turns rollback from a fire drill into a version-pointer change.

  3. The automated Eval Gate blocks promotion by default on regression mainly because…

    Automating with a default-blocked outcome removes human-reliability as a failure mode.

  4. Shadow/canary deployment exists because…

    Real traffic can differ meaningfully from a static evaluation set — shadow/canary validates against what the model will actually face.

  5. Ongoing production drift monitoring is necessary because…

    A one-time deploy-time check only proves the model was good then — drift monitoring catches gradual real-world degradation.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Register: every trained model becomes an immutable version, tagged with the exact data, code commit, and hyperparameters that produced it.
  • Promote: advance a version through explicit stages — dev → staging → production — via a deliberate, gated promotion action.
  • Evaluate: an automated gate compares a candidate against the current production baseline and blocks on a meaningful regression.
  • Shadow / canary: run a candidate on real traffic (mirrored, or a small served slice) before it fully replaces the live model.
  • Roll back: repoint production serving at any previous version id — instantly, with no retraining.

The qualities that shape everything

Each one names the mechanism that buys it.

A foundation everything else can rely on
Immutable versioning — a new training run is always a NEW version, never a mutation of an old one — is what makes rollback, reproducibility, and safe comparison possible.
A safety check that can never be skipped
An automated Eval Gate runs on every promotion, comparing candidate vs current baseline, and blocks by default on regression — removing manual review as a failure mode.
No regression reaches 100% of users
Shadow/canary deployment exposes the candidate to real, current traffic at zero (shadow) or bounded (canary) risk before it earns full promotion.
Roll back a bad model in seconds
Every past version is immutably stored, so rollback is a version-pointer change — the old model was never overwritten, so there is nothing to reconstruct.
Reproduce and audit any model months later
Lineage pins the exact data version, code commit, and hyperparameters by hash — not "latest" — at registration time.
Catch degradation that happens after deploy
A Drift Monitor watches the serving model’s real-world metrics continuously and flags the registry when they fall from their validated baseline.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Staged, gated promotion over serve as soon as training succeeds

A converged loss says nothing about whether a model beats what’s live. Staging makes a version earn production through explicit checkpoints instead of defaulting in just by existing.

An automated Eval Gate over a manual review each promotion

Manual review is inconsistent between reviewers and easiest to skip under deadline pressure — exactly when a regression slips through. Automating it, default-blocked, removes human reliability as a failure mode.

Shadow / canary before full rollout over trusting the offline held-out score

A held-out set is a fixed snapshot of past data; real traffic is a shifting distribution. Shadow/canary validates against the world the model will actually face, at zero or bounded user risk.

Continuous drift monitoring over a one-time deploy-time check

Distributions shift after deploy with zero code change, so a model validated six months ago quietly degrades. Ongoing monitoring catches what no pre-deploy gate could have predicted.

Fail closed when the gate is down over fail open and promote anyway

The costs are asymmetric: fail-closed only delays a promotion; fail-open ships an unvalidated model straight to production. Queue the promotion until the gate is healthy again.

The answer, out loud

What a strong answer to “Design a Model Registry (MLOps)” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Scope it before I draw anything

    Let me scope this first, because it’s easy to drift into training. I’m not designing how a model gets trained — I’m designing what happens to the artifact between the moment training finished and the moment it’s safely answering real production traffic. The questions I want this system to answer at any time are: which exact model version is serving right now, can we prove it’s the one we validated, and how fast can we get back to the last one that was good. If those three are unanswerable, not much else about the platform matters.

  2. 3–8 min

    Start from what people actually do

    So I’d start from the naive version, because it’s what teams really do: train a model, copy the weights to wherever serving reads from, done. It works, right up until something goes wrong. Then nobody can say what data, what code, what hyperparameters produced the thing that’s live, and there’s no reliable record of what to roll back to. A model artifact on its own is nearly meaningless — the metadata around it is as operationally important as the weights. And the blind spot gets worse with every extra model and every extra engineer, so that’s the gap everything after this closes.

    Built in step 1: A file gets deployed
  3. 8–14 min

    Immutable versions, tagged with their lineage

    The foundation is a registry, and the call I’d defend is making the version record the primary object, not the file. Training hands me weights; the registry holds the conditions that produced them, and that record is what I query. Then I’d hold it write-once — a property of the storage, not a rule in a review, because a rule survives right up until someone patches a version in place under pressure. Everything after this is a claim about the past: that what we compared against is still what we compared against, that what we go back to is what was running. Those are only claims if the past can be edited.

    Built in step 2: Every model, tagged with what produced it
  4. 14–19 min

    Make a model earn production

    Next: a registered version shouldn’t be able to serve production traffic just because it exists. I’d give every version a stage the registry owns — dev, staging, production — and make moving between them a deliberate action somebody takes. The part I care about is the default. If existing makes you eligible, every check after this one is opt-out, and opt-out checks get skipped precisely when things are busy. If the default is not eligible, they’re the only road forward and nobody has to remember them. A stage also turns promotion into an event with a time and a person on it, so I can ask later what was live during a bad stretch.

    Built in step 3: Dev → staging → production, not a straight line
  5. 19–25 min

    Automate the check that must never be skipped

    That promotion is the thing I’d gate, and I’d automate the gate rather than schedule a review, because a check only counts if it runs on every attempt. Better isn’t one number, though — a candidate that wins on accuracy but loses on tail latency is a regression in a dimension nobody gated on, so I’d say up front which metrics can trade against each other and which are hard floors. And I’d have it fail closed: if the gate is unavailable, promotions queue. The consequence I’d own rather than hide is that the gate’s uptime is now release velocity, so it gets operated like a production service.

    Built in step 4: Block promotion on regression, automatically
  6. 25–31 min

    Offline metrics aren’t the whole story

    Passing that gate still isn’t enough to hand the model all the traffic, so first I’d run the candidate in shadow, where it sees mirrored traffic and nobody sees its output, or as a canary serving a small, bounded percentage for real. What I’d want to be pushed on is how long. It isn’t a clock, it’s a sample — long enough is when traffic has covered the cases I’m worried about, which often means waiting out a weekday and a weekend. And I’d write down what passing looks like before starting, or the stage decays into watching a dashboard until somebody feels comfortable.

    Built in step 5: Offline metrics aren't the whole story
  7. 31–37 min

    The way back out, and the paper trail

    Then the part I’d want to be asked about: getting back. Because every past version is immutably stored, rollback is repointing production serving at a previous version id. There’s nothing to reconstruct and nothing to retrain, because the old model was never overwritten — that’s the immutability decision paying off. The same record answers the audit question months later: what data, what commit, what hyperparameters, all pinned to specific versions rather than to latest, so the lineage still means something after the dataset has moved on.

    Built in step 6: A version pointer, not a fire drill
  8. 37–43 min

    It can go bad with nobody touching it

    Last piece. A model can clear every gate, deploy cleanly and get worse with nobody touching it, so I’d watch it in production and write what that watching learns back against the version in the registry. Rolling back to the last known-good version only means something if known-good is a record rather than a memory — once each version carries how it behaved on live traffic, the target is a choice between measured options. The other thing I’d resist is making retraining the reflex. Sometimes the world moved; sometimes an upstream pipeline changed and retraining bakes that in.

    Built in step 8: Valid at deploy time isn't valid forever
  9. 43–45 min

    The trade-off, and what I’d do next

    The honest trade-off is velocity. Every gate here makes shipping a model slower and adds infrastructure somebody has to keep healthy, and on a low-risk model that’s a real cost — I’d expect to tier it, running the full gauntlet where a bad prediction is expensive and something lighter where it isn’t. With more time I’d put permissions and an approval trail on the promotion action itself, so the registry records who promoted what and when, not only which version won. And I’d work through what happens when one endpoint serves several models, or an ensemble, because then production isn’t a single pointer and most of what I’ve described quietly assumes it is.

What this teaches

Learn AI system design by building the platform that versions, evaluates, promotes and can instantly roll back trained ML models — not training itself (see Fine-Tuning Pipeline) or feature serving (see Feature Store), but the governance layer between "a model finished training" and "a model is safely serving production traffic." An interactive guide covering why an artifact alone (just weights) is meaningless without lineage, staged promotion through gated environments, automated evaluation gates that fail closed, shadow/canary deployment because offline metrics aren't sufficient on their own, instant rollback via immutable versioning, and production drift monitoring.

Key takeaways

  • A model artifact alone is nearly meaningless without lineage — exactly what data, code, and hyperparameters produced it.
  • Immutable versioning is the foundation everything else depends on: rollback, reproducibility, and safe evaluation all require past versions to be permanent.
  • Models progress through explicit, gated stages (dev/staging/production) rather than defaulting to serving traffic just by existing.
  • An automated Eval Gate compares a candidate against the current production baseline and blocks promotion by default on a meaningful regression — removing reliance on manual review.
  • Shadow/canary deployment validates against real, current production traffic, catching what a static offline evaluation set structurally can't.
  • Rollback is a version-pointer change, not a fire drill, because past versions were never overwritten.
  • Ongoing production drift monitoring catches degradation that happens with zero code or model change, purely from real-world data shifting over time.

Concepts covered

  • The gap between "it trained" and "it's safely serving"
  • A file gets deployed
  • Every model, tagged with what produced it
  • Dev → staging → production, not a straight line
  • Block promotion on regression, automatically
  • Offline metrics aren't the whole story
  • A version pointer, not a fire drill
  • Prove exactly what produced this model
  • Valid at deploy time isn't valid forever
RUN IT YOURSELF

The passing eval that no longer belongs to this model

A real registry round: seven candidate versions, each trained for real and each carrying the eval result some job wrote on the day it ran. It runs the promotion gate two ways — one that accepts any passing eval, one that requires the eval to have been run on the bytes under that version id now and on the eval set in force now — and prints the production accuracy each gate ships. Change EVALSET_BUMP_DAY to 5, so every record was measured on today’s set, and watch the gap fall to the part the re-upload alone accounts for.

HOW TO READ THE CODE — 5 IDEAS
  1. Both gates see the same candidates, the same scorer and the same threshold. The only difference is one edge — whether a result is evidence about a (digest, eval set) pair or about a name (step 7).
  2. Without it the gate promotes v2 on 0.958, a number that was true on day 10 against an eval set retired on day 40. Its bytes score 0.667 on traffic arriving now — below the model it replaced.
  3. The same missing edge throws away v3, which the old ruler scored at 0.656 and the current one at 0.826. A stale ruler does not only wave regressions through; it blocks the best candidate you have (step 4).
  4. v6 is the other failure: its record was measured on bytes 21e75568, and a day-46 "packaging fix" re-upload left 2ca85415 under that same id. Same name, same passing number: 0.836 claimed, 0.712 when those new bytes are re-scored on today’s set, 0.698 on real traffic (step 2).
  5. Net: 0.161 production accuracy between the two promotions, and the bound gate does not win by refusing to ship — it re-runs the 5 unbound records and promotes on the fresh number. The gap stays positive for every EVALSET_BUMP_DAY up to day 60 and is never negative; push it past 60, where the swap has not happened yet, and it reads 0.000 — v6 is still unbound, but it loses under both gates. Unbound evidence costs nothing until it wins.
CPython · WebAssembly
built so rolling back a bad model is a version pointer, not a fire drill — make the calls, kill the eval gate, run the gauntlet.
Finished this one? 0 / 65 AI System Designs done
NextDesign Cursor Closest AI system design to this one

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs