# Low-Level Design: Hotel Reservation System (Availability, Pricing, Concurrency)

Source: https://www.techinterview.org/post/3233464423/lld-hotel-reservation-system/
Updated: 2026-07-05 · techinterview.org

## Low-Level Design: Hotel Reservation System

A hotel reservation system manages room inventory, bookings, availability checks, pricing, and cancellations. It is a common LLD interview question at companies like Airbnb, Booking.com, and Expedia.

## Core Entities


```
from dataclasses import dataclass, field
from enum import Enum
from datetime import date, timedelta
from typing import Optional
import uuid

class RoomType(Enum):
    SINGLE = "single"
    DOUBLE = "double"
    SUITE = "suite"

class RoomStatus(Enum):
    AVAILABLE = "available"
    OCCUPIED = "occupied"
    MAINTENANCE = "maintenance"

class BookingStatus(Enum):
    CONFIRMED = "confirmed"
    CANCELLED = "cancelled"
    CHECKED_IN = "checked_in"
    CHECKED_OUT = "checked_out"
    NO_SHOW = "no_show"

BASE_RATES = {
    RoomType.SINGLE: 100,
    RoomType.DOUBLE: 150,
    RoomType.SUITE: 300,
}

@dataclass
class Room:
    room_number: str
    room_type: RoomType
    floor: int
    capacity: int
    amenities: list[str]
    status: RoomStatus = RoomStatus.AVAILABLE

@dataclass
class Guest:
    guest_id: str
    name: str
    email: str
    phone: str

@dataclass
class Booking:
    booking_id: str
    guest_id: str
    room_number: str
    check_in: date
    check_out: date
    status: BookingStatus = BookingStatus.CONFIRMED
    total_price: float = 0.0
    created_at: date = field(default_factory=date.today)

    @property
    def nights(self) -> int:
        return (self.check_out - self.check_in).days
```


## Availability Check and Booking


```
class HotelService:
    def __init__(self):
        self._rooms: dict[str, Room] = {}
        self._guests: dict[str, Guest] = {}
        self._bookings: dict[str, Booking] = {}  # booking_id -> Booking
        # For availability: room -> list of (check_in, check_out, booking_id)
        self._room_calendar: dict[str, list[tuple]] = {}

    def add_room(self, room: Room) -> None:
        self._rooms[room.room_number] = room
        self._room_calendar[room.room_number] = []

    def find_available_rooms(self, check_in: date, check_out: date,
                              room_type: Optional[RoomType] = None) -> list[Room]:
        available = []
        for room in self._rooms.values():
            if room.status == RoomStatus.MAINTENANCE:
                continue
            if room_type and room.room_type != room_type:
                continue
            if self._is_available(room.room_number, check_in, check_out):
                available.append(room)
        return available

    def _is_available(self, room_number: str, check_in: date, check_out: date) -> bool:
        for (existing_in, existing_out, _) in self._room_calendar[room_number]:
            # Overlap: existing starts before requested ends AND existing ends after requested starts
            if existing_in < check_out and existing_out > check_in:
                return False
        return True

    def book_room(self, guest_id: str, room_number: str,
                  check_in: date, check_out: date) -> Booking:
        if not self._guests.get(guest_id):
            raise ValueError(f"Guest {guest_id} not found")
        room = self._rooms.get(room_number)
        if not room:
            raise ValueError(f"Room {room_number} not found")
        if room.status == RoomStatus.MAINTENANCE:
            raise ValueError(f"Room {room_number} is under maintenance")
        if not self._is_available(room_number, check_in, check_out):
            raise ValueError(f"Room {room_number} not available for requested dates")

        booking_id = str(uuid.uuid4())
        nights = (check_out - check_in).days
        total = self._calculate_price(room.room_type, nights, check_in)

        booking = Booking(
            booking_id=booking_id,
            guest_id=guest_id,
            room_number=room_number,
            check_in=check_in,
            check_out=check_out,
            total_price=total,
        )
        self._bookings[booking_id] = booking
        self._room_calendar[room_number].append((check_in, check_out, booking_id))
        return booking

    def _calculate_price(self, room_type: RoomType, nights: int,
                          check_in: date) -> float:
        base = BASE_RATES[room_type] * nights
        # Weekend surcharge: +20% if any night is Fri/Sat
        weekend_nights = sum(
            1 for i in range(nights)
            if (check_in + timedelta(days=i)).weekday() in (4, 5)  # Fri=4, Sat=5
        )
        surcharge = weekend_nights * BASE_RATES[room_type] * 0.20
        return base + surcharge
```


