DEV Community

Cover image for Design a hotel booking system: the system design interview walkthrough
Gowtham Potureddi
Gowtham Potureddi

Posted on

Design a hotel booking system: the system design interview walkthrough

Design a hotel booking system: the system design interview walkthrough

The request to design a hotel booking system sounds like one line — list places, let guests book them — and that one line hides the whole interview. Picture it for a single inn and you reach for a table of rooms; picture it for Airbnb at world scale, where a guest searches a city for a date range and expects results in half a second, and where two people must never end up holding the same room for the same nights, and it becomes a search-plus-consistency problem with a dozen sharp edges. This is one of the most revealing hotel reservation system design prompts — the Airbnb-style question — because it splits cleanly into two halves that pull in opposite directions: a read-heavy availability search that can be a little stale, and a write-critical booking core that must be exactly right.

The round turns on one structural insight. "List places, let guests book them" hides a system with two very different jobs, and the listings catalogue is the easy one. The hard parts are finding which properties are free for a location and a date range, fast and at huge query volume, and booking one of them with strong consistency, so the same room is never sold twice for overlapping nights. Seen clearly, this is a search problem bolted to a reservation-consistency problem: discovery runs off a slightly-stale replica because it is only a shortlist, while the reservation must be an atomic check-then-reserve against the one authoritative record. Get that reframe and the rest of the system design interview is deciding how availability is modelled so date-range search is cheap, how two racing guests are resolved so nobody double-books, and how a hold keeps a room while a guest pays. This walkthrough follows the path a strong candidate draws on the whiteboard — requirements, estimate, API and data model, high-level design, then the deep dives — naming the follow-up the interviewer uses at each step to tell a memorised answer from real understanding.

Title card for a system design interview walkthrough on designing a hotel booking system like Airbnb, showing two halves — a fast availability search over a map and a calendar date range on one side, and a strongly consistent booking core that stops two guests double-booking the same room on the other — on a dark citrus-and-lavender background.

How the hotel booking round is actually run

A hotel booking round runs about 60 minutes on a shared canvas — you draw the boxes and arrows while you talk, and the interviewer interrupts. It is a favourite because the one-line problem opens onto deep water fast: the moment you draw "search, then book," the question becomes how do you know a place is free for those exact nights and what happens when two guests book the last free room at the same instant, and every answer after that compounds from those two. The clock is the full hour, so there is room to design both halves properly — but only if you do not sink the first twenty minutes into the listing and photo model.

The interviewer rarely lets "they book a room" stand. "Two guests hit Book on the last free room for the same week at the exact same instant — what happens, who gets it, and how do you make sure the other one doesn't also think they got it?" That question — a reservation race under concurrency — is the one a copied answer can never rehearse, and the one this round is really testing.

The rounds here are modelled on how the loop runs, not scraped from any candidate's recall. What matters for prep is the shape: a blank canvas, a full hour, and a follow-up after every decision you make.

The five-step framework for any HLD round

Every high-level design answer fits the same spine. Say it out loud at the start so the interviewer knows you have a plan, then walk it:

  1. Clarify requirements — functional and non-functional, and what you are explicitly not building.
  2. Estimate — search queries per second versus bookings per second, and the size of the availability data, to justify every later choice.
  3. High-level design — the boxes and arrows: the clients, the search path, and the booking core, and the line between the read path and the write path.
  4. Deep dive — the two or three decisions that actually decide this system.
  5. Follow-ups — bottlenecks, failure modes, and what you would change at ten times the scale.

What the debrief measures

The score is not "right or wrong design." A good HLD debrief reads on four dimensions, and every move below maps to one: problem solving (did you reach a split between a stale-tolerant search path and a strongly consistent booking core, with an atomic check-then-reserve), communication (did you say why search can be slightly stale but booking cannot, in a single sentence), technical execution (an availability model that makes date-range search cheap, a unique-constraint or locking strategy that stops double-booking, and a TTL hold for reserve-then-pay), and time management (did you spend your hour on search and the booking write, not on the listing photos and reviews model). Reading a walkthrough teaches the design; it does not teach you to deliver it on the clock while someone pushes back.

Step 1 — Requirements: what a hotel booking platform must do

Spend the first few minutes here. Jumping to boxes before you have scoped the problem is the fastest way to design the wrong system.

