Elevator System at Scale

📋 Jump to Takeaways

Elevator system design is an object-oriented design (OOD) problem more than a distributed systems problem. The challenge is dispatching multiple elevators efficiently to minimize wait time and travel time across a building. It tests state machine design, scheduling algorithms, and handling concurrent requests.

Step 1: Elevator Requirements

Elevator Functional Requirements

  • Handle hall calls (user presses up/down button on a floor)
  • Handle car calls (user presses destination floor inside elevator)
  • Dispatch the optimal elevator for each hall call
  • Move elevators between floors, opening/closing doors
  • Support multiple elevators in a building
  • Emergency stop and fire mode

Elevator Non-Functional Requirements

Metric Target
Building size Up to 50 floors
Elevators 4-8 per building
Average wait time < 30 seconds during peak
Throughput Handle 200 requests/minute at peak (morning rush)
Safety Doors don't close on passengers, emergency stop always works
Fairness No request starved indefinitely

Step 2: Elevator Core Components

State Machine (Per Elevator)

Each elevator is a state machine:

         ┌──────────┐
         │   IDLE   │
         └────┬─────┘
              │ (request received)

         ┌──────────┐
    ┌───►│  MOVING  │◄───┐
    │    │  (UP/DN) │    │
    │    └────┬─────┘    │
    │         │ (arrived │
    │         │  at stop)│
    │         ▼          │
    │    ┌──────────┐    │
    │    │  STOPPED │    │
    │    │  (doors  │────┘
    │    │   open)  │ (more stops in queue)
    │    └────┬─────┘
    │         │ (no more stops)
    │         ▼
    │    ┌──────────┐
    └────│   IDLE   │
         └──────────┘

States:

  • IDLE: stationary, no pending requests
  • MOVING_UP: traveling upward, serving requests in that direction
  • MOVING_DOWN: traveling downward
  • STOPPED: doors open, loading/unloading passengers

Key Objects

Building:
  floors: int
  elevators: Elevator[]
  dispatcher: Dispatcher

Elevator:
  id: int
  current_floor: int
  direction: UP | DOWN | IDLE
  state: MOVING | STOPPED | IDLE
  stop_queue: SortedSet<int>  (floors to visit)
  capacity: int
  current_load: int

Request:
  type: HALL_CALL | CAR_CALL
  floor: int
  direction: UP | DOWN (for hall calls)
  timestamp: int

Step 3: Elevator Dispatch Algorithms

Algorithm 1: FCFS (First Come First Served)

Assign each request to the first available elevator.

  • Pros: Simple
  • Cons: Terrible efficiency. An elevator on floor 1 might be sent to floor 50 while another sits idle on floor 48.

Algorithm 2: SCAN (Elevator Algorithm)

Each elevator moves in one direction, serving all requests in that direction, then reverses. Like a disk head sweep.

Elevator on floor 5, moving UP, stops queued: [7, 12, 15]
  → New request: floor 9 (UP) → insert into queue: [7, 9, 12, 15]
  → New request: floor 3 (DOWN) → serve after reversing
  • Pros: Fair, no starvation, predictable
  • Cons: Doesn't optimize for wait time. A closer idle elevator might be better

Algorithm 3: LOOK (Optimized SCAN)

