← All posts
accountingevent-sourcingbudget

A budget tracker is an append-only accounting ledger

July 22, 2026 · 7 min read

Storing a running balance is a bug waiting to happen. Modeling a budget the way accountants model money — immutable entries, derived totals, reversals instead of deletes — makes it auditable and impossible to corrupt.

The naive way to build a budget tracker is to store a remaining number on each envelope and add or subtract from it on every expense. It is also the way to end up with a balance that nobody can explain — off by a few dollars, with no record of which write went wrong. Real accounting systems solved this centuries ago, and we borrowed their answer directly.

In our Budget Tracker an envelope is an account, and every expense is an immutable entry in an append-only ledger. Nothing is ever mutated in place.

Totals are derived, never stored

There is no spent column and no remaining column. Both are pure functions of the entries:

spent(envelope)     = Σ entry.amount  where entry.envelope = envelope
remaining(envelope) = envelope.budget - spent(envelope)

Because the totals are computed from the log, they can never silently disagree with it. The ledger is the single source of truth; everything else is a view over it. If a number ever looks wrong, the entries that produced it are right there to audit.

Corrections are reversals, not deletes

You never delete an entry — deleting destroys the audit trail and makes "why did this change?" unanswerable. To undo a $40 expense you post a reversing entry of −$40. The original stays; the ledger now tells the whole story, including the mistake and its correction.

2026-07-18   Groceries      +$40.00
2026-07-19   Groceries      −$40.00   (reversal of the entry above)
                            ───────
             net effect       $0.00

This is exactly how double-entry bookkeeping treats an error, and it is why an accountant can trust a ledger they didn't write themselves.

Idempotency at the boundary

The same principle protects us from the messy edges of distributed systems. When money moves for real — a subscription payment through Stripe — the entry is keyed by the provider's event id, so a webhook delivered twice posts the ledger entry once. Append-only plus a dedupe key is a remarkably robust way to stay correct when the network retries on you.

Why this matters beyond budgets

An append-only ledger with derived balances is the backbone of billing systems, inventory, wallet and credits features, and any place where "how did we get to this number?" has to have an answer. A personal budget tracker is simply the smallest honest demo of the accounting archetype: immutable entries, totals as a fold over the log, reversals instead of deletes, idempotent writes at the boundary.

If you are staring at a balance column that drifts and a support queue asking why, this is the pattern that ends the conversation — and the kind of modeling we bring to client systems.

A budget tracker is an append-only accounting ledger — stop.procrastin.ar