CODEXA.Technologiez
All insightsEngineering

Why your fintech product needs a ledger

Codexa Engineering · Sep 22, 2026 · 3 min read

Almost every financial product starts the same way: a `balance` column on the accounts table, updated when money moves. It works in testing, it works in the first month, and then it quietly stops being true.

Why a stored balance goes wrong

A number you update is a number that can be updated twice, or half-updated, or updated by two requests that both read the old value first.

None of these produce an error. They produce a figure that is wrong by a small amount, discovered weeks later during reconciliation, with no record of which transaction caused it.

What a ledger actually is

An append-only record of movements, where a balance is derived rather than stored. Nothing is ever edited; a correction is another entry.

  • Every movement has two sides — money leaves one place and arrives at another, and the two must agree.
  • Entries are immutable. A mistake is corrected by a reversing entry, not by an update.
  • A balance is the sum of entries up to a point in time, which means you can compute it for any past date.

That last property is the one that matters when someone asks why an account showed a particular figure on a particular morning. With a ledger you answer in a query. Without one, you reconstruct it by hand and hope.

Concurrency is a daily event, not an edge case

Two requests touching the same account in the same instant is normal traffic, not a stress-test scenario. The usual fixes — a transaction, a row lock, an optimistic version check — all work. Choosing none of them also works, right up until it does not.

The pattern to avoid is read-modify-write across a network boundary. By the time your service has read the balance, decided it is sufficient, and written the new one, another request has done the same.

Idempotency is not optional

Every endpoint that moves money will eventually be called twice for the same intent: a retry after a timeout, a double-tap, a webhook redelivered.

The defence is a client-generated idempotency key stored with the entry. A repeat arrives, you recognise the key, you return the original result rather than creating a second movement. This is cheap to add on day one and awkward to retrofit once there is live data.

Building a ledger versus adopting one

If you are moving other people's money and need to answer for balances, you need a ledger — the only question is whose.

Payment providers cover the rails well but rarely model your product's own accounting: fees, holds, splits, refunds against partial captures. The decision worth making early is which system is the source of truth, because changing your mind later means a migration with real money in flight.

For how this lands in a budget alongside KYC and compliance work, see our fintech practice and the SaaS cost breakdown.

Do we need a ledger if we use Stripe?

Usually yes. Stripe records what happened on its own rails — charges, refunds, payouts — but it does not model your product's accounting: fees you split, balances you hold on behalf of users, credits you issue, or money owed between parties in your system. If you need to answer for a user-facing balance, you need your own record of it. Stripe is the rail, not the ledger.

How much does a ledger add to a build?

Designed in from the start, a double-entry ledger with idempotent money movement is typically a few weeks of work on top of the product itself. Retrofitted onto a system that stored balances as mutable numbers, it is a migration with live money in flight, and it costs several times more.

Enjoyed this? Let's work together.

Start a project