Technical

Double-Entry Bookkeeping for Developers

Double-entry bookkeeping is a distributed consensus mechanism. Every transaction is a commit that must balance. Here's how it works in code.

William Murray · 13 February 2026 · 3 min read
Double-entry bookkeeping T-accounts showing debits and credits balancing across Bank, Revenue, and Expense accounts with the accounting equation

Double-entry bookkeeping has survived 500 years because it’s essentially a distributed consensus mechanism. Every transaction is a commit that must balance. No partial states. No eventual consistency. Just maths.

If you’ve ever built a database with foreign key constraints, you already understand the principle. Double-entry is a constraint: every pound that enters one account must leave another. The books always balance, or the system rejects the transaction.

The Core Rule

Every financial event records at least two entries: a debit and a credit. The total debits must equal the total credits. Always.

Total Debits = Total Credits

That’s it. That’s the invariant. Everything else is implementation detail.

Debits and Credits Are Not Good and Bad

This is where most explanations lose developers. “Debit” doesn’t mean “money out” and “credit” doesn’t mean “money in.” They’re directions, not judgements.

Think of it like this: every account has a normal balance — the side that makes it increase.

Account TypeNormal BalanceIncreases WithDecreases With
AssetDebitDebitCredit
LiabilityCreditCreditDebit
EquityCreditCreditDebit
RevenueCreditCreditDebit
ExpenseDebitDebitCredit

Your bank account is an asset. When you receive payment, the bank balance goes up — that’s a debit to the bank account. When you pay for hosting, the bank balance goes down — that’s a credit to the bank account.

The hosting cost is an expense. Expenses increase with debits. So the same transaction debits the hosting expense account.

Two entries. Equal and opposite. The books balance.

A Real Example

A client pays your £5,000 invoice:

DEBIT   Bank Account (Asset)       £5,000   ← Bank balance goes up
CREDIT  Revenue (Income)           £5,000   ← Revenue goes up

Both sides equal £5,000. The transaction is valid.

Now you pay £89 for AWS hosting:

DEBIT   Hosting (Expense)          £89      ← Expense goes up
CREDIT  Bank Account (Asset)       £89      ← Bank balance goes down

Again, both sides balance.

The Developer Analogy: Git Commits

If you use git, you already think this way:

Git ConceptAccounting Equivalent
RepositoryGeneral Ledger
CommitTransaction (journal entry)
DiffDebit/Credit entries
git logTransaction history
git bisectAudit trail
Merge conflictUnbalanced transaction

A git commit that only modifies one file is suspicious. A transaction that only has one entry is invalid. Both systems enforce consistency through structure.

What This Looks Like in an API

Here’s how SpeyBooks represents a double-entry transaction:

{
  "success": true,
  "data": {
    "id": "txn_891",
    "date": "2026-02-01",
    "description": "Client payment - February consultancy",
    "lines": [
      {
        "id": "line_203",
        "accountId": "acc_1200",
        "accountName": "Bank Account",
        "debit": 500000,
        "credit": 0
      },
      {
        "id": "line_204",
        "accountId": "acc_4000",
        "accountName": "Consultancy Revenue",
        "debit": 0,
        "credit": 500000
      }
    ],
    "totalDebit": 500000,
    "totalCredit": 500000
  }
}

Notice the amounts are integers — 500000 means £5,000.00. We store everything in pence to avoid floating-point errors. More on that in Why We Store Money in Pence.

The API enforces the invariant at the database level. If totalDebit !== totalCredit, the transaction is rejected. No partial writes. No cleanup jobs.

The Accounting Equation

Every balance sheet in the world satisfies one equation:

Assets = Liabilities + Equity

Revenue increases equity. Expenses decrease it. Expand it out:

Assets + Expenses = Liabilities + Equity + Revenue

Both sides of this equation are always equal. That’s what “the books balance” means. Double-entry bookkeeping enforces this structurally — not through reconciliation, not through batch jobs, but through the fundamental design of every transaction.

Why You Should Care

If you’re building anything that handles money — invoicing, billing, payments, subscriptions — you need double-entry. Not because accountants say so, but because single-entry systems break.

Single-entry is like writing code without types. It works until it doesn’t, and when it fails, you can’t trace what went wrong. Double-entry gives you an audit trail baked into the data structure itself.

Every transaction tells you: where the money came from, where it went, and when. That’s not just accounting — that’s good systems design.

Key Takeaways
  • Every transaction has at least two entries: a debit and a credit
  • Total debits must always equal total credits — this is the invariant
  • Debits and credits are directions, not good/bad — their effect depends on the account type
  • Double-entry is a constraint system, like foreign keys or type safety
  • The accounting equation (Assets = Liabilities + Equity) is always satisfied

Early access for developers.

SpeyBooks is in soft launch. We're inviting a small group of developers to help shape API-first accounting for the UK.

90-day free trial. Proper double-entry. No tracking.