The single discipline that lets us answer the only question stakeholders actually ask: "why did the number change?"
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.
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.
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.
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