Back to home page

build

Tally

What left your bank and what you actually spent, both answered, for two people sharing money.

what it is

A shared expense tracker for two people, built on one ledger and four numbers, none of which are stored. We used Money Manager. It is a good app, and it can only answer one question: what left the card. That number is close to useless once you share money with someone. Half the groceries are not mine, the joint pot spends money that already came out of both of us, and a settle up is not spending at all, it is money moving from one pocket to another, so counting it double-counts a dinner we already logged. Every tracker I looked at either flattened everything into one number that quietly means nothing, or bolted on a separate split-with-friends mode keeping its own set of books, so you reconcile two apps by hand.

what is unique

Merchant memory is the actual reason to build your own. Every save teaches Tally what a merchant usually means for us: type three letters of Sainsbury's next time and it fills in the category, the account, and the split, without asking. The split is remembered per merchant, so groceries are 50/50 and the gym never is, and after a couple of weeks the app stops asking. Knowing what not to predict matters just as much: it never fills in the amount, because categories are stable and amounts are not, and a wrong prefilled number is worse than an empty field since it can be saved without being read. The moment you override anything by hand, autofill stops touching that entry, and whatever you chose becomes the new memory on save. Predictions are a starting point that has to be cheap to reject, never a default that fights you. Underneath, moving money is not spending money: topping up the joint account is a transfer because the money has not left the household, settling up is a transfer that deliberately touches no category because the category was already correct at the point of sale, and joint-pot spending creates no debt because it was shared on the way in.

how it was built

React 19, TypeScript, Vite, Tailwind CSS v4, Supabase (Postgres, magic link auth, row-level security), installable PWA, deployed on Vercel, in its own Postgres schema with schema-scoped grants. Mobile viewport only, by decision rather than omission. A transaction stores only what happened: the full amount charged, which account it left, who paid, and one row per person for their share. Cash out, real share of consumption, household spend counted once, and the running balance between us are all computed at read time, so a number that looks wrong can be traced to the rows that produced it. Merchant matching runs through a pure, dependency-free normalisation and ranking module, unit tested on casing, punctuation, both apostrophe characters, deduplication of rival spellings, and ranking so exact beats prefix beats mid-word, because "Sainsbury's", "sainsburys" and "SAINSBURYS" have to be one memory, not four. Row-level security is scoped to household membership, resolved through a single security-definer function rather than an inline subquery, which is what stops the members policy recursing into itself. Scope was written down before any code, including the non-goals: no bank linking, no receipt OCR, no AI coach, no notifications.

gtm angle

Tally is a personalization product with no model in it, which is exactly why it was useful to build. The hard questions had nothing to do with prediction quality. What is worth learning about someone, and what should you never presume? How do you earn enough behaviour to be useful when on day one you know nothing? How does a person correct a wrong guess in one gesture without being punished for it? And what makes someone open the app tomorrow, given that a tracker with gaps is worse than no tracker, because it produces confident numbers that are wrong? Those are the same four questions that decide whether an AI feature ships or gets switched off, and they get answered in the interface long before they get answered in the model. Building Tally forced me to answer them somewhere the cost of getting it wrong is immediate and personal: if it is slow or it guesses badly, the two of us stop using it that week.

live site

Visit the live site
Back to home page