Extended-stay booking platform

Yovivo

Full-stack engineer · DevLabs client project · about a year

A booking platform for people who need somewhere to live for a month or longer: the remote workers and long-stay travellers that nightly-rate sites serve badly. I joined an existing codebase as the full-stack engineer and owned the booking and payments path, plus search across a 350,000-listing catalog served by a system I didn’t control.

Visit project

The site is still up, but its listing feed no longer returns results.

Yovivo project preview
  • 350Klisting catalog searchedA third-party inventory feed maintained by another developer. I built the search and booking experience over it.
  • <1ssearch responseDown from multi-second waits, via response caching, server-side filtering, debounced input and pagination. Observed rather than instrumented.
  • Shippedto production, ran in the wildSearch, checkout and payments all went live. The site still loads today, but its listing feed no longer returns data.

The problem

Short-stay platforms are built around nights. Booking three months on one is either impossible, priced as ninety separate nights, or something you negotiate with a host in a message thread. The inventory that does suit longer stays was scattered across many sites, so anyone planning a months-long stay compared options by hand.

Yovivo’s bet was that the growth in remote work had created a real market nobody owned yet: people who need somewhere to live for a month or more, in a country they may never have visited, paying in a currency that may not be theirs. That last part matters more than it sounds. A three-month booking is a large enough transaction that deposits, refunds and currency handling stop being details and start being the product.

My role

Yovivo came to DevLabs as a client project and I was the engineer assigned to it, working in a small team for about a year. I joined an existing codebase rather than starting from scratch.

I owned the product’s full stack: the Next.js frontend and its search and results experience, account creation and management with NextAuth, and the Express and PostgreSQL backend behind Stripe checkout, webhooks, refunds and multicurrency payments.

What I did not own shaped most of the engineering. The property inventory, around 350,000 listings, came from a separate PHP service maintained by another developer. I consumed it; I could not change it.

  • An existing codebase, with no documentation and no tests
  • The property catalog lived behind a service I couldn’t modify
  • Requirements moved during the build
  • A client project, on a client’s timeline

Approach

I tried to fix the source first

The catalog was served by a PHP system another developer owned. It was slow, paginated awkwardly, and returned records whose shape varied from one to the next. Searching it directly and rendering whatever came back was enough to make the product feel broken. Before working around it I tried to fix it: I pulled the codebase down over FTP and found roughly ten gigabytes of unstructured PHP with no discernible organization, nothing I could safely change on a client’s schedule. So treating that service as a fixed boundary became a decision rather than a default. I absorbed its behavior on my side, where I could actually reason about it.

Cache the upstream instead of paying for it every search

Every keystroke reaching a slow third-party endpoint is the worst version of this problem. I cached supplier responses so repeated and overlapping searches were served locally rather than re-fetched.

Move filtering to the server, and stop the client shouting

Filtering moved off the browser and onto the API, and the search input was debounced so typing a city produced one request instead of a dozen. With pagination and lazy loading, the client never held the whole result set. Together these took search from multi-second waits to under a second.

Payments were the part I fully controlled

Stripe checkout, webhooks, refunds and multicurrency were mine end to end. On a long stay the amounts are large and usually cross-border, so refund rules and currency handling had to be right rather than approximately right. The failure mode there isn’t a slow page, it’s someone’s money.

Where the boundary sat

Yovivo system boundaryThe Next.js frontend calls an Express API, which owns PostgreSQL and Stripe. The API reads property listings through a response cache from an external PHP property service of about 350,000 listings, maintained by another developer and outside Oscar's control.WHAT I OWNEDNOT MINENext.jssearch & resultsExpress APIfiltering, authPostgreSQLStripeCachesupplier responsesPHP property service~350K listingsslow · another dev's code
Caching, server-side filtering and debounced input all sat on my side of the dashed line: compensation for a service I couldn’t change.

What went wrong

The right fix was somewhere I couldn’t reach

The honest version of the search story is that the correct fix was upstream and I couldn’t get to it. Ten gigabytes of unstructured PHP owned by someone else, on a client’s schedule, is not something you refactor. Caching, server-side filtering, debouncing, pagination: everything I did was compensation at a boundary. It worked, and search went from multi-second waits to under a second, but it treated a symptom, and I knew that while doing it.

An inherited codebase with no documentation or tests

I joined a project already in motion with nothing written down about how it fit together and no tests to tell me when I’d broken something. Every change carried more risk than it should have, and the only real mitigation was reading carefully and changing narrowly.

Requirements that moved

Scope shifted during the build, which on a fixed client timeline means re-cutting what “done” covers rather than extending the deadline.

Results

The platform shipped and ran in production. Then the feed it depended on stopped working.

As of August 2026 the site still loads, but search comes back empty: the third-party listing service it depended on no longer returns data. That service was the one piece of this system I flagged as outside my control, and it is what the product ultimately failed on. So I am leaving the link up and saying what happened to it.

I don’t have booking or revenue figures for it either. I was the engineer on a client project, not on the business side, and I am not going to invent numbers I can’t back up.

What I’d do differently

  • Normalize the upstream data at the boundary. I defended against the messy feed throughout the application instead of reshaping it once, at the edge, into something the rest of the code could trust. One adapter would have removed a category of defensive handling everywhere else.
  • Measure before optimizing. I improved search on observation and intuition. It clearly got faster, but without real timings I was choosing targets by feel rather than by evidence.

Stack

  • Next.js
  • Express.js
  • PostgreSQL
  • NextAuth
  • Stripe