Before you start
Splitwise-style expense splitting is a common design interview problem. Small enough for one class, complex enough to reveal whether you can factor cleanly.
The signals
Two are baked into the requirements:
- Multiple split types (equal, exact, percent) → Strategy pattern. Each split algorithm is its own class.
- Balance queries must be O(1) → keep a running balance map; don't walk the expense log every time someone asks.
Your design.md should name both.
The balance-representation decision
You have two natural ways to store the balance sheet:
Option A — single-direction storage:
balances[("alice", "bob")] = 50 # alice owes bob 50 (alphabetical)
Good: half the memory. Antisymmetric by construction (the other direction is just the negative). No "did I forget to update both sides" bug.
Bad: query lookup has to sort the pair every time.
Option B — double-entry (write both sides):
balances[("alice", "bob")] = 50
balances[("bob", "alice")] = -50
Good: direct O(1) lookup either way.
Bad: two writes per update; risk of drift if a bug forgets to update the paired entry.
Both are fine. Pin your choice in design.md.
The split-type strategy shape
Naive:
def add_expense(...):
if split_type == "equal":
...
elif split_type == "exact":
...
Strategy:
class EqualSplit(SplitStrategy):
def compute(self, amount, participants, shares):
return {p: amount // len(participants) for p in participants}
class ExactSplit(SplitStrategy):
def compute(self, amount, participants, shares):
if sum(shares.values()) != amount:
raise ValueError(...)
return shares
# Adding "by_ratio" = one new class, no changes to Splitwise.
This is OCP applied to the split algorithm. Adding a new type becomes one new class.
The rules discipline
Three rules matter:
- balance(a, b) == -balance(b, a) — one side is the negative of the other.
- Sum across all users = 0 — money doesn't appear or disappear.
- Every split sums to the full expense amount — you didn't lose or invent rupees while dividing it up.
Each is enforceable in ONE place. Name them.
The extension question
An interviewer will ask "how would you add a settle-up feature that minimizes transactions?" That's not in MVP scope, but your design.md's Trade-offs section can note whether your representation makes that easy (yes, if you keep raw pairwise balances; harder, if you only stored net amounts).