Versioning code, data, and models
Hookโ
A fraud model has been live for three months when an auditor asks a simple question: which data was it trained on? The code is right there in Git, tagged and clean. The 8 GB training extract is not โ it was regenerated last week, in place, so the file that trained the live model no longer exists. You have perfect history of one third of what produced the model and none of the other two thirds.
Conceptโ
A trained model is the product of three artifacts, and reproducibility requires versioning all three, not just the one Git is good at.
Code is the easy one: Git versions it well because source files are small and text, so Git can store every version and show you line-by-line diffs. The trap is assuming Git's job is done there.
Data is where Git stops helping. Git was built for small text files; commit a
few gigabytes of Parquet and the repository balloons, clones crawl, and diffs are
meaningless. The technique that scales is content-addressed storage: instead
of putting the big file in Git, you compute a content hash of it โ a short
fingerprint derived from the file's exact bytes โ and commit that fingerprint. The
real bytes live once in cheap object storage, keyed by their hash; Git holds only
the pointer. This is exactly how tools like DVC work, and it echoes the lakehouse
rule you may know from data engineering: the raw zone is immutable. You never
overwrite orders.parquet in place โ you write a new version and let its hash
distinguish it from the old one.
A content hash has one property that makes all of this work: it is a deterministic function of the bytes. Identical bytes always produce the identical hash; change a single byte and the hash changes completely and unpredictably. So a hash is both an identity ("this exact dataset") and an integrity check ("these bytes have not been altered"). Comparing two 8 GB files becomes comparing two 64-character strings.
Models are artifacts too โ a trained model is a binary blob of weights, and like data it does not belong directly in Git. A versioned model is three things together: the artifact (the weights file), its metadata (the metric it scored, the framework version), and its lineage (the data hash and the code commit it came from). Store the model by its own content hash and record the lineage alongside it, and you can answer the auditor's question in one lookup: this model hash came from that data hash and that commit.
| Artifact | Versioned with | Why not Git directly |
|---|---|---|
| Code | Git commits | Nothing wrong โ Git is built for this |
| Data | Content hash + object storage | Large binaries bloat the repo and diffs |
| Model | Content hash + metadata | Weights are large binaries with lineage |
A content hash answers "are these bytes identical?", not "how do they differ?". For a dataset that is usually enough โ you version it and store both copies โ but it is why you keep semantic, human-readable diffs (schema, row counts) alongside the hash rather than expecting the hash to explain a change.
Worked exampleโ
Let me fingerprint a dataset and show both properties โ identity and integrity โ
with the standard library's hashlib. I will hash a tiny CSV held as bytes,
change one value, and watch the fingerprint move.
import hashlib
def content_hash(data):
"""Return the SHA-256 fingerprint of some bytes."""
return hashlib.sha256(data).hexdigest()
raw = b"user_id,label\n1,churn\n2,stay\n"
original = content_hash(raw)
print("original: ", original[:16])
# Re-hashing the same bytes gives the same fingerprint every time.
print("stable? ", content_hash(raw) == original)
# Change one label from 'stay' to 'stat' โ a single byte.
tampered = raw.replace(b"stay", b"stat")
print("tampered: ", content_hash(tampered)[:16])
print("identical?", content_hash(tampered) == original)
original: 92aa712e45147e8f
stable? True
tampered: 29031565955b5f70
identical? False
Two lessons in five lines. First, stable? is True: the same bytes hash to the
same value on every call, so the hash is a reliable name for this exact dataset โ
that is the identity property that lets you commit the hash instead of the file.
Second, flipping one character flipped the whole fingerprint from 92aa... to
2903..., with no resemblance between them โ that is the integrity property.
There is no such thing as a "small" change to a hash; any change at all is a
different dataset with a different name. In a real project you would hash the file
from disk in chunks rather than loading it into memory, but the guarantee is
identical.
Hands-onโ
Your turn, with model lineage instead of a bare dataset. The exercise below gives you a data hash and a code commit and asks you to build a small model-version record that fingerprints the model bytes and records its lineage, then verify that re-hashing unchanged model bytes reproduces the same model hash. It runs in Python Arena, so there is nothing to install.
Success criteria: your record ties the model hash to its data hash and commit, and hashing the same model bytes twice yields the same hash while altered bytes yield a different one. The Arena validates both automatically.
Recapโ
- You can name the three ML artifacts that need versioning โ code, data, and models โ and say why Git alone only handles the first well.
- You can explain content-addressed storage: commit a small content hash, store the large bytes once in object storage keyed by that hash.
- You can use a content hash as both identity ("this exact dataset") and integrity ("these bytes are unaltered").
- You can describe a versioned model as artifact plus metadata plus lineage back to a data hash and a commit.
Next up: experiment tracking. Now that you can fingerprint the inputs, you record each run's params, metrics, and those fingerprints together, so comparing runs tells you which inputs produced which result.
- Version all three ML artifacts: code (Git), data, and models. - Git handles code; large data and model binaries need content-addressed storage. - A content hash is both identity (this exact bytes) and integrity (unaltered). - A versioned model carries metadata and lineage back to its data hash and commit.