Skip to content
AnithraSolutions
Fintech · 12 min

Double-entry ledgers for engineers who did not study accounting

Why a balance column is a bug, what immutability buys you at audit time, and how to model money movement so reconciliation becomes a query instead of a month-end fire drill.

Published 27 May 2026By the Anithra Solutions engineering team

If you are storing a user's balance in a column and updating it, you have a bug. Not a bug you have hit yet — a bug that is structurally present and will surface the first time two writes race, a job retries, or an auditor asks how a balance got to where it is.

Double-entry bookkeeping is five hundred years old and solves exactly this. It is worth twenty minutes of your time even if you never touch a financial system again.

The model in one paragraph

Money never appears or disappears; it moves between accounts. Every movement is a journal entry with at least two lines — one debit, one credit — and the lines must sum to zero. A balance is not stored; it is the sum of all lines touching that account. Journals are append-only. You never edit an entry; a correction is a new entry that reverses the old one.

What immutability buys you

  • Reconciliation becomes a query. Sum the ledger, compare to the bank, and any difference is a specific set of entries rather than a mystery.
  • Audit is free. 'Why is this balance 4,312?' has an answer that is a list of rows, each with a timestamp, a cause and a correlation ID.
  • Concurrency stops being terrifying. Appends do not conflict the way in-place updates do, and idempotency keys make retries safe by construction.
  • Bugs become visible instead of silent. A balance column absorbs an error. A ledger that must sum to zero refuses to.

The parts engineers get wrong

Signs. Debit and credit are not positive and negative — they are sides. Whether a debit increases or decreases an account depends on the account type. Get this wrong once and every report is subtly inverted. Write the account-type table down and test it.

Money types. Never use a float. Store minor units as an integer with an explicit currency, and make the type impossible to construct without both. Most rounding incidents we have investigated trace back to a division that lost a unit and a system that had no invariant to catch it.

Pending versus settled. A card authorisation is not a settlement. Model them as distinct entries against distinct accounts so an available balance and a cleared balance are both derivable rather than approximated.

A balance column is a cache. Treat it like one: derived, invalidatable, and never the source of truth.

Performance, honestly

The obvious objection is that summing every entry to read a balance does not scale. It does not, and you do not do that. You keep periodic snapshot balances — end of day, end of month — and sum forward from the most recent snapshot. Reads stay fast, the ledger stays authoritative, and a snapshot that disagrees with a recomputation is an alert rather than a silent corruption.

In Ledgerline the snapshot job runs continuously and recomputes a rolling window from scratch. It has caught three integration bugs in production that no test would have found, because the failure was in a partner's retry behaviour rather than in our code.

Working on this?

We run a paid two-week diagnostic that ends with an architecture record, a risk register and a costed plan — yours to keep either way.

Talk to an engineer

Let's find out whether we're a fit.

Thirty minutes with an engineer who has shipped this before — not a salesperson reading a deck. You leave with a straight answer about scope, cost and timeline, whether or not you work with us.

Prefer email? sales@anithrasolutions.com · We reply within one business day