Elevator System at Scale
📋 Jump to TakeawaysElevator 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 requestsMOVING_UP: traveling upward, serving requests in that directionMOVING_DOWN: traveling downwardSTOPPED: 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: intStep 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_targetStop 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 9Two 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 downMonitoring 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.