Double-Entry Ledger Design: The 500-Year-Old System That Powers Banking
SD
ScaleDojo
May 11, 2026
2 min read539 words
Why Stripe Cannot Use a Simple Balance Column
Imagine storing user balances as a single column: UPDATE accounts SET balance = balance - 100 WHERE id = 42. Now imagine a race condition updates it twice. Or a bug subtracts without adding. Or an audit asks 'where did $50,000 go last Tuesday?' You have no history, no trail, no proof. Every fintech company from Stripe to Square to Robinhood uses double-entry ledgers instead - a system invented by Luca Pacioli in 1494 that makes financial errors mathematically impossible.
The Core Rule: Every Transaction Balances
Double-Entry: Every Transaction Creates TWO Entries
Alice sends $100 to Bob:
+------------------+--------+---------+
| Account | Debit | Credit |
+------------------+--------+---------+
| Alice's Wallet | $100 | | <- money leaves
| Bob's Wallet | | $100 | <- money arrives
+------------------+--------+---------+
| TOTAL | $100 | $100 | <- MUST balance
+------------------+--------+---------+
The invariant: SUM(all debits) = SUM(all credits)
If they do not balance, something is WRONG.
You will discover it immediately, not months later.
Alice's balance = SUM(credits) - SUM(debits) = -$100
Bob's balance = SUM(credits) - SUM(debits) = +$100
System total = $0 (money was moved, not created)
The Schema: Append-Only Journal
-- Accounts (the buckets money lives in)
CREATE TABLE accounts (
id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(20) NOT NULL, -- ASSET, LIABILITY, REVENUE, EXPENSE
currency CHAR(3) NOT NULL DEFAULT 'USD',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Journal entries (IMMUTABLE - never update, never delete)
CREATE TABLE journal_entries (
id UUID PRIMARY KEY,
transaction_id UUID NOT NULL, -- groups debit + credit together
account_id UUID NOT NULL REFERENCES accounts(id),
amount BIGINT NOT NULL, -- cents, not dollars (avoid floats!)
direction VARCHAR(6) NOT NULL CHECK (direction IN ('DEBIT', 'CREDIT')),
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Balance is DERIVED, never stored:
-- SELECT SUM(CASE WHEN direction = 'CREDIT' THEN amount ELSE -amount END)
-- FROM journal_entries WHERE account_id = ?;
Why Immutability Is Non-Negotiable
Correcting Errors (the right way):
Original charge (wrong amount):
Entry #1001: Debit Customer $150, Credit Merchant $150
DO NOT: UPDATE journal_entries SET amount = 100 WHERE id = 1001
DO NOT: DELETE FROM journal_entries WHERE id = 1001
INSTEAD: Create a REVERSAL entry:
Entry #1002: Credit Customer $150, Debit Merchant $150 (undo)
Entry #1003: Debit Customer $100, Credit Merchant $100 (correct)
Result: Full audit trail preserved
Auditor can see: original charge, reversal, correction
Timestamps prove when each action happened
No data was destroyed
Account Types in Practice
Account Types for a Payment Platform (like Stripe):
Type Examples Increases With
---------- ---------------------- ----------------
ASSET Cash, Bank Account, DEBIT
Accounts Receivable
LIABILITY Customer deposits, CREDIT
Accounts Payable,
Platform float
REVENUE Processing fees, CREDIT
Subscription income
EXPENSE Refund costs, DEBIT
Infrastructure costs
Stripe example: When you pay a merchant $100 with 2.9% fee:
Debit: Customer bank $100.00 (asset decreases)
Credit: Merchant account $97.10 (liability increases)
Credit: Stripe revenue $2.90 (revenue increases)
Interview Tip
When designing a financial system, say: 'I would use a double-entry ledger where every transaction creates balanced debit and credit journal entries. Entries are immutable and append-only - errors are corrected with reversal entries, never updates or deletes. Account balances are derived by summing the journal (SUM of credits minus SUM of debits), never stored directly. Amounts are stored as integers in the smallest currency unit (cents) to avoid floating-point errors. This gives us a complete, auditable, tamper-evident financial history.'
<Architecture overview
/blockquote>
Key Takeaway
Double-entry ledgers make financial inconsistencies mathematically impossible - every transaction must balance. Journal entries are immutable records. Account balances are derived by summing history, not stored directly. This 500-year-old system is still the gold standard for financial system correctness.
No comments yet. Be the first to start the thread!
Related Articles
Enjoyed this content?
Your support keeps us creating free resources
We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.
$
One-time payment via Stripe. ScaleDojo account required.