~/projects/splits
Web + mobile project
Splits
Keeping track of the bills we share.
I built Splits to handle the bills that come around every month: recognize recurring expenses, apply agreed split rules, and keep track of who owes what. The Next.js web app and React Native/Expo mobile app share the same calculation logic and backend.
- Recurring bills
- Shared calculations
- Web & mobile
- Next.js client
- Web
- React Native / Expo
- Mobile
- sandbox data
- Demo
$ why I built this
The spreadsheet that wanted to be an app.
Rent and utilities come around every month, but keeping track of who paid what can still turn into the same round of receipts and messages. I wanted a way to keep those shared bills together and reuse the split rules.
The idea was simple enough: recognize a recurring bill, apply the agreed split, and keep a running balance. Building it meant dealing with the less obvious parts too, like rounding and what happens when a transaction sync runs twice.
$ how it works
From transactions to shared bills.
I built web and mobile clients around three connected pieces: importing transactions, applying split rules, and keeping a running balance.
01 Import sandbox transactions
The Plaid integration imports simulated bank transactions. That gives the recurring-bill logic transaction histories to work with without using real financial data.
02 Set a split rule
A rule says who shares a bill and how to divide it—for example, splitting rent equally with a roommate. Splits suggests rules for recurring bills and applies the rules you’ve set up when matching transactions arrive.
03 Track the balance
Each shared expense adds entries to the ledger and updates who owes what. Splits tracks those balances; it does not hold or transfer money.
$ try it
Explore the web demo.
Try shared expenses, split rules, and balance tracking in the web app using demo data. Bank-data examples use Plaid's sandbox; no real money moves through Splits. The demo is for exploring the project, not managing real finances.
The React Native/Expo app is part of the same implementation. You can explore the web version directly here.
Try the demo$ under the hood
The parts that took more thought.
The basic idea was simple. Building it meant dealing with changing transaction data, syncs that needed to run again, and three versions of the expense math.
Recognizing recurring bills
Merchant names and amounts aren't always identical from month to month. I normalize the names, group amounts within a 15% tolerance, and look for recurrence across at least two months before suggesting a split rule.
Sharing the calculations
The expense math had grown into three implementations. I brought it into one TypeScript package and added tests for splits, rounding, balances, and debt simplification.
Coordinating sync jobs
I added a lock for each bank connection so two sync jobs do not process it at the same time. Each sync retrieves changes since the last saved position.
When transactions change mid-sync
Plaid returns transactions a page at a time. Sometimes the data changes before all the pages have been fetched. I added recovery that restarts retrieval when that happens.
Handling retries
A sync can fail after it has already added bills. Retrying it shouldn’t add them again. I write the bill entries before saving the sync’s progress, and use database-enforced idempotency keys to prevent duplicate entries on a retry.
Using one backend
I moved the mobile backend into Next.js API routes. Web and mobile now share Supabase authentication and PostgreSQL storage, removing a separate backend deployment.
Lessons from building Splits
What I learned.
Getting the expense math right was only part of the work. It also had to stay consistent across clients and across syncs that overlap, change halfway through, or need to run again.
Once those workflows were complete, I stopped expanding the feature set. Keeping the web and mobile apps on a shared backend gave me fewer services to run and maintain, and room to move on to other work.
