AI Rescue · Small business · 2025
Harbor & Vine
A four-location restaurant group built its own reservation system with AI. It worked until two locations tried to book the same table.
- Rails
- PostgreSQL
- Hotwire
- Tailwind
- 0
- double-bookings since launch, down from three to five a week
- 8 days
- from first code read to a stable production deploy
- 94%
- test coverage on the booking path, up from none
What we inherited
The owner had built a working reservation app over about six weeks of nights and weekends with an AI coding assistant. It genuinely worked — customers booked tables, staff saw them on a dashboard, service ran. Then they rolled it out to a second location and the bookings started colliding.
What was broken
-
No database constraints, only application checks
The availability check ran in Ruby before the insert. Two simultaneous requests both passed the check and both wrote a row. Nothing in the database itself prevented it.
-
Every timestamp was in the server's time zone
Reservations were stored without zone information, so the 7pm slot meant different things depending on where the staff member was standing.
-
No tests at all
The AI had written no test suite, so nobody could tell whether fixing one thing broke another.
-
Admin pages had no authorization
Any logged-in customer who guessed the URL could read every reservation across all four locations, phone numbers included.
What we fixed
-
1
Put a real constraint in the database
An exclusion constraint on table and time range, so the database refuses a double-booking even when application logic fails. Then we made the app handle that rejection gracefully instead of erroring at the customer.
-
2
Moved every timestamp to UTC with an explicit location time zone
One migration plus a backfill, dry-run against a copy of production before it touched the real thing.
-
3
Wrote tests for the booking path before changing it
Including a concurrency test that fires simultaneous bookings at the same table — the exact failure that brought them to us.
-
4
Added real authorization
Staff, manager, and owner roles scoped to a location, with a test asserting a customer account cannot reach an admin route.
Why we didn’t rewrite it
The instinct when you open a codebase like this is to start over. We didn’t, and it’s worth saying why.
The owner’s data model was fundamentally sound. Tables, reservations, locations, and service windows were modeled about the way we would have modeled them. What was missing wasn’t design sense — it was the specific, unglamorous knowledge that concurrent writes need database-level guarantees, and that timestamps without zones are a bug waiting for a second location.
That’s a very common shape for AI-assisted work. The architecture is often reasonable, because the model has read a lot of reasonable architecture. What’s missing is the hard-won operational knowledge that comes from having been paged at 2am. Rewriting would have thrown away six weeks of correct decisions to fix four wrong ones.
“I thought I was going to be told to throw it all away. They kept most of what I had built and fixed the parts that actually mattered. That was not the answer I expected.”
Got something that looks like this?
Thirty minutes, no pitch. We'll tell you what it would actually take.
Book a free 30-min callRather not book a time? Send a message instead.