An expense splitter
Deceptively fiddly maths, and everyone needs one after a holiday.
Build time ~4 hrs
"Who owes what" looks like addition and isn't. Unequal shares, people who joined halfway, and rounding that must not lose a penny make this a genuinely interesting small project.
1. Get the model right
An expense has a payer, an amount, a date, and a set of people it's split between. Splits can be equal, by share, or by exact amount. Everything else derives from that list.
Don't store balances. Compute them from the expenses every time — a stored balance that disagrees with the expense list is the bug you'll never find.
Write the settlement calculation. Input: a list of expenses, each with payer, amount in minor units, and a split (equal / by shares / exact amounts). Output: the minimum set of payments that settles everyone up. Requirements: - Integer minor units throughout. No floats anywhere. - An equal split of 100p between 3 people must total exactly 100p. Say how you distribute the remainder and make it deterministic. - Minimise the number of transfers, not just net everyone against one person. Show me a worked example with an awkward number, and one where two people paid for overlapping subsets.
2. Rounding is the whole problem
£10 split three ways is 333, 333 and 334 pence. Somebody gets the extra penny, and your code must decide who deterministically — otherwise the total doesn't match and people notice immediately.
Assign remainders in a fixed order and document it. "The payer absorbs the remainder" is a fine rule; having no rule is not.
3. Minimise the transfers
Naively, everyone pays everyone. In practice a group of six can usually settle in three or four payments. Netting balances and matching largest debtor to largest creditor gets close to optimal and is easy to explain — which matters, because people want to understand why they're paying that person.
4. Make it work without accounts
The highest-friction thing you could add. A shareable link with the group in the URL or in local storage gets people using it in seconds.
Show the workings. "You owe Sam £14.50" gets queried; a breakdown of which expenses produced it doesn't.