If you can derive it, do not store it

Somewhere in most business systems there is a number nobody trusts. Stock on hand. Account balance. Points earned. Seats used. It is usually a column that code writes to, and by the second year it is wrong somewhere, and no one can say when it went wrong or why.

The fix is not better validation. It is to stop storing it.

Stored versus derived

A stored total is a claim. Something calculated it once and wrote it down, and from that instant it can drift from the events that produced it - a retry that applied twice, a transaction that half-failed, a well-meaning manual correction, a race between two requests.

A derived total is a question asked of the events themselves. It cannot drift, because there is nothing separate to drift from. Ask again and you get today's answer from today's facts.

What this looks like in practice

Rather than a quantity column, keep a table of movements: each one signed, immutable, carrying who did it, when, and against what reference. A warehouse's stock is the sum of the movements touching it. A correction is a new movement in the opposite direction, never an edit.

Three things follow immediately, and they are the real payoff:

  • Reconciliation stops being a task. The balance and its explanation are the same object, so they cannot disagree. There is no monthly exercise in finding out where the number went wrong.
  • "Why do we have eleven?" is always answerable. Not usually. Always. The full history is the number.
  • Undo becomes safe. Appending the inverse leaves the audit trail intact, where deleting the original destroys the evidence of what happened.

There is a small working version of this on the clinic case study - post a movement and watch the totals recompute from the log, with nothing anywhere storing a quantity.

The objection, and the honest answer

"Summing every movement on every read will be slow."

Usually it is not. A few thousand movements is nothing to a database with the right index, and most systems that worry about this have far fewer rows than their authors imagine.

When it genuinely does become slow, the answer is a periodic snapshot - a checkpoint row saying "as of this date the balance was X" - and you derive forward from the most recent one. That is a cache, and it is safe precisely because it is rebuildable: delete every snapshot and the system still knows all the right answers. The moment you cannot rebuild it from the events, you have gone back to storing, and you have the original problem again with extra machinery.

Where not to use it

This is for things that are counted and must be explainable. It is unnecessary for a display preference, a draft, or anything where the current value is the only thing anyone will ever care about. Event-sourcing a user's chosen theme is not rigour, it is ceremony.

Deciding which of the two a given number is happens during discovery, not during the build - which is part of why the first price anyone quotes is usually wrong. That is the note about what a platform actually costs.