## Cancellation and Check-in


```
def cancel_booking(self, booking_id: str) -> float:
        booking = self._bookings.get(booking_id)
        if not booking or booking.status != BookingStatus.CONFIRMED:
            raise ValueError(f"Booking {booking_id} not found or not cancellable")

        # Cancellation policy: full refund if > 48h before check-in
        hours_until_checkin = (booking.check_in - date.today()).days * 24
        refund = booking.total_price if hours_until_checkin >= 48 else 0.0

        booking.status = BookingStatus.CANCELLED
        # Remove from calendar
        calendar = self._room_calendar[booking.room_number]
        self._room_calendar[booking.room_number] = [
            entry for entry in calendar if entry[2] != booking_id
        ]
        return refund

    def check_in(self, booking_id: str) -> None:
        booking = self._bookings.get(booking_id)
        if not booking or booking.status != BookingStatus.CONFIRMED:
            raise ValueError("Invalid booking for check-in")
        if date.today()  float:
        booking = self._bookings.get(booking_id)
        if not booking or booking.status != BookingStatus.CHECKED_IN:
            raise ValueError("Cannot check out: booking not in CHECKED_IN state")

        # Calculate late checkout fee if past noon
        # (simplified: charge one extra night if same day after checkout)
        booking.status = BookingStatus.CHECKED_OUT
        self._rooms[booking.room_number].status = RoomStatus.AVAILABLE
        return booking.total_price  # in production, add incidentals
```


## Pricing Strategy Extension


```
from abc import ABC, abstractmethod

class PricingStrategy(ABC):
    @abstractmethod
    def calculate(self, room_type: RoomType, nights: int, check_in: date) -> float:
        pass

class StandardPricing(PricingStrategy):
    def calculate(self, room_type: RoomType, nights: int, check_in: date) -> float:
        return BASE_RATES[room_type] * nights

class DynamicPricing(PricingStrategy):
    """Surge pricing based on occupancy rate."""
    def __init__(self, occupancy_rate: float):
        self.multiplier = 1.0 + max(0, (occupancy_rate - 0.7) * 2)  # 40% surge at 90% occupancy

    def calculate(self, room_type: RoomType, nights: int, check_in: date) -> float:
        return BASE_RATES[room_type] * nights * self.multiplier

class SeasonalPricing(PricingStrategy):
    PEAK_MONTHS = {6, 7, 8, 12}  # Summer and December
    def calculate(self, room_type: RoomType, nights: int, check_in: date) -> float:
        rate = BASE_RATES[room_type]
        if check_in.month in self.PEAK_MONTHS:
            rate *= 1.3  # 30% seasonal surcharge
        return rate * nights
```


## Concurrency: Preventing Double Booking

In a production system, use optimistic locking: store a version number on each room's booking list. When booking, read the current version, compute the new state, and update with `WHERE version = read_version`. If another transaction committed first, the update affects 0 rows — retry or surface conflict to user. Alternatively, use database-level `SELECT FOR UPDATE` on the room row to serialize concurrent bookings for the same room.

Asked at: [Airbnb Interview Guide](https://techinterview.org/airbnb-interview/)

Asked at: [Stripe Interview Guide](https://techinterview.org/stripe-interview/)

Asked at: [DoorDash Interview Guide](https://techinterview.org/doordash-interview/)

Asked at: [Lyft Interview Guide](https://techinterview.org/lyft-interview/)