Type Requirement
Functional A guest searches properties by location, check-in and check-out dates, and number of guests; views a listing with its availability and price; books a room for a date range and pays; can cancel; a host lists a property and manages its availability and price. All parties see accurate availability.
Functional (optional) Reviews and ratings, map-based search, dynamic pricing, multi-room or multi-night discounts, messaging between guest and host — call these out, then scope most of them for later.
Non-functional Search is fast (results in well under a second) and can tolerate being slightly stale; booking is strongly consistent — the same room is never double-booked for overlapping dates; high availability; durable reservations and payments; scale to millions of listings and far more searches than bookings.

The sentence that earns points: "The listings catalogue is the easy part — the hard part is a read-heavy availability search that can be a little stale, and a booking write that must be strongly consistent, so the two live in separate systems." Say that and you have framed the entire design before drawing a box.

Step 2 — Capacity estimation (back of the envelope)

Estimation here is about proving one thing: search and booking have wildly different volumes and very different consistency needs, and the design must treat them as two systems.

Quantity Assumption Result
Bookings ~1.5 million bookings/day across the platform ~17 bookings/sec on average — a modest write rate that fits an ordinary transactional database; this is not the scaling problem
Searches every booking is preceded by many browses and searches search QPS is orders of magnitude higher than booking TPS — the read path is the high-volume one
Availability data millions of listings × a rolling calendar of bookable nights a large but bounded dataset of per-listing, per-night availability — cheap to index for date-range queries
Consistency a room is a single resource for a given night the booking write must be exactly-once per room-night; search results only need to be approximately current

Two conclusions fall out, and you should say both out loud. First, booking is the low-rate, high-stakes axis — seventeen writes a second is nothing for a normal database, so the reservation can afford strong consistency (a transaction, a lock, a unique constraint) without breaking a sweat. Second, search is the high-volume, low-stakes axis — it must scale to a flood of queries and can tolerate being a second behind reality, because it only produces a shortlist the booking step re-checks anyway. You have now pointed the interview at the two real deep dives before anyone asked.

Step 3 — API and data model

The API is deliberately small: a guest searches, views, reserves and pays; a host manages availability.

Operation Shape
search_listings(location, check_in, check_out, guests, filters) returns properties that are free for the whole date range and match the filters; served from the search path.
get_listing(listing_id) returns listing detail and its current availability calendar.
create_reservation(listing_id, check_in, check_out, guests, idempotency_key) attempts to reserve the room for the date range; the idempotency_key makes a retried or double-clicked request safe.
confirm_payment(reservation_id) confirms the held reservation once payment succeeds.
cancel_reservation(reservation_id) cancels and frees the nights.

The data model is thin. The reservation and the availability record are the source of truth for what is booked; the search index is a derived copy:

Entity Notes
listing listing_id, host_id, location, title, amenities, nightly price, photos — mostly static catalogue data.
availability (listing_id, date, status) — per-listing, per-night inventory (open / held / booked); the authoritative answer to "is this room free on this night?"
reservation reservation_id, listing_id, guest_id, check_in, check_out, state (pending / confirmed / cancelled / expired), idempotency_key — the durable record of a booking.
payment payment_id, reservation_id, amount, status — idempotent, keyed to the reservation.

Step 4 — High-level design: the search path and the booking core

Now the boxes. The clients — guest and host apps — talk through an API gateway to two clearly separated halves, a read path for discovery and a write path for booking:

  • Search service: answers search_listings from a search index (an Elasticsearch-style store) that holds listings with their location and per-night availability, so a location + date-range + guests + filters query is a fast index lookup.
  • Listing service: serves listing detail — description, amenities, photos, price — the mostly-static catalogue.
  • Reservation service: owns the booking database, the authoritative source of truth for reservations and availability, and runs the atomic check-then-reserve. Every booking write lands here.
  • Availability service: maintains the per-listing, per-night availability and streams changes out to the search index so discovery stays roughly current.
  • Payment service: takes payment for a held reservation and confirms it, idempotently, keyed on the reservation id.
  • Index replication: the booking database is the truth; availability changes are replicated into the search index, so search runs off a copy and never contends with booking writes.

Say explicitly that this is a read path and a write path kept in separate systems: the search path is high-volume and may be a second behind reality, the booking path is low-rate and strongly consistent. Naming that split unprompted — search off a replica for speed and scale, booking against the authoritative record re-checked at commit — is a signal the interviewer is listening for.

A hotel booking architecture diagram showing guest and host clients talking through an API gateway to a read path and a write path: a search service backed by a search index replicated off the booking database answers location and date-range queries, a listing service serves listing detail, and on the write side a reservation service owns the authoritative booking database re-checked at commit, with an availability service and a payment service, split into a slightly-stale read path and a strongly consistent write path.

Deep dive: availability search by location and date range

