Immutable ledger design

The previous lesson built the accounting model. This one covers the rule that makes it trustworthy — financial records are written once and never changed, and "fixing a mistake" means adding a new correcting entry, not editing the old one.

Advanced

3 min read

The rule, stated as bluntly as real systems state it

A genuinely common pattern in production ledger schemas is a comment directly on the model definition: "IMMUTABLE — never UPDATE or DELETE." Not a suggestion, not a code-review preference — a constraint the schema itself is documented as depending on. Once a JournalEntry and its JournalLine rows are written and committed, nothing in the application is expected to ever run an UPDATE or DELETE against them again.

Why mutability is the actual danger, not just messy

If a financial record can be edited after the fact, the ledger stops being able to answer "what did we believe was true at any point in the past" — which is the entire reason a ledger exists in the first place. An editable ledger:

  • Can't reliably support an audit (an external accountant, a regulator, or a fraud investigation asking "show me exactly what changed and when" has no reliable trail if changes overwrite history instead of adding to it)
  • Makes any cached/derived report (a monthly revenue total, a customer's running balance) potentially wrong retroactively, with no record of why it changed
  • Removes the strongest evidence that a system wasn't tampered with — a chain of appended, never-altered entries is genuinely harder to falsify convincingly than a table of currently-true values with no history

Corrections are new entries, always

Mistake discovered: a $200 charge was miscategorized as Revenue when
it should have been recorded as a deposit (a liability, since it's
owed back later)

WRONG:  UPDATE the original JournalEntry's lines to fix the category

RIGHT:  1. Post a new entry reversing the original
           (undoing exactly what the mistaken entry did)
        2. Post a second new entry recording it correctly

Result: three entries exist in the ledger forever — the original
mistake, its reversal, and the correction. All three remain visible.
Nothing was erased; the ledger shows the full, honest history of
what actually happened, including the mistake.

This might look wasteful compared to "just fix the wrong number," but the honesty is the point: a ledger that only ever shows the currently-believed-correct state has thrown away the information needed to answer "was this ever wrong, and if so, how did we find out and fix it" — a question audits and investigations ask constantly.

All three entries stay in the ledger forever — nothing is erased.

The self-referencing reversal pattern

A common, concrete way to model "this entry corrects that one" without ever mutating the original:

JournalEntry
  id
  reversedById    -- points to the entry that later reversed THIS one,
                     if any (nullable — most entries are never reversed)
  reversesEntryId -- points to the entry THIS one reverses, if this
                     entry is itself a reversal (nullable)

A correction is simply a brand-new JournalEntry, linked back to the one it corrects via this self-relation — never a mutation of the original row. Querying "what's the real, current picture" becomes "look at all entries, follow the reversal links, and net them out" rather than "trust whatever the single latest edit says."

Where this connects back to everything earlier in this domain

  • Idempotency: an immutable ledger pairs naturally with idempotent webhook handling — if the same event is processed twice, the second attempt should recognize it already has a matching entry and post nothing new, rather than appending a duplicate.
  • Refunds/disputes: exactly the case worked through in the double-entry lesson — a refund's journal entry doesn't touch the original charge's entry, it stands beside it.
  • Reconciliation: an append-only ledger is far easier to reconcile against a gateway's own records, since "what changed since yesterday" is just "what new entries were appended since yesterday" — there's no risk that yesterday's numbers silently look different today for reasons hidden in an edit history nobody kept.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. A financial ledger schema is documented with the comment "IMMUTABLE — never UPDATE or DELETE" directly above the model. What does this actually mean in practice?

2. Why does an editable ledger fail to reliably support an audit?

3. A $200 charge was miscategorized in a posted JournalEntry. What's the correct fix?

4. How does a self-referencing reversedById/reversesEntryId pair on JournalEntry let a correction be modeled without mutating the original row?