I built an entire budgeting app from scratch. What started as a brainstorming session with Claude turned into a working prototype in hours, and a production app in weeks. But this isn't a story about AI writing code. It's about how AI changed the decisions I was willing to make.
Why I Built Tally Budget
I've always been frustrated with budgeting apps. They force you into rigid calendar-month cycles, bombard you with ads (or are overpriced), require bank account connections (which always break), and disconnect you from your spending habits.
I wanted something different.
-
Custom budget periods - Budget bi-weekly, weekly, or any period that matches your paycheck schedule
-
Manual transaction entry - Stay mindful by logging each expense yourself
-
No bank connections - No more broken syncs or security concerns
-
Household sharing - Budget together as a couple or family with full transparency
-
Clean, modern UI - A beautiful, simple mobile experience
Most budgeting apps treat the calendar month as sacred. But my wife and I like to budget based on our credit card statement cycle. That mismatch was the core problem I wanted to solve.
Starting with a Prototype
I started by sharing a screenshot of the Google Sheet my wife uses for our budget with Claude. It was simple. Income at the top, categories with allocated amounts, and transactions logged manually. Nothing fancy, but it worked for us.
The problem with the spreadsheet was that it made budgeting feel like a chore. We'd dread the end-of-week ritual. Sit down together, review our credit card statement, manually enter dozens of transactions. Life gets busy, and suddenly you're three weeks behind on logging expenses. We needed a way to capture transactions in the moment, wherever we were, so the data entry didn't pile up into something we'd avoid.
That spreadsheet became my north star. Every feature decision came back to one question. Does this truly help us budget and track our spending habits?
The first prototype was deliberately minimal, a single budget with hardcoded categories, manual transactions, and a summary view. No authentication, no persistence beyond the session. I just wanted to see if the core interaction felt right.
It did. And that's when I started making bigger decisions.
The Stack Decisions
Why Supabase
Supabase was always going to be the choice for a simple app like Tally. It eliminated the need to build a bunch of APIs, manage database connections, or roll out a custom auth solution. Out of the box, I got a PostgreSQL database, Edge Functions, realtime subscriptions, and authentication. Everything I needed without the infrastructure overhead.
PostgreSQL gave me the relational data model I needed (budgets have categories, categories have transactions, households have members). Row Level Security meant I could enforce multi-tenancy at the database level rather than hoping I got every API endpoint right. And when I needed server-side logic (Stripe webhooks, push notifications, recurring transaction generation), Edge Functions let me write it in TypeScript and deploy alongside my database.
Why Legend State
State management in React Native is notoriously painful. Redux requires too much boilerplate for a solo project. Actions, reducers, selectors, and all the wiring in between. Zustand is simpler, but it's still fundamentally imperative. You dispatch updates and hope your UI stays in sync.
Legend State thinks differently. It's built on observables, so your UI reacts to data changes automatically. But the real reason I chose it was the Supabase plugin. Legend State has first-class support for syncing with Supabase, handling optimistic updates, conflict resolution, and offline persistence out of the box. I didn't have to build any of that infrastructure myself.
When a user adds a transaction, the UI updates instantly (optimistic), the data syncs to Supabase in the background, and if they close the app and reopen it offline, everything's still there via MMKV persistence. The Supabase plugin handles retries, deduplication, and eventual consistency.
This architecture decision rippled through the entire app. I didn't need loading spinners on every screen. I didn't need to manage cache invalidation. The data layer just worked.
Why HeroUI Native
I discovered HeroUI while scrolling Twitter after the prototype was already working. I fell in love with the clean, simple components immediately. They looked polished out of the box but were flexible enough to customize with Tailwind. I wasn't trying to build a design system from scratch. I was building a budgeting app, and HeroUI let me focus on that.
The Product Decisions
Flexible Budget Periods
This was non-negotiable. One of the main points of Tally was to escape the calendar-month prison. I designed budgets to have arbitrary start and end dates. Want to budget from the 15th to the 28th? Done. Want a weekly budget? Done. Need to budget based on credit card statement? Done.
The implementation turned out to be simpler than I expected. Transactions are added to the active budget, and the budget tracks its own period. No complex date calculations or period-spanning logic. It's the core differentiator.
Manual Entry by Design
Every other budgeting app tries to automate transaction entry through bank connections. I went the opposite direction: manual entry only.
This wasn't a limitation, it was a feature. When you manually log every purchase, you feel the money leaving. You become more intentional. My wife and I have used this approach for years, and the mindfulness it creates is worth the 10 seconds per transaction.
Household Sharing
While many people create solo budgets, we value transparency and budgeting as partners in my household. So, I thought having the ability to share budgets with your partners or roommates was an important feature to include. That led to creating households, where a user can create a household, invite members, and share budgets within your household.
The tricky part was RLS policies. When a budget is shared with a household, all members need to see every transaction in that budget. The policies had to traverse the relationship chain. Transaction belongs to budget, budget is shared with household, user is member of household. Getting those joins right while keeping queries performant took some iteration.
Real-Time Collaboration
Speaking of real-time. I implemented live updates using Supabase's real-time subscriptions. When my wife adds a transaction on her phone, I see it appear on mine within a second.
It's 2026. Users expect real-time updates in their mobile apps. A refresh button feels broken. But building a sync engine from scratch? That's weeks or months of work. Websockets, conflict resolution, reconnection logic, edge cases everywhere. Supabase gives you this out of the box. Subscribe to a table, get updates. What would have been a major infrastructure project became a few lines of configuration.
The Technical Decisions
Row Level Security Everywhere
I made an early decision. Every table gets RLS policies, no exceptions. This forced me to think about security at the data layer rather than the application layer.
The pattern became simple. Users can access rows they created, or rows that belong to a household they're a member of. This single rule, applied consistently, made the security model predictable and auditable.
Edge Functions for Server Logic
Some things can't happen in the client. Stripe webhooks need to verify signatures and update subscription status. Push notifications need to batch and send efficiently. Recurring transactions need to generate on a schedule.
I built each of these as Supabase Edge Functions. They run close to the database, have access to admin credentials when needed, and deploy with a single command.
The subscription flow was the most complex. Authenticate the request, create or retrieve the Stripe customer, check if they've used their free trial before, create the subscription, store it in the database, and return the client secret. Getting all the edge cases right (trial eligibility, payment method handling, webhook reconciliation) required careful thought, but once it worked, it just worked.
PostgreSQL Triggers for Automation
Recurring transactions were an interesting design problem. Users set up a template (amount, category, cadence), and the system generates actual transactions automatically.
I implemented this with PostgreSQL triggers and a cron job. When a recurring transaction is due, a trigger generates the next occurrence and calculates the following due date. A scheduled Edge Function runs daily to process any pending generations.
I also use triggers for soft deletes. When a user deletes a budget, I don't actually remove the row. A trigger sets a deleted_at timestamp and cascades that to related categories and transactions. This keeps referential integrity intact while letting me recover data if needed. The RLS policies filter out soft-deleted rows, so the app never sees them.
This keeps the complexity in the database where it belongs. The mobile app just reads transactions. It doesn't need to know or care that some were auto-generated or that deletions are reversible behind the scenes.
Iterating from Prototype to Product
The path from prototype to production wasn't linear. I'd build something, use it for a few days, notice friction, and redesign.
The backend stayed simple. A budgeting app doesn't need complex business logic. The database schema was straightforward and barely changed after the initial design. Most of my iteration happened in the UI.
Transaction entry went through the most revisions. This is the core interaction. If adding a transaction feels slow or clunky, people stop logging expenses. I obsessed over reducing taps, making category selection fast, and keeping the flow seamless whether you're standing in line at a coffee shop or sitting on the couch.
The dashboard evolved based on what I actually wanted to see. It started as a simple list of categories with spending totals. Then I added spend streaks to encourage daily logging, summary cards showing total spent and remaining, and progress bars for each category. Savings goals came later. Each addition came from using the app and wishing I could see something at a glance.
Budget creation and editing got similar treatment. The goal was always the same. Make sure all relevant information is visible and every action feels effortless.
What I Learned
The Right Tools Make Hard Things Easy
Tally isn't a complex app. But a few years ago, building it would have meant stitching together a dozen services, writing boilerplate for auth, managing websocket connections, and debugging sync conflicts. The features themselves aren't hard. The infrastructure used to be.
Supabase, Legend State, and modern tooling collapsed all of that. Real-time sync, push notifications, recurring transactions. These became configuration problems instead of engineering projects. I spent my time on the product, not the plumbing.
These tools with the help of AI coding agents, such as Claude Code and Cursor, made building this app in a few weeks possible.
The Database is the Product
For a data-heavy app like this, the database schema is the product. Get the relationships right (budgets, categories, transactions, households, members, permissions) and everything else follows. Get them wrong, and you're fighting your data model forever.
I spent more time on schema design than any other part of the project. Every table got thoughtful constraints, indexes, and RLS policies. The investment paid off in simpler application code downstream.
Iteration Beats Planning
I could have spent weeks writing detailed specs. Instead, I built a prototype in hours, used it, and iterated. The final product looks nothing like what I imagined at the start. It's better, because it evolved based on actual use.
The feedback loop was tight. Build something, use it while budgeting with my wife, notice what's annoying, fix it. Repeat.
What's Next
Tally Budget is live on the App Store and Google Play. I'm actively working on budget templates, spending insights, home screen widgets, and export functionality.
Each feature goes through the same process. Prototype, use, iterate, ship.
Final Thoughts
Building Tally taught me that the limiting factor isn't technical ability. It's ambition. When building becomes faster, the question shifts from "Can I build this?" to "Should this exist?"
The answer, for Tally, was yes. I wanted a budgeting app that fit how my wife and I actually manage money. Now we use it every day.
If you have an idea for something that should exist, there's never been a better time to build it.
Try Tally Budget: tallybudget.app