This is the first decision that actually decides the round, and the part that makes a hotel system different from a ticket system. The follow-up is always about freshness: your search said a place was free, but it just got booked — is that a bug?

The trick is to model availability so that "which properties in this area are free for these nights?" is a cheap lookup rather than a scan. Availability lives as per-listing, per-night inventory — one row per listing per bookable date with a status — or equivalently as date-range intervals; either way the question "is this listing open for every night from check-in to check-out?" is answerable without walking every reservation. That availability, together with each listing's location and attributes, is indexed into a search index that the search service queries:

Query element How it is served
Location a geo filter on the index (city, bounding box, or map viewport) — narrows to the right area cheaply.
Date range the index holds per-night availability, so "free for every night in the range" is a filter, not a join over reservations.
Guests / rooms a capacity filter on the listing attributes.
Price / amenities ordinary attribute filters and sorting on the indexed listing fields.
Freshness the index is a replica fed by availability changes, so it may lag reality by a second or two — acceptable, because booking re-checks.

The reason this works is the division of labour. Search only needs a shortlist of plausible rooms, so it runs off a replicated, eventually-consistent index — fast, horizontally scalable, and able to absorb far more queries than the booking core ever sees. The authoritative availability still lives in the booking database and the reservation step re-checks it at commit, so a stale search result is not a correctness bug: at worst a guest clicks a room taken moments ago and is told, at booking, to pick another. Saying that a stale search is fine precisely because the booking is exact is the sentence that separates a real answer from a hand-wave.

"Your search says a place is free but it got booked a second ago — is that a bug?" No — search runs off a replicated index that can lag reality by a second or two, because it only produces a shortlist. The authoritative availability is in the booking database, and the reservation step re-checks it inside a transaction, so a stale search result just means the guest occasionally has to pick another room at booking time. Search is allowed to be approximate because booking is exact.

An availability search diagram showing a guest query of location, check-in and check-out dates, guests and filters hitting a search service that reads a search index of listings with per-night availability, returning a ranked list of properties free for the whole date range, with a note that the index is a slightly-stale replica and the booking database is re-checked at reservation time.

System Design
HLD · Payments & marketplaces · 60 min
Design a hotel booking system out loud, with follow-ups

Practise on Zynter →

Deep dive: booking a room without double-booking

This is the decision the whole round is really about. Search is read-heavy but forgiving; the booking write is low-rate but unforgiving, and the follow-up is always the race: two guests book the last free room for overlapping dates at the same instant — who wins?

The one rule that must survive every failure is that a room is never booked twice for overlapping nights. The mistake candidates make is to read availability, decide the room is free, and then write the reservation as a separate step — two guests can both read "free" before either writes. The fix is to make check-availability-then-reserve a single atomic operation against the authoritative booking database, so exactly one writer can win. There are three tools, and a strong answer names the tradeoffs rather than picking one blindly:

Approach What it optimises / costs
Unique constraint / conditional update (optimistic) a unique key on (listing_id, date) or a conditional "update rows only if all nights are still open" lets the database itself reject the second writer; the loser gets a conflict and retries or picks another room. Cheap and lock-free, best when real contention on any one room is low — which is the common case across millions of listings.
Pessimistic row lock lock the listing's rows for the date range inside a transaction, check, write, commit; the second guest waits and then sees the room is gone. Simple to reason about and airtight, at the cost of holding locks — fine at this write rate, heavier under a true stampede.
Idempotency key a client-supplied key on create_reservation so a double-clicked or retried request collapses to one reservation, not two. Orthogonal to the two above — it defuses duplicate submissions rather than competing guests.

The shape to state plainly: wrap the availability check and the reservation write in one transaction, let a unique constraint or a lock decide the single winner, and carry an idempotency key so retries are harmless. Because contention is spread across millions of listings and date ranges, the optimistic path wins almost every race without blocking — but a single hot listing (a famous property on a holiday weekend) behaves more like the ticketing problem, where a thundering herd piles onto one resource and pessimistic locking or a queue earns its keep. Naming that hot-listing case as the sibling of the Ticketmaster on-sale round shows you know where the contention model changes.

"Two guests book the last free room for overlapping dates at the same instant — who wins and how?" I never read-then-write as two steps. The availability check and the reservation write happen in one transaction against the booking database, and a unique constraint on room-and-date (or a row lock over the range) means exactly one commit succeeds; the other gets a conflict and retries or is shown another room. A client idempotency key makes sure a double-click from the winner is still just one booking.

