Skip to content
Provenza
All posts
ReproducibilityProtocol versioningLab operations

Version your experiments like software

Treat every protocol as a versioned artifact. Capture it once, diff every change, and tie each result to the exact protocol version and reagent lots that produced it.

DO

Daniel Okafor

Co-founder and CTO · July 16, 2026 · 6 min read

Software engineers stopped losing work to "it ran yesterday" about twenty years ago. The fix was not discipline or better memory. It was version control: every change to the code is a recorded, diffable, reversible event, and every build points back to the exact commit that produced it. When something breaks, you run git bisect and find the line that did it in minutes.

Wet-lab science runs on the opposite model. The protocol lives in a Word file called final_v3_ACTUAL.docx, a paper notebook, a postdoc's memory, and three Slack messages. When a result stops reproducing, there is no commit to point at. You are left comparing runs from memory, which is exactly the situation that leaves an estimated half of preclinical results unable to reproduce.

You can borrow the software model without becoming a software team. Here is how to do it in a real lab, this quarter.

Treat the protocol as the artifact, not the notebook entry

The core mental shift: the protocol is a versioned object with an identity, separate from any single run. A run is an execution of a specific version. This is the difference between a recipe and the dinner you cooked from it.

In practice that means three things. First, every protocol has a stable name and a version number that increments when the steps, reagents, or conditions change. Second, a version is immutable once a run has used it. You do not edit Western blot v4 after Tuesday's run; you create v5. Third, every result carries a pointer back to the exact version that produced it, so "which protocol made this figure" is a lookup, not an archaeology project.

Immutability is the part labs skip, and it is the part that makes everything else work. If a version can change after the fact, the pointer from a result back to its protocol becomes a lie. Freeze the version the moment it is used, and the record stays honest.

Diff every change so drift is visible

A version number alone tells you *that* something changed. A diff tells you *what*. This is where the software analogy earns its keep, because a good diff turns a vague "the assay feels off lately" into a specific line you can test.

Say your qPCR results shift between two runs. Instead of re-reading two full protocols, you compare the versions and see only what moved:

Protocol: RT-qPCR, GAPDH normalization
Compare: run 2026-06-30 (v7)  →  run 2026-07-14 (v9)

  Reverse transcription
-   incubation: 42 C for 30 min
+   incubation: 42 C for 15 min

  Reagents
-   SYBR mix: lot SG-2231 (opened 2026-05-02)
+   SYBR mix: lot SG-2410 (opened 2026-07-10)

  Cycling
    unchanged (40 cycles, 60 C anneal)

Two lines changed and one reagent lot rolled over. The RT incubation dropped from 30 to 15 minutes, and the SYBR mix is a new lot. Now you have two concrete hypotheses to test instead of a shrug. Maybe the shortened RT step cut cDNA yield. Maybe the new lot has different amplification efficiency. Either way, you know where to look, and you can rule one out with a single control run.

Without the diff, this investigation takes a week of re-running everything. With it, it takes an afternoon.

Tie results to versions and lots, automatically

The link between a result and its inputs has to be captured at the moment of work, not reconstructed later. Reconstruction is where errors enter, because nobody remembers on Friday which lot they opened on Monday.

This is the argument for capturing as you go rather than writing up afterward. When you log a step by voice or text at the bench, the protocol version and the reagent lots in use are stamped onto the run automatically. A month later, when a reviewer asks how a number was produced, the answer is already attached: this figure came from v9, using SYBR lot SG-2410 and primer lot P-118, run by this person on this date. Provenza is built around this capture-at-the-bench model precisely because after-the-fact write-ups are where the chain of custody breaks.

Lot tracking deserves its own line here. Reagent lots are the single most common hidden variable in a failed reproduction, and they are almost never in the written protocol. A protocol that says "add antibody" is not reproducible. A record that says "added anti-GAPDH, lot P-118" is. Treat lots as part of the run's identity, the same way a software build records its exact dependency versions.

A checklist you can adopt this week

You do not need new instruments or a six-month rollout. Start here:

  1. 1Name and number one protocol. Pick your most-run assay. Give it a stable name and mark the current version. Everything downstream hangs off this.
  2. 2Freeze on use. Agree as a team: once a run uses a version, that version is read-only. Changes go into a new version. No exceptions, because one exception breaks the guarantee.
  3. 3Record lots at the bench. For that one protocol, log the reagent lot for every entry, at the moment you use it. Not from the box later.
  4. 4Diff before you re-run. Before re-running a protocol that drifted, compare the last two versions and read the changed lines first. Test those before you test anything else.
  5. 5Attach the version to the result. When a number goes into a slide, a paper, or a report, write the protocol version and key lots next to it.

Do this for one protocol for two weeks. The first time a result drifts and you find the cause in an afternoon instead of a week, you will roll it out to the rest of the bench on your own. That is the whole pitch: reproducibility stops being a virtue you aspire to and becomes the default state of how the lab records its work.

Read next

The drift you never wrote down

Read