A hotel booking is a promise about a room category across several nights, at a particular price and under particular conditions. It is not simply a counter that decreases when someone clicks Reserve. The stay may span different nightly rates, occupancy restrictions and cancellation deadlines, while several sales channels advertise the same physical capacity.

That combination makes hotel booking an excellent system design problem. Search must be fast, availability must be checked carefully, and the final reservation must survive payment delays and external-channel failures without misleading the traveller about what has been confirmed.

Introduction

In this interview design, we will build a platform where travellers search properties, compare room offers and book a stay. Hotels can manage room types, rates, restrictions and inventory, while integrations exchange availability and reservations with external distribution channels.

We will focus on date-range inventory, the distinction between room types and rate plans, quote consistency and channel synchronisation. Those concerns extend beyond the familiar question of preventing two transactions from claiming the last item. A reservation can be valid for Friday and Saturday yet impossible for Sunday, or available under one occupancy rule but not another.

The architecture will separate a flexible search view from authoritative booking state. It will use durable reservation and payment workflows, preserve the exact terms accepted by the traveller and make uncertain external outcomes visible. The design is illustrative and does not describe the internal implementation of any named travel provider.

Define the Booking Model and Scope

Assume the platform directly manages inventory for participating hotels and also receives reservations from connected channels. Travellers book one property per checkout, possibly with several rooms. Multi-property itineraries are outside the initial scope because they introduce a separate coordination problem across independent suppliers.

Search inputs include destination, check-in and check-out dates, room count and the adults and children assigned to each room. Children may have different occupancy or pricing rules, so a single total guest count is insufficient for many offers.

An offer includes a room type, rate plan, meal arrangement, cancellation terms, payment timing and a price breakdown. A double room with breakfast and free cancellation is a different commercial product from the same room category with a non-refundable room-only rate.

For a concrete external example, Booking.com's accommodation search documentation exposes dates, occupancy, product policies and price components. These fields illustrate why a booking request needs more than a hotel identifier and a nightly headline price.

Our availability target is high for browsing and existing-reservation access. Creating a reservation prioritises correct inventory and terms over accepting writes during an uncertain ownership state. An honest “please try again” is preferable to a confirmation that the hotel cannot honour.

Estimate the Workload

Suppose the platform serves ten million daily visitors, each making five availability searches. Fifty million searches per day average roughly 580 per second, with a peak ten times higher around major travel campaigns or local evening hours.

If one in one hundred visitors completes a booking, the platform handles one hundred thousand bookings per day, averaging slightly more than one per second. Booking traffic is much smaller than search traffic, but individual popular properties can still experience intense contention during a major event.

Assume two hundred thousand properties, twenty room types per property and an inventory horizon of eighteen months. A dense room-type-per-night representation would contain billions of rows. Actual property sizes vary, so partitioning, sparse creation or bounded inventory calendars deserve consideration.

Search need not evaluate every nightly row for every property on each request. Location filtering, precomputed availability summaries and short-lived cached results can narrow candidates. Final booking still validates the exact dates and product against the authoritative state.

Also estimate channel updates. A hotel changing a year's rates across many room types can generate more integration traffic than ordinary booking events. Bulk calendar maintenance needs batching and backpressure so that it does not delay last-room availability updates.

Separate Property, Room Type, Rate Plan and Inventory

A property holds location, amenities, policies and descriptive content. A room type describes a category such as Deluxe Double, including occupancy capacity and physical characteristics. Individual room numbers are operational units assigned according to hotel policy, often closer to arrival.

A rate plan defines commercial rules: cancellation, meals, payment timing, minimum stay and related restrictions. Several rate plans can sell the same underlying room-type inventory. Treating each rate plan as an independent stock pool would accidentally multiply the hotel's capacity.

Property
property_id, time_zone, location, policies

RoomType
room_type_id, property_id, occupancy_rules

RatePlan
rate_plan_id, room_type_id, policy_version

NightInventory
room_type_id, stay_date, sellable, held, reserved

NightRate
rate_plan_id, stay_date, occupancy_band, amount, currency

