Mum opened the app on a Tuesday morning instead of the spreadsheet and said nothing. One week later two of her friends sent back specific feature requests. That's the moment a favour becomes a product -- except in our case, we never planned to build a product at all.
VitalRegistry is a web CRM for BEMER device distributors: rental tracking, client records, contracts, calendar sync, in-app reminders. Ten distributors run it today, each managing between ten and thirty devices. Mum was user number one. The build took eight months across two developers working part-time. This is the case study we'd have wanted to read before starting.
The Spreadsheet Problem Is Specific
Mum had over ten rental devices at any given time. She tracked returns, payments, and client contact details across several Excel files that had multiplied across versions. End-of-month reconciliation meant hours of cross-referencing between files she'd updated at different points. She'd edited one sheet and forgotten she'd already updated another.
The breaking point was ordinary: she asked at Christmas whether we could build something that stopped her losing track. Not a product. Something that let her trust her own data again.
That request sits at the centre of a problem we see constantly among ops-heavy operators. The spreadsheet starts as a convenience. It grows until the operator is maintaining the spreadsheet instead of running the business. At that point the choice is a generic SaaS CRM -- expensive, over-specified, requires migration and training -- or a custom tool that fits the actual job.
Decision 1: Firebase as the Backend
Options considered: roll our own Postgres and Auth stack, Supabase, Firebase, thin custom Node API on top of any of the above.
Chosen: Firebase native end to end -- Firestore for data, Firebase Auth for login, Firebase Hosting for the frontend.
Rationale: generous free tier for the scope we needed. Google-backed, so we weren't betting on a startup outliving the project. Firestore and Firebase Auth ship with most of a CRM's requirements out of the box. Every line we don't write is time for learning the domain instead of scaffolding infrastructure.
Tradeoff: locked into Google's data model and pricing curve. If we scale hard, the migration off Firestore is the bill we'll pay later.
Six Weeks to a Working Prototype
We started over Christmas, committing daily. Six weeks later -- by mid-February -- mum had auth, CRUD for devices and clients, and the table views she recognised from her spreadsheets. Six weeks from mum asking us at Christmas whether we could build something to a Streamlit prototype she used daily. The first version wasn't pretty. It was usable, which was the only metric that counted.
Streamlit was the right call for the prototype stage. It runs Python, deploys fast, and let us build functional UI without a frontend specialist. We spent the next month refining flows until she didn't need us standing over her shoulder. At two and a half months in, the tool was handling real daily load.
Decision 2: Drop Streamlit, Rewrite in React
Options considered: stay on Streamlit and accept the cost cliff, partial rewrite (Streamlit for admin, React for users), full React rewrite.
Chosen: full rewrite.
Rationale: Streamlit runs on the server, not the client. Past twenty concurrent users, the hosting math broke in ways that made staying expensive and continuing to improve the UI impossible. A real product needed a real design system.
Tradeoff: five months of part-time engineering work. A UI mum had to relearn. She did, without complaint.
The non-obvious thing here: the rewrite trigger was not a technical complaint. Nobody filed a bug saying "Streamlit feels wrong." The decision came down to a single spreadsheet showing hosting cost past twenty concurrent users. One number. Everything else -- component library, proper routing, mobile-friendly layouts, contract generation, in-app reminders, calendar sync -- those followed from that number. The entire five-month rewrite was justified by one line on a cost projection.
That's the pattern founders miss. Rewrites don't get triggered by engineering taste. They get triggered by a number that makes the alternative impossible to defend.
Decision 3: Agentic Workflow for a Two-Person Team
Options considered: continue with Gemini Pro in browser tabs with manual context paste, move to Claude Code, build a custom agent harness.
Chosen: Claude Code.
Rationale: Claude Code reads the codebase instead of waiting for us to copy it into a chat. On a project past a few thousand lines, that's the difference between shipping a feature in a session and burning the session on context work.
Tradeoff: a $20 monthly subscription, and a skill proliferation problem we fixed later with GSD.
The harder problem on a two-person team was not technical. The hardest problem on a small team is not technical. It's two people in the same repo trying not to step on each other's branches. Messenger threads of "are you on this?" and "wait, did you just push?" burned hours we didn't have. That problem pushed us to GSD and an adversarial review pipeline. We did not solve it during the VitalRegistry build. We solved it after.
What Ten Real Users Taught Us
Ten active distributors, each running ten to thirty devices. That's enough load to find every hole in a data model.
Real usage surfaced fields we'd underspecified, queries we'd underindexed, and UX flows that assumed more discipline from the user than was reasonable. Device return reminders needed to be more aggressive. Calendar sync needed to handle timezone edge cases mum's region exposed. Contract generation needed to be exportable without a print step.
The feedback loop was fast because the users knew us. A handful of distributors with ten to thirty devices each is enough load to find the holes in your data model. We didn't need a hundred users to find the gaps. We needed ten people with real stakes using the thing daily.
Mum saves tens of hours a month compared to her spreadsheet workflow. End-of-month reconciliation -- the task that used to cost her most of a Sunday -- now happens in the app automatically. Mum saves tens of hours a month with VitalRegistry compared to her spreadsheet workflow. The hours that used to go to end-of-month reconciliation now go to actually talking to clients.
When we asked early users whether they would pay for VitalRegistry, the answer was "in a heartbeat." Several named a specific price. We gave it away free because a competitor had already changed the price floor. That's a separate conversation.
The Shape of the Build
The actual timeline:
- Weeks 1-6: Streamlit prototype on Firebase. Auth, CRUD, table views. Handed to first user.
- Months 2-3: Refinement of Streamlit flows. Daily use, regular feedback, no engineers required on-site.
- Month 3: Rewrite decision triggered by hosting cost projection.
- Months 3-8: Full React rewrite. Component library, routing, design system, mobile layouts, contract generation, reminders, calendar sync.
- Now: Ten active distributors. Conversations open with BEMER distributor newsletters and Facebook groups about wider launch.
Two part-time developers. Eight months. No third-party CRM integrations. No outside contractors.
The Principle
Custom tooling earns its cost when the alternative is a founder maintaining the tool instead of running the business. Spreadsheets are not the problem. Spreadsheets that have outgrown the operator's ability to keep them coherent are the problem.
The build decision, the rewrite decision, the agentic workflow decision -- each came from a number, not an opinion. That discipline is the part founders skip. They rewrite because the code feels wrong. They add features because a user asked once. They stay on spreadsheets because switching feels expensive.
Start with the number. The decision usually makes itself.
If you're an ops-heavy operator who's cross-referencing files at the end of the month to figure out what happened during it -- at what point does the spreadsheet cost you more than a tool built to replace it?