A reservation concurrency diagram showing two guests racing to book the same room for overlapping dates, an atomic check-then-reserve inside a single transaction where one writer wins via a unique constraint or conditional update and the other retries, an idempotency key defusing double-clicks, and a reserve-then-pay lifecycle where a pending hold with a TTL marks the dates unavailable during checkout and is confirmed on payment or released on expiry so inventory is never leaked.

System Design
HLD · Payments & marketplaces · 60 min
Defend the no-double-booking write out loud

Practise on Zynter →

Deep dive: holds, payment and consistency

Preventing double-booking decides who owns the room; this decides what happens in the seconds between "I'll take it" and "payment cleared" — and it is where inventory quietly leaks if you get it wrong. The follow-up is always about the gap: a guest reserves, then abandons the payment page — what happens to those nights?

A booking is not one instant; it is reserve-then-pay. If you only mark a room booked after payment, two guests can both pass the availability check while each is entering a card. If you mark it booked before payment and the guest vanishes, the room is stuck forever. The resolution is a short-lived hold:

  • Create a pending hold: on create_reservation, the reservation goes to state pending and the nights are marked held in availability — so the room is immediately unavailable to everyone else while this guest checks out. This is the write that wins the race in the previous deep dive.
  • Give the hold a TTL: the hold is valid for a short window (a few minutes). While it lives, no one else can book those nights; when it expires, the nights are freed.
  • Confirm on payment: when confirm_payment succeeds, the reservation moves pending → confirmed and the nights move held → booked. Payment is idempotent, keyed on the reservation id, so a retried charge never double-charges.
  • Release on timeout or failure: if payment fails or the guest abandons checkout, the hold expires and the nights return to open — automatically via a TTL on the record, or swept by a background job that reverts stale pending holds. Inventory is never leaked.

The point to state plainly: the hold turns the booking write and the payment into a small saga with a built-in escape hatch. The room is reserved the instant the guest commits (so the race has exactly one winner), an expired hold always frees the nights (so the room is never quietly lost), and idempotent payment makes retries safe. Strong consistency lives where it matters — the availability transition — while the slow, failure-prone payment step runs behind a hold that can always be rolled back.

"A guest reserves then abandons the payment page — what happens to those nights?" The reservation is a pending hold with a short TTL that marks the nights held, so no one else can take them during checkout. If payment never completes, the hold expires — via the record's TTL or a sweeper that reverts stale pending holds — and the nights flip back to open. The room is never double-booked and never permanently stuck; the hold is the rollback.

The follow-ups that decide the hotel booking round

Scan the probes from each deep dive and they rhyme. Master these five and you are ready for a version of the question that is not drawn exactly like this one.

  • "Why split search from booking instead of one database?" → Search is high-volume and tolerates being a second stale, booking is low-rate and must be strongly consistent, so search runs off a replicated index for speed and scale while booking writes hit the authoritative database re-checked at commit. (communication)
  • "How do you stop two guests double-booking the same room?" → Never read-then-write as two steps: the availability check and the reservation write are one transaction, and a unique constraint on room-and-date (or a row lock over the range) lets exactly one commit win; the other retries or is shown another room. (problem solving)
  • "Optimistic or pessimistic locking — which and why?" → Optimistic (unique constraint / conditional update) for the common case where contention on any one listing is low, because it is lock-free and scales; pessimistic locking or a queue for a single hot listing on a peak weekend, which behaves like the Ticketmaster stampede. (technical execution)
  • "A guest reserves then never pays — how do you not lose the room?" → The reservation is a pending hold with a TTL that marks the nights held; on payment it confirms, on timeout or failure the hold expires and the nights free up via TTL or a sweeper, so inventory never leaks. (technical execution)
  • "Your search showed a room that was already taken — bug?" → No: search runs off an eventually-consistent replica and only produces a shortlist; the booking step re-checks the authoritative availability in a transaction, so a stale result just means the guest occasionally picks another room. (problem solving)

Each maps to a debrief dimension. Reading this list teaches you the answers; it does not teach you to deliver them while someone interrupts — that only comes from saying them out loud.

How to prepare: a one-week plan

You do not need fifty systems. You need this one cold and the framework automatic, so the next "design X" is just new nouns in the same five steps.

  • Days 1–2: Learn the five-step framework until it is reflex. Do the hotel booking system end to end on paper — requirements, estimate, API, data model, and the search-path/booking-core boxes.
  • Days 3–4: Drill the availability search deep dive: per-night inventory, the search index off a replica, location + date-range + guests + filters, and why a stale search is fine because booking re-checks. Say each tradeoff in one sentence.
  • Days 5–6: Own the no-double-booking write and the hold lifecycle — atomic check-then-reserve, optimistic versus pessimistic locking, the idempotency key, and the reserve-then-pay hold with its TTL and sweeper. Answer each of the five follow-ups out loud.
  • Day 7: A full timed 60-minute round, narrating the whole way, with someone (or something) interrupting. This is the rep that counts.