Sellable capacity reflects rooms available for sale after maintenance blocks and other operational adjustments. Held and reserved quantities consume that shared capacity. Derived available quantity is useful for display, but updates must preserve the underlying invariant rather than trusting a stale cached number.

Keep policy and rate versions. A later hotel edit must not retroactively change the cancellation terms of an already confirmed booking. Reservations store an immutable snapshot or durable reference to the accepted commercial contract.

Model a Stay as Local Calendar Nights

A stay from 10 October to 13 October consumes the nights beginning on the 10th, 11th and 12th. Check-out is excluded. Using a half-open date interval avoids accidentally consuming the next guest's arrival night.

Inventory dates belong to the property's local calendar. They are not twenty-four-hour durations measured from the traveller's device clock. Daylight-saving transitions do not create an extra hotel night or remove one; stay dates remain calendar dates under the property's rules.

Operational timestamps such as hold expiry are different. Store them as unambiguous instants, typically UTC, while retaining the property's time zone for displaying local deadlines and evaluating policies. A cancellation deadline of 18:00 local time requires a defined time-zone conversion.

A three-night request for two rooms succeeds only if every night can support two additional rooms of the required category under the applicable restrictions. Taking an average across the stay is incorrect. Availability of five rooms on Friday cannot compensate for zero on Saturday.

Minimum length of stay, closed-to-arrival and closed-to-departure restrictions belong to the offer evaluation. They are not necessarily equivalent to zero inventory. A hotel may have capacity but deliberately decline a one-night booking that would create an undesirable gap.

Build Search for Speed and Explainable Results

Use a search index for geography, property attributes, room characteristics and broad filtering. A cached availability projection can identify likely candidates for the requested dates and occupancy. The projection is optimised for browsing and may lag authoritative changes briefly.

Search returns indicative offers with clear dates, occupancy and price context. The user should not have to discover at the final step that the headline price covered one adult while their request contained two adults and a child.

Cache keys need the dimensions that affect the result: property or destination, stay dates, room allocation, occupancy ages where relevant, currency and applicable eligibility rules. Omitting cancellation or meal requirements can return a superficially similar but commercially different offer.

Keep cache lifetimes and invalidation proportionate to volatility. Descriptive property text can live much longer than last-room availability. A booking event can invalidate or update the relevant date-range projection without requiring every property page to be rebuilt.

Do not expose internal uncertainty as a false guarantee. A search result means an offer was recently available under the search contract. A later quote and hold establish stronger commitments. Measure the rate at which selected search offers disappear so that stale projections become an observable quality issue.

Create a Versioned Quote before Checkout

When the traveller selects an offer, recompute or confirm the exact stay, occupancy, price and policies. Create a quote identifier with a short validity period and a snapshot of what the traveller is being asked to accept.

Quote
quote_id, property_id, room_allocations
check_in, check_out, nightly_breakdown
taxes_and_fees, total, currency
cancellation_policy_version, payment_terms
inventory_version_reference, expires_at

Separate included charges from amounts payable at the property. A total that mixes a prepaid amount with an on-arrival fee without explanation creates a poor customer experience even when the arithmetic is technically correct.

Use decimal or integer monetary representations with explicit currency and rounding rules. Sum the quoted nightly components according to the pricing contract rather than multiplying one displayed average rate by the number of nights and hoping the result matches.

Price changes require a product rule. The platform can honour a still-valid locked quote if the supplier contract supports it, or return a revised quote requiring acceptance. It should not silently charge more because a rate changed while the traveller entered their details.

A quote alone need not reserve inventory. If it does not, say so in the design and create a separate hold at the appropriate checkout stage. Conflating price validity and inventory ownership makes expiry and recovery unnecessarily ambiguous.

Reserve the Whole Date Range Atomically

For directly managed inventory, create a hold only when all required room-type nights can be reserved together. Keep the rows for one property's booking transaction within a suitable database partition where possible.

Lock or conditionally update the relevant nightly inventory rows in a consistent order, such as room_type_id followed by stay_date. Validate capacity for every requested night and room allocation, then increment held quantities and insert the hold record in the same transaction.

