24 April 2025 / Deterministic systems

If software produces a score, the rules need a version

A score looks objective, but there's usually a changing set of weights, thresholds and assumptions behind it. I want the rule version stored with every result so someone can reproduce it, explain it and tell whether the answer changed or the evidence did.

Hands using a digital caliper to measure a machined metal part.
Photo: Hans Westbeek (opens in a new tab)

Eligibility checks, risk bands, lead ratings, assessments and internal priority lists all tend to end in one number. That number usually sits on top of parsing, defaults, transformations and a few decisions somebody made, and if any of those change, the same input can come out with a different result. If all you've kept is the final score, you can't explain how the software got there. So I'd give the rule set an immutable version that points to the exact weights, thresholds, question mappings, formulas and default behaviour used at the time, kept in source control with a plain-language spec beside it.

The version should change whenever a result could change. Fixing a spelling mistake in a label doesn't need one. Moving a threshold from 60 to 65 does, and so does changing a weight, changing how missing values are handled or mapping a new answer to an existing category. Don't reuse a number after a rollback either. If 1.4 gets withdrawn and fixed, release 1.5, because a version whose contents quietly changed is much harder to reason about later.

Keeping the original submission isn't always enough to reproduce a score. Say someone enters a date, picks a few options and leaves a field blank. The application might parse the date in a particular timezone, map the options to internal codes and fill the blank with a default, and the scorer only ever saw those interpreted values. I'd store something like this with each result:

  • the raw submission reference and its version
  • the normalised input values the scorer used
  • the scoring rule version
  • the application or scorer build identifier
  • the calculation time and the relevant effective date
  • the final score, band and reason codes

Reason codes save a lot of guesswork, because they record which rules contributed instead of leaving you to work backwards from a number. You'll also want to replay a historical result without touching production data, which means packaging the scoring logic so an old version can load by itself, and preserving the lookup tables, mappings and any external values (an exchange rate, say) it relied on. "Latest" reference data makes old decisions impossible to reproduce. Sensitive inputs still belong in their proper stores with access controls, linked rather than copied into every log.

When a score changes, the first thing to ask is what changed. The person's evidence might have changed, someone might have corrected an input, the rules might have changed or the old code might have been wrong. Record those as separate events, so the interface can say "your result changed because answer 7 was corrected" or "the same answers were assessed under rules effective from 1 May". Don't silently recalculate old results when new rules go out. If the organisation wants to reassess them, run a migration that creates new result records and keeps the old ones.

To see where you're at, pick one completed result and try to reproduce it in a clean environment from the original inputs and the recorded rule version, then compare every intermediate value with what was stored. Anything missing belongs in the versioned rules or the result metadata. That's why the rules need a version: when the software produces a score, you should be able to say which rules produced it and whether a later change came from the rules or from the evidence. Once that reproduction works for one result, I'd automate it and run it against a small historical sample before each scoring release, so I'd find out before a new rule version goes out if any older score can no longer be reproduced.

Got a take on this?

Share it on X. I read the replies.

All blogs