This post is the deep walkthrough behind one row of the broader Top 25 system design interview questions — once the hotel booking system is automatic, the same spine carries the rest of the list.

Frequently asked questions

How do I design a hotel booking system step by step?

Follow the five-step HLD framework. Clarify requirements (search by location, dates and guests; view a listing; book a room for a date range and pay; host manages availability; everyone sees accurate availability); estimate to show the split — a modest ~17 bookings/sec that fits a normal transactional database, against far higher search volume; define the small API (search_listings, get_listing, create_reservation with an idempotency key, confirm_payment, cancel_reservation) with a thin listing/availability/reservation/payment model; draw the design (clients through a gateway to a search path — search service over a replicated index — and a booking core — reservation service over the authoritative booking database, plus availability and payment services); then deep-dive the decisions that matter — availability search by date range, booking without double-booking, and holds with reserve-then-pay. Finish on the follow-ups about staleness and abandoned payments.

How does a hotel booking system prevent double booking?

By never reading availability and writing the reservation as two separate steps. The availability check and the reservation write happen in a single atomic transaction against the authoritative booking database, and a unique constraint on (listing_id, date) — or a pessimistic row lock over the date range — ensures exactly one writer commits for a given room-night. The guest who loses the race gets a conflict and either retries or is shown another room. A client-supplied idempotency key on the reservation request collapses a double-clicked or retried submission into a single booking. Strong consistency on that one write is cheap because the booking rate is low — a few tens of writes a second platform-wide.

Optimistic or pessimistic locking for room reservations?

Both are valid; name the tradeoff. Optimistic concurrency — a unique constraint or a conditional "reserve only if still open" update with a retry on conflict — is lock-free and scales well, and it is the right default because contention on any single listing is usually low across millions of properties. Pessimistic locking — locking the listing's date rows inside a transaction so the second guest waits — is airtight and simple to reason about, and it earns its keep on a single hot listing during a peak weekend, where many guests pile onto one room and the pattern starts to resemble a ticketing on-sale stampede. A strong answer defaults to optimistic and calls out the hot-listing case as the exception.

How does availability search by date range work?

Availability is modelled as per-listing, per-night inventory (one row per listing per bookable date with a status) or equivalently as date-range intervals, so "is this listing free for every night from check-in to check-out?" is a filter rather than a scan over reservations. That availability, with each listing's location and attributes, is indexed into a search index (an Elasticsearch-style store) that the search service queries by location, date range, guests, price and amenities. The index is a replica fed by availability changes, so it can lag reality by a second or two — which is fine, because it only produces a shortlist and the booking step re-checks the authoritative availability. Search is allowed to be approximate precisely because booking is exact.

What is a booking hold and why does it expire?

A hold is a short-lived pending reservation that marks the nights held the instant a guest commits, before payment completes, so no one else can book those nights during checkout. It exists to close the gap between "I'll take it" and "payment cleared": without it, two guests could both pass the availability check while each enters a card. The hold carries a TTL of a few minutes; if payment succeeds the reservation confirms and the nights become booked, and if payment fails or the guest abandons the page the hold expires — via the record's TTL or a background sweeper that reverts stale holds — and the nights return to open. That is what stops inventory from leaking while still guaranteeing the room is never double-booked.

How do I practise a system design interview out loud?

Reading a walkthrough is not the same as defending a design while someone interrupts. On Zynter, the HLD track runs live voice rounds modelled on how real system design loops run — you talk through a shared design on the canvas, and a voice interviewer pushes with the follow-ups above one at a time, then gives you a debrief on problem solving, communication, technical execution, and time management. The score is a practice signal, not a hiring decision.

Practise it on Zynter

You can read every reservation state. The round is won on search freshness and the no-double-booking write.

Pick the Hotel Booking round, draw the search-path and booking-core design on the canvas, and defend it out loud to a voice interviewer that keeps asking "two guests book the last room at the same instant — who wins?" and "a guest reserves then never pays — what happens to those nights?" — then read the debrief. Zynter.ai is on-demand mock interviews modelled on real company loops, with 150+ rounds across 20 companies including the HLD system design track (Sep 2026).

Practise the Hotel Booking round →
Browse all interview tracks

Top comments (0)