Product engineering · Personal finance · Case study
Productising a spreadsheet: building Tally, an offline-first budgeting app
Tally is a mobile-first, offline-first budgeting PWA we built: a monthly-budget spreadsheet turned into a real synced app with a constrained AI coach.
- Next.js 15 (App Router) + React 19 + TypeScript
- Supabase Postgres with RLS (London, eu-west-2)
- Drizzle query layer + SQL migrations
- Supabase Auth (email + Google OAuth)
- Offline-first PWA (Serwist) with an IndexedDB sync outbox
- Anthropic Claude via the Vercel AI SDK (constrained coach)
- Stripe (subscriptions, coded)
- Tailwind CSS 4, Resend, Sentry, Vercel (London/lhr1 target)
Millions of people run their money on a spreadsheet. It has the right shape for a monthly budget: income at the top, expenses by category, a running balance at the bottom. What it does not have is legs. You cannot log a coffee at the till, you cannot share one honest source of truth with a partner, and there is no sync, so the sheet you carefully build on a Sunday is out of date by Tuesday. Tally is that spreadsheet, productised: the same month-by-month view, turned into a real, offline-first, synced app that lives on your phone.
This is a build story, not a results story. Tally is designed and built but not yet launched, so there are no user or revenue numbers to report, and we will not invent any. What follows is what we set out to make and what we actually engineered.
The problem
Budgeting tools tend to pull in two unhelpful directions. At one end sit powerful envelope systems that ask you to learn a methodology before you can log a purchase. At the other sit bank-aggregation apps that connect to your accounts, which is convenient until you think about who is reading your transactions, and which are largely built around a United States banking model. When one popular free tool shut down, a lot of people went back to a spreadsheet, because the spreadsheet at least did exactly what they told it to.
Tally is built for that person. Manual entry is a deliberate choice, not a missing feature: no account linking, no third party quietly ingesting your spending. The job is to keep the discipline of a monthly sheet while removing its friction, so the running balance is always current and always in your pocket.
The approach
We kept the mental model people already trust. The core screen is a month, addressed directly as a URL, showing what you planned against what has actually happened, with the balance rolling up as you go. Everything else is built to protect two things: your data and your ability to use the app when the signal drops.
Privacy is enforced in the database, not just promised in the interface. Every table is protected by row-level security, so a user can only ever read and write their own rows, and the write path is gated server-side so the paywall cannot be stepped around from the client. Offline is treated as the normal case, not an error state, because the moment you most want to log a spend is standing at a counter with one bar of signal.
What we built
The app that exists today is substantial. It covers real budgeting: a month view with planned-versus-actual roll-up and inline planning, transactions, categories with one level of sub-categories, accounts, recurring rules, and funding tags so money earmarked for a specific purpose is tracked as such. Authentication runs on email and password or Google sign-in. There are proper account controls, including data export and account deletion, wired as real endpoints rather than a promise.
Two pieces are worth calling out because they are where the engineering went.
An offline-first sync engine. Tally is a full progressive web app with its own service worker, but the real work is an offline outbox held in IndexedDB. Actions taken without a connection are queued locally and replayed when the device comes back online, and every queued action carries its own identifier so a replay can never double-apply a change. The result is an app you can use on the Underground and trust to reconcile itself cleanly afterwards.
A constrained money coach. Tally includes an opt-in assistant, built on Claude through the Vercel AI SDK, that can talk about your budget because it is grounded in your own numbers. It is deliberately fenced in: it refuses to give regulated financial advice, it does not lecture or shame, and it is prompted to offer one concrete next action rather than a wall of text. The same component is packaged so it can be embedded elsewhere, which opens a distribution route beyond the app itself.
Around all of this sits a full marketing and SEO site, with pricing, persona landing pages, comparison pages and a blog, built on the same codebase so the front door and the product share one system.
The stack
Tally is a Next.js 15 App Router application in strict TypeScript, on Supabase Postgres pinned to London, with row-level security as the security model and a Drizzle query layer over SQL migrations that are the source of truth for the schema. Auth is Supabase, payments are Stripe, the assistant is Claude through the Vercel AI SDK, and the progressive web app layer is Serwist with the custom IndexedDB outbox described above. Styling is Tailwind 4, transactional email is Resend, monitoring is Sentry, and hosting is intended for Vercel in the London region.
Where it stands
Tally is pre-launch. It has been built and typechecked locally, but it has not yet been deployed to a live environment, it has no users, and the name itself is still being finalised pending a trademark check. We are being explicit about that because the whole point of the product is honesty about money, and the case study should hold to the same standard.
What it demonstrates is the shape of what we build: a real, non-trivial product, engineered properly from the database up, with offline resilience and a carefully bounded use of AI, ready to move to launch rather than mocked up to look finished.