← All work

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. 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. 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. 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. 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.”

[Client name], [Title], Harbor & Vine

Got something that looks like this?

Thirty minutes, no pitch. We'll tell you what it would actually take.

Book a free 30-min call

Rather not book a time? Send a message instead.