"Ledgerly" — Shared Expenses Without the Arguments

A shared-expense tracker for flatmates and trips. Balances are derived from an append-only ledger rather than stored, settlements are idempotent so a double-tap never double-pays, and the "who owes whom" graph is simplified to the fewest possible transfers. (Sample project — replace with your own.)
- My role
- Solo — data model, settlement logic, interface
- Built for
- Sample case study · replace with your own
“When you can store the evidence instead of the conclusion, store the evidence.”
The Brief
Shared expenses sound simple until two people add a bill at the same moment, a request is retried, or someone settles up while a third person edits an old expense. Balances stored as numbers drift.
The second problem is human: with six people and forty expenses, "who pays whom" has dozens of valid answers, and most of them involve too many transfers.
The Approach
Ledgerly stores an append-only ledger of expenses and payments and derives every balance from it. Nothing is edited in place; a correction is a new row that reverses the old one.
Settling up runs a greedy matching over net balances — largest creditor against largest debtor — which keeps the number of transfers at or near the minimum for typical groups.
How It Works
- 01Clientadds an expense with an idempotency key
- 02APIvalidates, writes one ledger row
- 03PostgreSQLunique key makes retries harmless
- 04Balancessummed from the ledger
- 05Settlefewest-transfers plan
Key Decisions
Idempotency keys on every write
A unique index on the client-generated key turns double-taps and retries into a single row.
Money as integers
Amounts are stored in the smallest currency unit, with explicit rounding rules for uneven splits.
A rebuildable summary
A materialised balance table speeds up large groups and can be dropped and rebuilt from the ledger at any time.
- Sample case study — replace with a real one.
- Multi-currency groups convert at entry time and do not revalue later.