A booking platform tuned from search to checkout
A reservation platform where the search-to-checkout path is designed around real traveler behavior, then engineered to load fast and hold up under seasonal spikes.
What the engagement involved
Travel & Tourism
16 weeks
Fixed price, phased
Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.
What we were brought in to do
The booking flow converted well on desktop and lost travelers on mobile. We reshaped the whole journey (search, results, property detail and checkout) around how people actually decide, then rebuilt it as a fast, responsive web app.
Two-thirds of traffic was already mobile and converting at less than half the desktop rate, which the team had been treating as a design problem for three quarters. The engagement started six months before the summer booking window, which set both the deadline and the load target: whatever shipped had to survive ten times normal traffic on the first attempt.
Design & Build
Where the old way broke
- 01
The previous flow buried key decisions and dropped users between search and payment. Pages were slow on mobile, and checkout asked for too much too early, which drove a 62% abandonment rate during peak travel season.
The single-page checkout asked for passport details before showing a total, and the abandonment analytics put 62% of the loss on that one screen. Underneath, search re-queried the origin on every filter change, so a traveler narrowing by price on a hotel connection waited four seconds per checkbox and usually gave up narrowing.
We restructured the booking platform around a clear search-to-confirm path, split checkout into staged steps, and rebuilt the frontend for fast rendering and stable performance during peak booking windows.
Instant Filter Engine
Sub-50ms search filtering across 10,000+ real-time inventory listings with edge-cached queries.
Filtering used to round-trip to the origin on every checkbox. Now the inventory index ships to the edge as a compact binary payload and the filter runs in the browser against it, falling back to a GraphQL query only where live pricing is needed. Redis holds that pricing layer on a short TTL, so a room that sold out three seconds ago disappears from the list before anyone can fail at checkout on it.
- Compact inventory index served from the edge
- Client-side filtering; server called only for live price
- Short-TTL pricing so sold-out stock drops out fast
Search results filtered down to what's actually bookable: 38 Lisbon stays for eight nights, filtered on the device in under 50 ms against an index of 10,000+ listings, three stays that sold out after the search already removed, and every total priced with taxes.
Staged 3-Step Checkout
Seat selection and the staged checkout: step 1 of 3 held in the URL, seats 14A and 14B held on the outbound flight, two checked bags, and a fare breakdown that sums to $1,686.80 before any passport details are asked for.
A lean checkout flow with inline validation, currency auto-detection and one-tap mobile payments.
One long form became three steps that each hold their state in the URL, so the back button behaves and a half-finished booking survives a refresh. Inline validation runs against the same schema the API enforces, which removed the whole class of failure where the form accepted a value the server then rejected. Currency is resolved once from the billing address, never guessed from the IP.
- Step state in the URL: back and refresh are both safe
- One validation schema shared by client and API
- Currency from the billing address, never guessed from the IP
Seasonal Peak Resilience
Resilient edge architecture built to withstand 10x traffic spikes during holiday booking windows.
Peak weeks put ten times normal load on the same infrastructure. Search and browse were made fully static-renderable so they serve from cache under any load, leaving checkout as the only path that touches origin. Checkout sits behind a queue that admits a fixed rate, so it never accepts everything and then times out. The whole route was load-tested at twelve times peak before the first holiday window.
- Search and browse served entirely from cache
- Rate-admitting queue in front of checkout
- Load-tested at 12x ahead of the first peak
The operations console: +34% booking conversion, +41% mobile checkout completion and a 1.2s median page load, revenue by hour against last year, which routes serve from cache and which reach origin through the queue, and the live booking stream.
What we built together
- 01
Mapped the real decision path from 40+ recorded booking sessions
Sessions were coded for where a traveler hesitated, not only where they dropped, and that put the fault a screen earlier than the analytics did.
- 02
Prototyped a staged checkout and tested it with returning travelers
Testing with returning travelers was deliberate. They know what the old flow asked for, so any hesitation means the step order is wrong.
- 03
Rebuilt the frontend for sub-second navigation between steps
The inventory index moved to the edge and the filter runs in the browser against it, calling the server only where live pricing genuinely changes the answer.
- 04
Load-tested the checkout against projected seasonal peaks
Checkout was load-tested at twelve times peak before the first holiday window, which is where the rate-admitting queue came from.
Operational results after launch
+34%
Booking conversion
+41%
Mobile checkout completion
1.2s
Median page load
Conversion compares the eight weeks after launch against the same eight weeks the previous year, so the seasonal shape is held constant and nothing is flattered by a quiet spring. Median page load is field data from real sessions weighted to the traffic mix, with no lab runs.
Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.
Phase by phase
Phase 1: Discovery & UX Audit
User Journey & Friction Analysis
Audited 40+ recorded booking sessions and analytics to isolate mobile drop-off triggers and cart abandonment spikes.
- Friction Audit Report
- User Journey Map
- Mobile Conversion Benchmark
Phase 2: UI/UX Redesign
Staged Mobile-First Architecture
Designed a progressive 3-step checkout with instant price breakdown, inline field validation, and 1-tap mobile payment options.
- Figma Prototype System
- Component Design Library
- Usability Test Results
Phase 3: Core Engineering
High-Performance Next.js Build
Rebuilt the frontend on the Next.js App Router, with edge Redis caching for search queries and optimistic state handling for zero lag.
- Next.js App Engine
- GraphQL API Integration
- Edge Cache Strategy
Phase 4: Load Testing & Go-Live
Peak Stress Simulation & Release
Executed load tests simulating 50,000 peak concurrent booking requests, achieving zero server errors and 1.2s median page load times.
- k6 Load Testing Suite
- Production Deployment
- Performance Baseline Monitoring
Instant Filter Engine
Instant Filter Engine
Sub-50ms search filtering across 10,000+ real-time inventory listings with edge-cached queries.
A property page where the room types are priced side by side for the whole eight-night stay, nightly rate, taxes and total, with the room that sold out three seconds ago already gone from the list.
About our collaboration
A cross-functional team of 5 worked on a fixed price, phased basis over 16 weeks, covering UX research, Frontend engineering, Checkout redesign. We held a standing mid-week checkpoint and wrote decisions down in place of status meetings. Nothing shipped without a live demo first.
Forty recorded sessions came before any design work, and the staged checkout was prototyped and tested with returning travelers before it was built. Load testing ran continuously from week six, never saved for a pre-launch gate, and that's why the queue in front of checkout exists at all. It was the answer to a failure found in week seven.
What we'd carry into the next one
- 01
Staging mobile checkout into clear, bite-sized steps reduced cart abandonment by 41%.
Abandonment fell because the steps each hold their state in the URL: a traveller who backs out to check something returns to a form that still holds their answers.
- 02
Edge caching popular destination searches dropped global median page load time to 1.2s.
Caching the searches people actually run beat caching everything. The long tail was never the load problem, and treating it as one had been wasting cache space.
- 03
Optimistic UI updates ensured users never felt lag during inventory booking surges.
Optimistic updates only work when the pessimistic case is honest. The short pricing TTL is what lets the interface move immediately without ever promising a sold-out room.
Three steps, one running total
The total comes before the passport
One round trip for two, from seats to a confirmed booking. Each stage shows what the traveler is asked, what's held for them, the address the stage lives at, and a total that's always its lines added up. Switch tabs, or use the arrow keys once one is focused.
- Asked at this stage
- Two seats on the seat map. Nothing personal yet.
- Why it behaves this way
- The fare is read from the short-TTL pricing layer, so a seat or fare that has just gone is never offered.
- Seats 14A and 14B on AL 318, held for you · 15:00 hold (illustrative)
- Base fare2 × $612.00$1,224.00
- Taxes & carrier fees2 × $148.40$296.80
- Seats 14A, 14Badded2 × $38.00 · Preferred$76.00
From a traveler’s first filter to a confirmed booking on their phone
Everything a traveler browses is served from cache or computed in their browser. Only checkout reaches origin, and it goes through a queue whose rate was found by load testing, never assumed.
- 01 · TriggerSearch & filter in the browserThe inventory index ships to the edge as a compact binary payload and filtering runs against it on the device: under 50 ms across 10,000+ listings, with no round trip per checkbox.
- 02 · QueueRate-admitting checkout queueCheckout is the only path that touches origin, and a queue admits it at a fixed rate instead of accepting everything and timing out under 10× normal traffic.
- 03 · EngineGraphQL pricing + staged checkoutThe server is called only where live price changes the answer. One validation schema is shared by the form and the API, so nothing the form accepts is rejected later.
- 04 · StateRedis pricing TTL + step state in the URLPricing lives on a short TTL, so a room that sold out seconds ago drops out of the list. Each checkout step holds its state in the URL, so back and refresh are safe.
- 05 · DeliveryStatic search & browse from cacheSearch and browse are fully static-renderable and serve from cache under any load; 1.2s median page load in field data. Checkout load-tested at 12× peak: 50,000 concurrent requests, zero server errors.
So a booking is a booking
Real inventory, a checkout that finishes, pages that stay fast
Gone stock is never offered
Live pricing sits in Redis on a short TTL, so a room that sold out seconds ago disappears from results before it can fail at checkout. The form validates against the same schema the API enforces, so the server never rejects what the traveler was allowed to submit.
No checkout left hanging mid-payment
Checkout sits behind a queue that admits a fixed rate instead of accepting everything and timing out. It was load-tested at 12× peak before the first holiday window: 50,000 concurrent booking requests, zero server errors. Step state lives in the URL, so a refresh returns to the same step of the same booking.
Slow pages no longer cost the mobile buyer
Search and browse serve entirely from cache under any load, and filters run in the browser in under 50 ms where each checkbox used to wait four seconds. Median page load is 1.2s, measured from real sessions weighted to the traffic mix.
Are travelers leaving somewhere between search and payment? Scope your build in 3 minutes.
Scope your buildNearby engagements
Data & AnalyticsDynamic pricing that reads demand in real time
A revenue-management engine that turns booking pace, seasonality, and local events into nightly rate recommendations across a multi-property portfolio.
Hotels & Accommodation · 12 weeks
Web PlatformsThe invoice and the trip it pays for were separate records, so nobody could say which trips were unpaid
A booking and invoicing system for a travel agency where every invoice line points at the trip component it bills, and every payment points at the bank account it landed in. "Which trips are unpaid" becomes a query instead of an afternoon.
Travel & Tourism · 10 weeks
Mobile AppsA trip assistant that still answers with roaming switched off
A companion app for a tour operator's guided trips, whose assistant answers questions about a traveler's own itinerary on-device. The moment people land abroad they turn data roaming off, and most apps go dark exactly when they're needed.
Tour Operators · 12 weeks
Let's talk
Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.