Begin transaction
Lock all required room-type nights in a fixed order
Verify each night has enough uncommitted capacity
Insert hold and immutable room-night allocations
Increase held quantities for those allocations
Commit transaction

If any night fails validation, roll back the entire operation. Reserving Friday and Saturday while failing Sunday is not a usable three-night booking unless the product explicitly offers split stays and the traveller accepts one.

Keep the transaction short. Payment authorisation and user interaction happen outside the inventory lock. PostgreSQL's explicit locking documentation explains row locks and why consistent lock ordering helps avoid deadlocks. Deadlock or serialisation failures still need bounded transaction retries.

The hold records its expiry instant and the exact allocations it owns. Releasing it uses those recorded allocations, preventing a later rate-plan edit or room-type remapping from returning capacity to the wrong inventory pool.

Limit active holds per traveller or session and monitor repeated abandoned holds. Otherwise a small number of clients can temporarily remove a property's inventory without making genuine bookings. Expiry workers must release capacity idempotently, and the booking transaction must check the hold's authoritative state rather than relying on a countdown displayed in the browser. Browser time is useful for guidance, not for deciding who owns the last room-night.

Distinguish Room-Type Capacity from Room Assignment

Room-type inventory assumes that rooms in the category are sufficiently interchangeable for the stay being sold. The hotel may assign a physical room number later, balancing cleaning, accessibility requests, maintenance and guest preferences.

That assumption needs qualification. If individual rooms already have fixed assignments or maintenance intervals, positive aggregate capacity on every night may not guarantee one continuous physical room is available for the entire stay. Different rooms might be free on different nights.

For a promise that the guest will not move rooms, the allocation system must preserve feasible continuous assignment, either by assigning a unit at booking or by validating that the category's operational constraints permit later assignment. Do not use simple daily counters for non-fungible inventory without addressing this gap.

Requests such as a particular floor or bed arrangement may be preferences rather than guarantees. Store the distinction and communicate it consistently. Guaranteed accessible accommodation or a specific unit requires stronger allocation rules than a general request in a notes field.

Out-of-service changes also need safeguards. Reducing sellable capacity below confirmed commitments should create a visible operational exception, not silently invalidate reservations. The hotel needs a relocation or inventory-resolution process that preserves the record of what was promised.

Coordinate Booking and Payment as a Workflow

A reservation and a payment provider do not share one ordinary database transaction. Model their states separately and connect them through a durable workflow with stable operation identifiers.

Reservation: held -> confirming -> confirmed
-> expired
-> cancelled

Payment: pending -> authorised -> captured
-> failed
-> outcome_unknown
-> refund_pending -> refunded

The exact sequence depends on whether the rate is prepaid, deposit-based or payable at the hotel. For a prepaid example, obtain the required payment result while the inventory hold remains valid, then atomically convert held allocations into confirmed reservations under the chosen policy.

Use idempotency keys for payment operations and reservation submission. Stripe's idempotent request documentation provides a concrete example of a provider contract for retrying requests. The platform still needs its own stable booking identity and durable payment references.

If a payment succeeds after inventory ownership has been lost, do not automatically revive the expired hold. Follow a defined reconciliation policy, such as checking whether the stay can still be allocated and otherwise arranging a void or refund. Confirmation must reflect an inventory-backed reservation, not simply a successful payment event.

An uncertain provider response remains uncertain until reconciled. Repeating the operation with a new identity can create a second charge or supplier booking. Persist the original reference and query or retry under the provider's documented semantics.

Integrate External Sales Channels Explicitly

Hotels often sell the same capacity through their own website and several distributors. A channel manager exchanges availability, rates, restrictions and reservations. Expedia's lodging API overview illustrates these distinct integration responsibilities.

Choose an inventory ownership model. A central shared pool offers efficient utilisation but depends on timely channel updates. Allocated channel quotas reduce some oversell races but can leave rooms unsold on one channel while another has exhausted its allowance.

Neither model makes external systems participate in our local transaction. A reservation may arrive from a channel based on availability it saw before our latest update. Safety buffers, stop-sell rules and supplier agreements can reduce exposure, but the design must acknowledge that synchronisation latency creates operational risk.

