Field Notes
Modelling · Internal

Why we vintage the models (and stopped shipping Excel)

The single discipline that lets us answer the only question stakeholders actually ask: "why did the number change?"

Modelling practice · Project Kilimanjaro field notes

Every year a stakeholder asks the same question: “Why did the 2030 diesel forecast change versus last year?” A spreadsheet cannot answer it. It cannot decompose the change into “the assumptions moved” versus “the logic moved”. A vintaged code-plus-config model can — and that single capability is why we left one-off Excel deliverables behind.

The governing equation

Treat every formal output as a function of four governed terms:

Output = f( model_version, assumptions_version, data_snapshot, run_context )

If any of the four is unknown or uncontrolled, the output is not reproducible and not defensible. Reproducibility isn’t a nice-to-have; it is the precondition for an output being allowed into a board paper at all.

Vintages are immutable

A vintage directory, once shipped, never changes. Corrections open a new vintage. That is the only way the year-on-year “why did the number change” question stays answerable: we run new-logic on the old vintage and on the new vintage, and attribute the gap — assumption-delta here, logic-delta there.

The unit of reproducibility

Every output carries the triple (vintage, scenario, model_version) and a provenance.json recording the assumptions_commit, who triggered it, and when. A run that omits provenance is not eligible for a formal deliverable. Stubs raise errors rather than return fake values — missing pieces fail loudly, not silently.

The takeaway: we don’t embed numbers in model code. Assumptions live behind a provider interface and are read from the governed registry. That seam is what lets the central registry replace local config without touching a line of model logic.


Internal field note — sanitised for cross-team learning. More field notes