← Selected work

Founding a Galápagos cruise marketplace

Nobody let you book a Galápagos cruise online. I founded, designed, and engineered the marketplace that does; it's live, and no operator has paid yet.

Founder, designer, engineer · 2024 – present

A cruise departure page on Book Cruise Now: the itinerary drawn on a satellite map of the islands, cabin classes with prices, and a request-to-book panel
  1. In the Galápagos the ship is the trip, and a cabin unsold when the ship leaves is gone. Nobody let a traveler see that cabin and book it online; every path ran through an agent's inbox or a WhatsApp thread.

    I hired the first build out, took the keys a year later, then started over: mapped the domain as objects (Sophia Prater's OOUX), compiled that map into the schema, and built the marketplace with AI agents working against it.

    The rebuild is in production with 22 ships seeded, billing live since August 31, and operators running five ships committed to configuring their departures. Nobody's paid yet, so the ~150 inquiries a month still reach shipowners through me, forwarded one at a time.

  2. Twenty objects run the whole product. Here are three you'd meet as a buyer — a ship, a dated sailing, you. Keep scrolling and I'll introduce the rest, one relationship at a time.

    A ship has cabin classes — standard, superior, suite, the thing a buyer picks — and real numbered cabins behind them. Only the operator ever sees cabin 4, the one with the window.

    Each ship runs itineraries: named loops that can be cut into shorter segments on the same hull. An itinerary is just a list of day stops — a morning or afternoon at one visitor site.

    Buyers don't shop ships or itineraries — they shop departures: a dated, priced sailing. And on availability they only ever get a yes or a no, never the cabin numbers.

    The buyer side starts anonymous. A visitor can favourite and inquire without an account; confirming an email promotes them to a buyer, and everything they did follows them across.

    An inquiry only exists once the buyer's email code comes back — until then it's an encrypted stash. If the operator says yes, that's a booking invitation: a held cabin, a price, and a link that expires.

    Accepting the invitation makes a booking, and the booking consumes exactly one availability slot. The payment is a mirror of what happened on the operator's own rail — BCN is never the merchant.

    Every booking has exactly one trip, and the trip owns the manifest: passports, dietary needs, flights. Passenger data is purged six months after the trip ends.

    The operator: teams as a tenancy boundary — row-level security scopes every query to one — staff in the portal, and exactly one subscription. Unpaid is a state, not an absence.

    That's the cast. Every relationship you just watched is a sentence in a Python file, and the database schema is generated from those sentences. When the model changed, the schema followed.

    The model the rebuild runs on, introduced one relationship at a time
  3. The ship is the trip

    My father, Marc, came to the Galápagos as a researcher with the Charles Darwin Foundation, and I was born in Puerto Ayora. Most of the islands can only be reached by small ship, on a fixed schedule, so an unsold cabin is a lost sale — and in 2004 he built Galápagos Cruise Links so travelers could find those cabins. Twenty years on it's Book Cruise Now, and I run it with him.

  4. I signed off on the wrong nouns

    In 2024 our marketing agency, Studio Q, built the app from my Figma, and I signed off on their entity diagram in about twenty minutes. I'd designed screens; I hadn't designed nouns. When I took over the codebase in July 2025 I inherited 53 tables, nine of them nothing wrote to — and none of that is on Kameron Hartman, who built what I asked for, quickly.

    This is the map I signed off in twenty minutes. Fifty-three tables shipped; three did most of the work — a ship, a dated listing, and one user table for everyone. Keep scrolling for what I didn't catch on that call.

    Everything hung off the ship — seventeen tables directly. The row itself held 45 columns: the spec sheet, ops flags, feature booleans, the sales owner's personal phone number, and an AI-written description.

    Beds were mapped to cabin classes twice — a legacy table and its replacement, both live at once. The departures join still read the old one, and the delete cascade still knew it by name.

    The bookable thing was a listing: an itinerary plus a date and a price. It also carried its own analytics — view and inquiry counters incremented on every page hit, in the same row buyers were reading.

    One user table held travelers and portal staff, with a free-text role column checked in middleware. The entire multi-tenancy story was user_ship: three columns saying who could see which ship.

    Then a second identity graph. Anonymous subscribers lived in email_subscriptions with their own favourites and saved searches — and saved_search kept its user_id nullable so it could serve both masters.

    Three favourite tables shipped. One worked. The other two were schema only — no route ever read or wrote them. My mockups said wishlist, so the code said favourite, and nobody asked which objects deserved one.

    The lead — the thing that paid for everything — was ship_inquiries: 27 columns, two email fields, five UTM columns, and a name that was blank on every production row. The real names landed one table over, in the raw attempts log.

    And the leftovers. A visitor-site table with coordinates to ten centimetres and zero routes. A notification queue with no processed flag, so it only grew. And the corpse of the views table, archived after 2.3 million fake rows — still holding a foreign key to ship.

    Nine tables nothing wrote to. Twenty-eight routes nothing called. None of it is on Kameron — he built the nouns I gave him, quickly. The nouns were mine, and two years later the rebuild started with a week of refusing to draw screens until they were right.

    The 2024 schema I signed off: everything hung off the ship, and nine tables were never written to
  5. What the inbox taught me

    Every inquiry copied me, so for a year I read both sides. Operators answered "Dear Tourist, I just contact you by WhatsApp," guests replied "any update on our request?" three days in, and one operator had to enter every price change twice, and sometimes forgot. The booking lived in inboxes and WhatsApp threads, never in the product.

  6. “Your website is awesome and I'm sure will be a big success, but this whole cash thing is ridiculous. People don't feel comfortable carrying thousands of dollars in cash around.”

    A guest from Australia — replying to the automated "Did the trip happen?" email, after a ship asked for full payment in cash on-island

  7. Starting over from the nouns

    In May 2026 I started a new repo with one line: pretend the old app doesn't exist. Then I didn't draw a screen for a week; I ran Sophia Prater's OOUX process instead, foraging 103 nouns from two years of inquiries and cutting them to 45 objects. The matrix settled the argument the old app never had: buyers don't shop ships or itineraries, they shop departures, a dated and priced sailing.

    This is the flow the rebuild exists for: a buyer books a cabin, an operator confirms it, and the money never touches the platform. The dashed cards are the admin lane — the part of the flow that is still a person, on purpose.

    It starts anonymous. A table of dated, priced sailings with island, wildlife, and cabin filters — no account, no cookie wall. A cabin class says available or not; a buyer never sees a count, so nobody can watch inventory drain.

    The inquiry form is stashed encrypted, and nothing exists until the six-digit email code comes back. Only then is there a lead — and it lands in the operator's inbox, where a paid ship sees the whole thing.

    A ship that hasn't subscribed doesn't see the inquiry — a teaser at most: first initial, month, party size. The full thing falls through to me, I forward it to the shipowner by hand, and they answer the traveler directly. Every action still writes an audit row.

    A subscribed operator replies by email or WhatsApp, then holds a specific cabin class against the inquiry. The hold lasts 24 hours — a cron sweeps the dead ones every hour — and the invitation that follows locks its price at send time.

    The buyer gets one link. It holds the cabin at a fixed price for 24 hours, and opening it under their own row-level security is what proves it's theirs. The operator can withdraw the invitation; the buyer can only read it.

    Passengers, contact details, the agreement. The total is computed once, server-side, and snapshotted on the booking — then it sits at requested until the operator answers.

    Confirming attaches the operator's own payment link. The money moves on their rail — WeTravel, PayPal, or a wire — and BCN mirrors the state without ever holding a dollar.

    Passports and dietary needs go on the manifest. Air tickets, vouchers, and the confirmation come back as private documents — the bytes never get a public URL — and six months after the trip it's purged.

    That's the happy path. The admin lane never left: I can act as any operator — every write under impersonation audits both identities and shows a banner — and until a ship subscribes, its inquiries are answered by a person. For now, the person is me.

    The flow the rebuild exists for. The dashed lane is the part that's still a person: me
  8. The model compiles into the schema

    The model lives as Python, and one command generates the schemas, fixtures, and a guide page per object; the build fails if the output drifts. Generated files are never edited, the server action is the security boundary, and the audit log is append-only with a hash chain, so the product can't lie about what happened.

  9. Agents typed, I decided

    AI agents did most of the typing, but every decision came back to me as a plain-English question with a recommendation, and I accepted 191 of 198 during modelling. That says less about the agents than about me finally knowing my domain well enough to argue with them.

  10. Small things I got a bit obsessed with

    A buyer never sees cabin counts, only whether a class is available, so nobody can watch inventory drain. An operator can generate a season of departures from "every second Tuesday" and set a rule like 20% off inside 60 days that reprices each departure the day it crosses the line. And the booking route is forbidden a loading file, because a Suspense boundary there turns a redirect into a broken page; a script fails the build if anyone adds one.

    The public marketplace: a table of Galápagos cruise departures with ship, dates, itinerary, and starting price
    Rows, not cards. Buyers compare dates and prices, so the catalog is a table; the photos wait until the departure page
    The generated object guide for Inquiry, with its definition and a six-state lifecycle
    Every object gets a generated guide. Its heaviest readers turned out to be the agents, who stopped inventing fields the moment they had it
  11. Heather broke the operator portal in an hour

    On August 14 Heather Blenkiron, who sells these cruises at CNH Tours, sat through a moderated walkthrough of the operator portal. Itineraries and departures held; the booking half didn't — no payment link in the confirmation, nowhere to paste a cancellation policy, no way to say a cabin had a window — and two became launch gates. Her question about where a traveler's air tickets go became a new object the following week.

  12. Choosing the rewrite over a thirty-day plan

    In November 2025 I wrote a thirty-day plan to fix the mobile funnel, launch paid tiers, and activate 52 ships; I chose the rewrite instead. Ten months later the old app still takes the inquiries, and a year of ad traffic landed on a funnel I'd stopped improving. I'd make the same call, because the old model couldn't hold a booking, but running a marketplace and rebuilding one are two jobs, and I did them in sequence, then badly in parallel.

  13. Go-live, slowly

    The new app took over app.bookcruisenow.com on August 3, and Stripe billing worked end to end on August 31, the day I demoed the portal to Lorena and Mónica, who run five ships between them. Mónica's verdict on the itinerary maps: "eso me parece lo mejor de todo." Listing is free and operators pay to have inquiries land in their inbox; until they do, those inquiries come through me.

  14. What's next

    The DNS flip from the old site is the next gate, then the first ten founding operators, then linked voyages, child pricing, and eventually Antarctica and Svalbard on the same model.

  15. 45 objects, and buyers touch five

    I modelled 45 objects; buyers touch about five, so next time the list-click test comes first. I'm also the support desk, the sales team, and the audit log's only reader, and the strangest part is still that one person can hold it all in their head.

  16. Thanks

    • Marc Patry — Co-founder, my father, and the reason the shipowners pick up the phone. Twenty years of answering "which ship?" went into the nouns.
    • Kameron Hartman — Built the first version at Studio Q from my Figma in 2024 and handed it over cleanly a year later.
    • Amy Cooper, Katie Jones, and Michaela Valiquette — Studio Q. Ran marketing throughout, wrote the November report that told me the truth, and the intro script some of this story borrows from.
    • Heather Blenkiron — Broke the operator portal in an hour and turned what she found into launch gates.
    • Aditya Arora — Took the first cut at the inherited codebase with me in 2025.
    • Lorena, Mónica, Nicole, and Juan — Answered every "Dear Tourist," then told me exactly what the portal got wrong.
    • Sophia Prater — OOUX and the ORCA process this rebuild stands on.