The Ledger and the Balance
The entries are the truth. The balance is a convenience you can always recompute. Store the balance and throw the entries away, and you have traded something you will not miss until the day you need it — the ability to answer questions you had not thought of yet.
Your loyalty system stores each customer's current points balance and keeps it up to date. Product asks a perfectly ordinary question: "how many points expired unused last quarter?" It matters — expiring points is why customers churn. How long until they have the number?
The balance store
StateOne row per customer. Every entry runs an UPDATE.
The event ledger
EventsOne row per entry, append-only. The balance is derived, never stored.
| Date | Entry | Δ | Running |
|---|---|---|---|
| Nothing posted yet. | |||
The reserve column
claim.current_reserve gets updated on every revision. The current position is
always right and the development history does not exist — which is the one thing
reserving actually needs.
Current salary
One field, overwritten at each review. Then someone asks about pay progression by cohort, or has to answer an equal-pay claim, and the answer is a spreadsheet somebody kept privately — if you are lucky.
Status fields
An order that is delivered tells you nothing about how long it sat in
picking. Every operational question about flow needs the transitions, and
a status column has thrown all of them away.
Events are not free
More rows, more storage, and every read becomes a computation you have to keep fast. Real systems keep both: the ledger as the source of truth and the balance as a cache you can always rebuild. The failure is keeping only the cache.
Ask of any important field in your systems: is this a ledger or a balance? If it is a balance and there is no ledger behind it, you can answer the questions you thought of when you built it — and no others, ever. The entries are cheap to keep and impossible to reconstruct. The balance you can always recompute; the history you cannot.