Building Divvy: A Simple Expense Splitter
I used Splitwise for years. It does the job, but it started getting in the way. Too many features I didn’t need, and at some point they added daily limits on how many expenses you could log without paying. For something as basic as “I paid for dinner, split it four ways,” that felt wrong.
So I built Divvy.
What it does
Divvy splits group expenses. You create a group, share a link, people join. Log an expense, pick who it’s split between, and Divvy tracks who owes what. When it’s time to settle up, it simplifies the debts so you make the fewest payments possible.
That’s it. No social features, no receipt scanning, no budgeting tools. Just splitting costs with a group of people.
The stack
Next.js with TypeScript, Turso for the database, Drizzle as the ORM. Passwordless login with email OTP so there are no passwords to manage. Stripe for the premium tier. Postmark for transactional emails. Deployed on Fly.io.
This is roughly the same stack I use for all my side projects now. I extracted a starter kit at some point because I kept setting up the same auth, payments, and email plumbing every time.
Joining without signing up
The one UX decision I cared about most was keeping the join flow short. Share a link, enter your email, verify with a one-time code, and you’re in the group. No passwords to set up, no profile to fill out. Just the minimum to get you splitting expenses.
The undo problem
Deleting an expense sounds simple until you think about what happens next. If four people split a dinner bill and someone deletes it, everyone’s balance changes. If someone joined the group after that expense was logged, the math is different again.
I ended up with a soft delete pattern. Expenses aren’t permanently removed. They get flagged as deleted with a timestamp. There’s a 5-minute window where you can undo the delete, and a 30-day window where deletion is allowed at all. Only the person who created the expense can delete it. Every delete and restore gets logged in an activity feed, and email notifications go out to the group.
The edge cases were the hard part. What happens when you delete an expense, a new member joins, and then you restore it? The balances need to account for the new member. Getting all those state transitions right took more time than building the core splitting logic.
Rebalancing
This is the part most people won’t notice, but it’s the thing I’m happiest about.
When a new member joins a group, past expenses that were split equally get automatically rebalanced. If three people split a $90 dinner and a fourth person joins the group, those existing equal splits don’t retroactively change. But new expenses going forward split correctly among all four. The system handles this without anyone having to think about it.
It’s a small detail, but it’s the kind of thing that breaks trust in a splitting app if it’s wrong.
Settlement simplification
If Alice owes Bob $20 and Bob owes Charlie $20, Alice should just pay Charlie directly. The settlement algorithm sorts everyone into debtors and creditors, then matches them greedily to minimize the number of transactions. In a group of six people with a dozen expenses, you might only need two or three payments to square up.
What I learned
The boring parts matter most. Nobody gets excited about delete confirmation flows or email notification logic. But those are the things that make people trust the app with real money. Getting the emails right, making sure balances are always correct, handling every edge case around deletion and restoration. That’s where the time goes.
A reusable starter kit pays off. Auth, payments, email, database setup. I’ve done this enough times now that having a template saves days on every new project. The interesting work is the domain logic, not wiring up Stripe for the fifth time.
You can try it at divvy.club.