Case study: independent product
BuffrHQ
An all-in-one CRM for mobile auto detailers: scheduling, client lookup, invoicing, inventory and route planning. Designed, built and shipped alone.
- Role
- Solo: product, design, four surfaces, backend
- Timeline
- Mar 2026 → now · still shipping updates
- Stack
- React Native / Expo, Vue 3, Node, Postgres, Mapbox
- Status
- Live on the App Store, under Buffr LLC
- Source
- Private (reason below)
The problem
Every tool built for detailers assumes a desk and a receptionist.
Mobile detailers work out of a vehicle, not a shop. Between jobs they are booking work, looking up returning clients, taking payment and tracking how much product they burned through. The software sold to them was designed for somebody sitting down.
The constraint that shaped everything else: the person using this is standing outside in sun glare with one dry hand and about fifteen seconds before the next thing needs their attention. That is not a styling problem. It decides how many taps a job transition can take, how large a target has to be, what belongs on a home screen at all, and which features are allowed to require typing.
Constraints
What was fixed before I wrote anything.
- No team, no specification, no design partner. Every decision was mine, and every one of them came back to me later, usually at the worst time.
- Real money and real location data from the first release. That is why the source is private, and it is why several of the calls below are risk decisions rather than engineering ones.
- One person's operating budget. Anything priced per request had to justify itself against a subscription a solo detailer will actually pay.
- The whole of it. Auth, edge cases, App Store review, subscription entitlements, and the unglamorous last twenty percent a team normally absorbs.
What I decided,
and what I rejected
Four calls, and what each one cost.
These are the decisions I would want to be asked about in an interview, so they are the ones written down. Each has a cost, because all of them did.
Call 01Route ordering without a routing bill
Commercial route-optimization APIs charge per request, and that cost scales badly against a price a solo operator will pay.
So the owner's dashboard has an Optimize Route button: a nearest-neighbor heuristic over haversine distance, computed locally and anchored to the earliest scheduled job. The planner at the top of my home page is the same algorithm. Jobs missing coordinates go to the end of the list instead of quietly disappearing from it. On the phone, where detailers actually are, the day simply follows the schedule.
The drawn route calls a free public routing service for a road-following polyline, with a straight-line fallback if it is slow or unreachable, so the map degrades to useful rather than to blank. Turn-by-turn navigation is handed off to Apple Maps or Google Maps. I have no business trying to out-navigate them, and no detailer wants me to.
In hindsight it is the one feature I would cut entirely (more on that below).
- Chose
- Nearest-neighbor ordering on the owner's dashboard, behind an Optimize Route button; navigation handed to the maps app they already use.
- Rejected
- A paid optimization API, and building turn-by-turn navigation in-app.
- The cost
- The tour is not optimal, and at three or four jobs a day there is little to optimize. On the phone, stops simply follow the schedule.
Call 02Buy the recognition; engineer the uncertainty
Detailers rarely remember names. They always recognize the car. Matching the lookup key to how the user actually thinks was the highest-leverage product decision in the app: point the camera at a plate and the returning client comes up.
Recognition itself is a solved problem sold as an API, and it is not where my advantage lives. The app captures a frame and hands it to my backend, which calls a commercial ALPR service. The token never ships to the client. In the bundle, anyone can pull it out of the IPA and spend my quota.
The engineering that mattered was everything around the call. A confidence threshold, below which the app shows what it read and asks the user to confirm instead of silently searching for the wrong plate. A manual entry path that is always visible, never a fallback you have to discover. Recognition fails in rain, at angles and on novelty plates, and a feature that only works in good conditions is one people stop trusting. And a region selector, because plate strings are only unique within an issuing state and two clients can genuinely share one.
The interesting part of integrating an ML service is rarely the call. It is deciding what to do when it is unsure.
- Chose
- Buy recognition, proxy it server-side, and design the interface around the failure case.
- Rejected
- On-device OCR, and shipping the vendor token inside the app.
- The cost
- A network round trip, a per-scan cost, and a hard dependency on somebody else's uptime.
Call 03No card processing, on purpose
The obvious move is to integrate a card processor and take a cut of every job. I chose not to. Onboarding a solo operator into a payments platform means underwriting, KYC, payout delays, chargeback exposure and a compliance surface I would be carrying alone.
Detailers already get paid through Cash App, Venmo, PayPal and Zelle. So the invoice generates a QR pay page with whichever of those the business has configured, the customer pays through a channel they already trust, and the detailer marks it settled. Tips are chosen by the customer at pay time rather than pre-set by the detailer, which converts better and is less awkward for both of them.
It is a smaller-looking feature that removed an entire category of risk from a one-person company.
- Chose
- Four payment handles behind a generated QR pay page.
- Rejected
- Card processing, and revenue from a take rate.
- The cost
- No payment revenue, and settlement stays a manual step. Reconciliation is the detailer's job.
Call 04Permissions before there were users to permission
Access is gated on two independent axes: what your role allows, and what your subscription tier includes. A feature has to clear both.
That is over-engineering for launch, and it was, right up until the first team account, at which point retrofitting it would have meant auditing every screen in the app.
What made it survivable in production was a deliberate fail-safe: if the permission set cannot be loaded, a small set of account-essential screens stay reachable. An empty permission set rendering a dead app is a much worse failure than briefly showing someone a button they cannot use.
- Chose
- Two-axis permissions in the first release, with a fail-safe floor.
- Rejected
- Ship single-user, add roles when a team asks for them.
- The cost
- Weeks of work before a single team existed, and a check on every screen to keep correct.
What it does
Four client surfaces over one backend.
A React Native app for the detailer in the field. A Vue 3 dashboard for the owner planning a day or reconciling a week. A public booking site per business, so their customers can request work that lands in the app as an incoming job. And a platform admin for me. Behind all of it, a Node and Postgres backend handling auth, integrations, scheduled work, and push, SMS and email notification depending on urgency and on what the recipient has.
Services are priced the way detailers actually sell them: one price for a small vehicle plus an increment per size step, rather than four unrelated prices per service. Setting up a price list stops being data entry and becomes one number and three adjustments. The distinction between no increment configured and an increment of zero turned out to matter, and getting it wrong is exactly the kind of bug that quietly overcharges someone's customer.






Scroll or arrow through · six of the app's screens
Outcome
Live, and still mine to maintain.
- On the App Store with in-app subscriptions, operating under Buffr LLC, and still shipping updates.
- First commit to release in under three months, solo, across four client surfaces and one backend.
- Automatic status notification removed the single most common support conversation a detailer has.
- Plate lookup, barcode inventory, route planning, a four-app QR pay page and role-scoped multi-tenancy all shipped in the first year.
I am not going to publish install or revenue numbers. It is a young product in a small market and the honest version of that chart is not impressive yet. What it does prove is delivery: I chose the scope, made every call above, and got it through App Store review without anyone to hand the hard parts to.
Why the source is private
BuffrHQ handles payment handles, addresses and location data for real businesses. Git history exposes every commit, not the current state. One config file committed and removed a year later is still trivially recoverable. The case study carries the value here; the code does not need to. I am happy to walk through any part of the implementation in an interview.
What I'd do
differently
I built features instead of selling it.
The people I showed it to along the way loved it. But I got so focused on new features that I didn't sell it to anyone, and I haven't spent anything on ads. If I started again, I'd talk to detailers before building, launch a smaller product sooner, and put real effort into getting it in front of people from the start.
I'd also skip the route planner. Solo detailers usually clean three or four cars a day, so there is nothing there to optimize. It was a bad idea from the start, and I would never build it again.