Like SCAN but reverses direction when there are no more requests ahead (doesn't travel to the top/bottom floor unnecessarily).

Algorithm 4: Nearest Elevator with Direction Awareness

The most practical algorithm for multi-elevator systems:

For each hall call, score every elevator:

score(elevator, request) =
  if elevator is IDLE:
    distance(elevator.floor, request.floor)
  if elevator is moving TOWARD request AND same direction:
    distance(elevator.floor, request.floor)  (will pass by)
  if elevator is moving TOWARD request BUT opposite direction:
    distance(elevator.floor, request.floor) + penalty
  if elevator is moving AWAY from request:
    remaining_travel + distance_back + penalty

Assign to elevator with lowest score.

Why direction matters: An elevator on floor 10 moving UP is better for a floor-12-UP request than an elevator on floor 11 moving DOWN (which must finish its downward trip first).


Step 4: Elevator Deep Dive

Dispatch Logic (Multi-Elevator)

on_hall_call(floor, direction):
  best_elevator = null
  best_score = infinity

  for elevator in building.elevators:
    score = calculate_score(elevator, floor, direction)
    if score < best_score:
      best_score = score
      best_elevator = elevator

  best_elevator.add_stop(floor)
  illuminate_indicator(floor, direction, best_elevator.id)

Score calculation:

calculate_score(elevator, target_floor, target_direction):
  if elevator.state == IDLE:
    return abs(elevator.current_floor - target_floor)

  if elevator.direction == UP and target_direction == UP:
    if elevator.current_floor <= target_floor:
      return target_floor - elevator.current_floor  // on the way
    else:
      return remaining_up + full_down + (target_floor - bottom)

  if elevator.direction == DOWN and target_direction == DOWN:
    if elevator.current_floor >= target_floor:
      return elevator.current_floor - target_floor  // on the way
    else:
      return remaining_down + full_up + (top - target_floor)

  // Opposite direction: must complete current trip first
  return remaining_trip + distance_back_to_target

Stop Queue Management

Each elevator maintains a sorted set of floors to visit:

Elevator moving UP, current floor 5:
  up_stops:   [7, 9, 12]   (serve these going up)
  down_stops: [10, 4, 2]   (serve these after reversing)

Arrives at floor 7:
  → open doors, remove 7 from up_stops
  → check for new car calls (passenger presses 14) → add 14 to up_stops
  → close doors, continue to 9

Two queues per elevator:

  • Stops in current direction (served now)
  • Stops in opposite direction (served after reversal)

Handling Peak Traffic (Morning Rush)

8-9 AM: everyone goes from lobby (floor 1) to their office floor.

Optimization, Zoning:

Elevator A: serves floors 1-12
Elevator B: serves floors 1-25 (express to 13, then local 13-25)
Elevator C: serves floors 1-50 (express to 26, then local 26-50)

Optimization, Lobby dispatch:

  • Group passengers by destination zone
  • Fill one elevator with floors 10-15 passengers, another with 20-25
  • Reduces stops per trip (express behavior)

Capacity and Load Awareness

Don't dispatch a full elevator:

on_hall_call(floor, direction):
  for elevator in building.elevators:
    if elevator.current_load >= elevator.capacity * 0.8:
      skip  // don't assign more passengers to a nearly full car
    score = calculate_score(...)

Weight sensors: Real elevators use weight to estimate passenger count. If overloaded, doors reopen and buzzer sounds. Don't move.

Emergency Handling

FIRE_MODE:
  1. All elevators return to lobby (floor 1)
  2. Doors open, all passengers exit
  3. Elevators go out of service
  4. Only firefighter key enables operation

EMERGENCY_STOP:
  1. Immediately stop motor
  2. Open doors if between floors → move to nearest floor first
  3. Trigger alarm

POWER_FAILURE:
  1. Switch to battery backup
  2. Move all elevators to nearest floor
  3. Open doors
  4. Shut down

Monitoring and Metrics

Track system health:

  • Average wait time: from hall call to door opening
  • Average travel time: from boarding to destination
  • Utilization: percentage of time each elevator is moving vs idle
  • Longest wait: detect starvation (no request should wait > 2 minutes)

Key Takeaways

  • Each elevator is a state machine

    IDLE → MOVING → STOPPED → IDLE (or back to MOVING). The state machine governs when doors open, when to reverse direction, and when to accept new requests. Keep transitions explicit and testable.

  • SCAN/LOOK is the baseline algorithm

    Serve all requests in one direction before reversing, like a disk head. It's fair (no starvation) and predictable. Multi-elevator systems build on this with scoring-based dispatch.

  • Direction-aware scoring is key to good dispatch

    An elevator already moving toward a request in the same direction is far better than a closer elevator moving away. Score must account for current direction, remaining stops, and reversal cost.

  • Two queues per elevator (current direction + opposite)

    Requests in the current travel direction are served immediately. Opposite-direction requests queue for after reversal. This prevents unnecessary direction changes mid-trip.

  • Peak traffic needs special strategies

    Morning rush (everyone from lobby) benefits from zoning (assign elevators to floor ranges) and destination dispatch (group passengers by floor). Standard algorithms perform poorly under asymmetric load.

  • Capacity awareness prevents wasted trips

    Don't dispatch passengers to a nearly-full elevator. Use weight sensors or passenger count estimates to skip full cars during scoring. An overloaded elevator helps nobody.

© 2026 ByteLearn.dev. Free courses for developers. · Privacy