Use durable outbound updates with version or sequence information where the channel supports it. An older availability message arriving after a newer one must not reopen inventory that was just closed. If the external protocol cannot enforce ordering, serialise updates appropriately and reconcile the resulting state.

Inbound reservations use the channel's stable reservation identifier for deduplication. Persist the original payload, map room and rate identifiers explicitly and process changes or cancellations as versions of the same external booking rather than unrelated new requests.

Reconcile Channels after Failure

A successful outbound HTTP response may mean the channel accepted an update, not that every downstream storefront immediately reflects it. Track acknowledgement separately from observed synchronisation state where the integration makes that distinction available.

When a channel is unavailable, retain updates durably and coalesce replaceable availability snapshots by room type and date range. Do not coalesce distinct reservations or cancellations that represent separate business facts. Current inventory state and booking history need different recovery strategies.

On reconnection, compare the authoritative reservation ledger with the channel's retrievable state and send a fresh bounded availability snapshot. Replaying every stale decrement and increment blindly can reproduce old intermediate states in the wrong order.

Monitor channel lag, unacknowledged updates and unmatched reservation identifiers. An integration that is technically returning 200 but mapping all bookings to the wrong room type is not healthy. Semantic reconciliation catches failures that transport metrics cannot.

Define a degraded mode for stale channels. For example, stop selling the last few rooms through a channel whose inventory state is too old, under the hotel's configured policy. This trades some sales opportunity for a reduced chance of an unfulfillable reservation.

Make Cancellation a Versioned Business Operation

Cancellation evaluates the policy captured at booking, the property's local deadline rules and the current reservation state. A hotel changing its public policy today should not retroactively remove a traveller's previously accepted free-cancellation period.

Record the cancellation request and its idempotency identity. In the inventory transaction, move the reservation to the appropriate cancelled state and release each owned room-night exactly once. Duplicate requests or channel notifications must not increment availability repeatedly.

Refunding is a separate asynchronous operation with its own durable status. The booking can be cancelled while the refund is pending. The customer interface should display that distinction rather than implying that money returned immediately when inventory was released.

Some rates charge a penalty or retain a deposit. Calculate it from the stored contract and produce an auditable breakdown. Do not recalculate from today's room rates or an updated cancellation policy.

Supplier-confirmed reservations may require acknowledgement from the external channel before cancellation becomes final. Represent requested, pending and confirmed cancellation states when needed. A local button click cannot guarantee that an independently managed supplier has released its obligation.

Treat Amendments as Inventory Changes

Changing stay dates is not merely editing two fields. It can release some room-nights, acquire others, change the rate and cross a cancellation boundary. The platform should create an amendment quote describing the new terms and any payment difference.

For inventory managed in one database, calculate the net allocation change and acquire the additional nights atomically before releasing the old commitment according to the workflow. Preserve the original reservation if the new dates cannot be secured.

For an external supplier, amendments may be supported directly or require a cancel-and-rebook workflow. Do not present those as equivalent: cancelling first can lose the original room, while booking first can temporarily create two payable reservations.

Multiple-room bookings also require explicit partial-success rules. A family asking for two rooms should not receive one confirmed room and an unexplained failure for the other. If partial booking is allowed, the traveller must understand and accept the resulting arrangement.

Keep amendment history and supplier references. Confirmation messages, invoices and support screens should identify the current version while retaining the earlier terms needed to explain charges or resolve disputes about what changed.

Walk Through a Three-Night Booking

Consider a traveller booking one Deluxe Double from Friday to Monday. The stay consumes Friday, Saturday and Sunday nights. The authoritative available quantities are two, one and three rooms respectively, so the stay is constrained by Saturday's final available room.

The selected rate quotes £120 for Friday, £180 for Saturday and £110 for Sunday, with all charges in this hypothetical example included. The total is £410. Displaying an average nightly price is acceptable as an additional explanation, but the immutable quote retains all three nightly components and the exact cancellation deadline.

