5.3.3 · D2MLOps & Deployment

Visual walkthrough — Model versioning and registries

1,966 words9 min readBack to topic

Step 1 — What is a trained model, really?

WHAT. Imagine you just finished training. On your disk sits a file — say model.pkl. It feels like "the model". But a model is really four ingredients that got cooked together plus the cooked result.

WHY. We must know what to save. If we only save the cooked result (the weights) and throw away the recipe, we can reload the meal but never re-cook it. So step one is simply to lay out every ingredient on the table.

PICTURE. The four raw ingredients on the left, the oven (training) in the middle, the finished weights on the right.

Figure — Model versioning and registries

If all four are pinned and training is deterministic, re-cooking gives you the byte-for-byte identical meal. That claim is the engine of everything below.


Step 2 — Why a filename is a terrible identity

WHAT. Suppose we name versions by filename: model_final.pkl, model_final_v2.pkl, model_final_REAL.pkl. Two different people, two different models, could pick the same name — and the same name could point at two different byte-blobs on two machines.

WHY. We need an identity with two magic properties: (1) same model ⇒ same ID, and (2) different model ⇒ different ID. A filename gives neither. We are hunting for something better, so first let's see the failure.

PICTURE. Two clearly different weight blobs both wearing the label model_final.pkl — a name collision.

Figure — Model versioning and registries

Step 3 — The tool that fixes it: a hash

WHAT. A hash function is a machine: you pour any pile of bytes in, and out comes a fixed-length fingerprint — for SHA-256, always 256 bits (64 hex characters), no matter if was 3 bytes or 3 gigabytes.

WHY THIS TOOL AND NOT ANOTHER? We asked in Step 2 for "same input → same ID, different input → different ID, and cheap to compare". A hash is literally designed to give exactly those three:

  • Deterministic — same bytes always → same fingerprint. (property 1 ✅)
  • Collision-resistant — finding two inputs with the same fingerprint is practically impossible. (property 2 ✅)
  • Fixed tiny size — compare 256 bits instead of gigabytes. (the "cheap" bonus ✅)

No simpler tool (checksum, filename, timestamp) gives all three at once. That is why the hash earns its place here.

PICTURE. The funnel: big variable-size blob → → short fixed fingerprint. Change one bit of input and the whole fingerprint scrambles.

Figure — Model versioning and registries

Step 4 — Two IDs, two jobs: fingerprint vs. label

WHAT. The hash is exact but unreadable (9c1f...ae). Humans want v3. So a registry keeps both: the immutable hash (machine identity) and a human-friendly monotonic counter v1, v2, v3, ... layered on top.

WHY. These are two different jobs. The hash answers "are these two artifacts identical?" with certainty. The label v3 answers "which one do I mean in conversation?". Git does the exact same split: a commit SHA (immutable, exact) vs. a branch name (mutable, meaningful). Never confuse the two.

PICTURE. Each artifact tagged with a long grey hash (welded on, unchangeable) and a short coloured label v1/v2/v3 (a sticker you can add).

Figure — Model versioning and registries

Step 5 — The registry: a name → ordered versions map

WHAT. A registry maps one model name to an ordered, append-only list of immutable versions. Separately, a few movable stage labels and aliases point at whichever version is currently playing that role.

WHY. This is the whole data structure that makes the parent note's four questions answerable — what/how-good/where/rollback — all become simple lookups on this map.

PICTURE. The list [v1, v2, v3, v4] welded in place; a floating Production pointer and a champion alias, each an arrow you can re-aim.

Figure — Model versioning and registries
registry["fraud-detector"] = [v1, v2, v3, v4]   # append-only, frozen
   Production ─▶ v4          # movable pointer
   champion   ─▶ v4          # movable alias

Compare with Experiment tracking and metadata logging (which run produced each version) and Model serving and deployment patterns (who reads the Production pointer at serve time).


Step 6 — Promotion is a pointer flip, not a rebuild

WHAT. You train v5 (AUC 0.93 on the same frozen eval set as v4's 0.91). To promote it: append v5 to the list, then move the champion/Production pointer from v4 to v5, and relabel v4 as Archived.

WHY. Notice what did not happen: no artifact moved, no file was copied, no rebuild. Because promotion is just re-aiming an arrow, it is atomic and instant. This is the payoff of Step 4's split — the mutable pointer is the only thing that moves.

PICTURE. Before/after: the Production arrow swinging from v4 to v5; the artifacts stay bolted in place.

Figure — Model versioning and registries

Step 7 — Rollback: the same arrow, swung back (edge case)

WHAT. v5 in Production starts emitting garbage (a drifted feature broke it). Rollback = set Production → v4 and mark v5 Archived.

WHY THIS IS THE CRUCIAL DEGENERATE CASE. Because v4 was immutable and never deleted (Step 4), its bytes are still sitting there. Rollback is therefore the same pointer flip as promotion, just backwards — seconds, not hours. Had we overwritten model_prod.pkl on each deploy, v4 would be gone and rollback would be impossible.

PICTURE. The Production arrow swinging back from a red, cracked v5 to the intact v4.

Figure — Model versioning and registries

Step 8 — Reproduce from lineage (walking the DAG backward)

WHAT. A regulator asks: "rebuild the model that denied this loan on 2024-03-01." Each version stores backward pointers to its inputs: experiment_run → data_version → code_commit → seed. That web of pointers is a DAG (directed acyclic graph — arrows all flow one way, no loops).

WHY. Because we pinned the full 4-tuple in Step 1, walking these pointers backward lands on frozen, immutable inputs. Re-running a deterministic pipeline on them regenerates byte-identical weights → whose hash must equal the stored ID. Match ⇒ audit passed.

PICTURE. The lineage DAG: v3 → {code=b12, data=s09, seed=42}, each leaf a pinned immutable node; a dashed "re-run" loop that recomputes the same hash 9c1....

Figure — Model versioning and registries

The one-picture summary

Figure — Model versioning and registries

The whole life of a version in one frame: four ingredientstrainhash into a fingerprintfile as an immutable versiona movable pointer picks Productionflip it forward (promote) or backward (rollback)walk the pointers back to reproduce.

Recall Feynman retelling — say it out loud in plain words

A model isn't just the finished file — it's four things cooked together: the code, the data, the knob settings, and the frozen coin-flips (seed). If I keep all four, I can re-cook the exact same meal.

Naming the meal by a filename is useless — two different meals can share a name. So instead I run the finished bytes through a hash, a machine that turns any pile of bytes into a short fingerprint. Same bytes → same fingerprint; change one bit → totally new fingerprint. That fingerprint is the model's true, unfakeable identity.

The registry is a shelf: for each model name it keeps a frozen, append-only list of versions — I can add but never edit or delete. On top of the shelf float little pointer arrows like "Production" and "champion". To promote a new model I don't move any file — I just re-aim the arrow at the new version. Instant. To roll back when the new one breaks, I aim the same arrow back at the old one — which still exists, because I never delete anything. Also instant.

And because every version remembers which code, data, and seed made it (a one-way web of pointers, a DAG), I can walk those pointers backward, re-run the deterministic pipeline, and get byte-identical weights whose fingerprint matches the stored one. That's how I prove to a regulator, years later, exactly which model made a decision.

Recall Test yourself
  • What are the four ingredients in the reproducibility tuple, and which one freezes randomness?
  • Give the three properties of a hash and match each to the demand it satisfies.
  • Why is promotion (and rollback) instant? What actually moves — and what must not have been deleted?
  • Why does walking the lineage DAG backward regenerate the same hash?