The hold transaction locks the three inventory rows, validates one room on each night and records the allocations together. Available capacity becomes one, zero and two. A second traveller searching the same dates may still see a cached result, but their authoritative hold attempt fails because Saturday is no longer available.

The first traveller completes payment while the hold remains valid. The confirmation transaction moves one unit on each night from held to reserved without changing the total capacity consumed. An outbound event updates search projections and channel availability. A lost confirmation response is recovered by querying the same reservation identity, not by creating another booking.

Later, the traveller requests a change to Saturday through Tuesday. Saturday and Sunday overlap the existing stay, Friday would be released, and Monday night must be acquired. Suppose Monday has no availability. The amendment fails without releasing Friday or altering the original confirmed booking.

If Monday later becomes available, a new amendment quote may have a different total and policy. The traveller accepts that quote, and the workflow secures the additional night, records the revised allocations and handles the payment difference under its durable state machine. The original contract remains in history.

Now imagine an external channel sends a duplicate notification for a separate booking. Deduplication by channel reservation identifier prevents a second inventory decrement. If that booking conflicts with stale channel availability, the system records an inventory exception and alerts the hotel's resolution process rather than allowing counters to hide the discrepancy.

This example shows why the design keeps several identities: the quote describes accepted terms, the hold owns temporary room-nights, the reservation records the confirmed stay and the amendment records a later change. Payment and channel references explain external actions. Combining everything into one mutable booking row would make retries, price history and partial failures much harder to reason about.

Scale the Hot Paths without Weakening Ownership

Partition transactional inventory by property or another boundary that keeps a typical booking's nights together. This makes most reservations local to one shard and avoids distributed transactions across unrelated hotels.

A major concert can make one property unusually hot. Apply bounded admission, short transactions and measured contention controls. Spreading that property's counters across random shards may increase throughput but complicates the invariant that every requested night remains within capacity.

Separate browsing traffic from booking writes. Search replicas and caches should not consume all resources needed to confirm an existing hold. Likewise, bulk rate imports need a lower-priority path than time-sensitive inventory changes.

Cache property media through a CDN and store descriptive content separately from rapidly changing inventory. The service should not read the reservation database to serve every image or amenity list.

Keep a single authoritative write owner for a property's inventory during regional failover unless the design explicitly supports a stronger distributed coordination model. Two regions accepting last-room bookings from stale counters would undermine the central promise of the system.

Observe the Traveller's Complete Journey

Track search latency, offer-selection failure rate, quote changes, hold creation, payment completion and final confirmation separately. A fast search service can still deliver a poor experience if a large fraction of selected rooms disappear during checkout.

Measure inventory exceptions, expired holds, payment outcomes awaiting reconciliation and channel synchronisation age. These metrics identify obligations that need resolution, not merely endpoint errors that happened in the past.

Use a stable correlation identifier across quote, hold, reservation, payment and channel records. Support staff should be able to answer whether a traveller owns a room, whether money was collected and whether a supplier confirmed the booking without manually comparing unrelated logs.

Protect personal and payment information in those diagnostics. Store provider tokens and references rather than raw card data, and restrict access to guest details according to operational roles. Audit changes to rates, capacity and reservation state.

Test concurrent overlapping stays, partial nightly shortages, out-of-order channel updates, duplicate bookings and lost payment responses. Include daylight-saving deadlines and rooms temporarily removed from service. The important result is that each promise remains explainable and backed by the correct inventory and terms.

Summary

A hotel booking platform combines fast discovery with precise commitments. Model room types, rate plans and physical inventory separately, and evaluate every night of a stay under the property's local calendar and occupancy rules.

Use versioned quotes, atomic room-night holds and durable payment workflows to connect the selected offer to a confirmed reservation. Cancellation and amendment are inventory and contract operations, not simple status edits. External channels require explicit ownership, deduplication, ordered updates and reconciliation.

The strongest design remains honest during uncertainty. Search may show a recent view, but confirmation must represent a real commitment. When payments, suppliers or channels disagree, preserve the evidence and resolve the workflow without inventing availability or silently changing what the traveller accepted.