Design an Elevator System (LLD)
Model cars, floors and requests as a state machine, then pick a dispatch strategy that keeps waits short and never starves a floor.
- 2 sweepslongest wait bound under LOOK
- ~148 sround-trip time in the 20-floor example
- 72 s vs 52 stime stopping versus travelling per trip
Show what to say
“I'd split the problem in two: dispatch decides which car answers a hall call, and scheduling decides the order a car serves its stops. Each car is a state machine running LOOK over up-stops and down-stops, so no floor waits more than two sweeps. One controller owns every hall call and gives it to exactly one car through a pluggable strategy.”
Trade-offs
- FCFS vs nearest car: FCFS is trivial and fair by arrival but zig-zags; direction-aware nearest car gives short waits but commits at press time.
- Nearest car vs collective: collective keeps no assignment state and suits small groups, but cars bunch and calls may wait.
- One controller vs none: no double service of a call, but a single point of failure, so cars fall back to local mode.
Bring it up before they ask
When a car fails or goes out of service, its hall calls move to other cars keeping their queue place, and riders exit at the next floor.
Each car is a state machine that serves stops in LOOK order, so no floor waits more than two sweeps. One controller gives each hall call to exactly one car through a swappable dispatch strategy.
Functional
- N floors, M cars; ▲/▼ buttons on each floor, a panel in each car
- One car answers each hall call, stopping in the requested direction
- Doors open at stops; a sensor reopens them
- Cars show floor and direction; hall lamps stay lit until answered
- A full car skips hall calls; an overloaded car will not leave
- Out-of-service and fire-service modes
Non-functional
- Short average wait, bounded longest wait
- Each hall call goes to exactly one car, even under simultaneous presses
- Doors open only when stopped at a floor; never move with doors open
- Deterministic tests: time is an injected tick
- New dispatch strategies plug in without touching the car
Out of scope
- Motor drives and acceleration profiles
- Destination dispatch kiosks (a follow-up)
- Many buildings (an HLD follow-up)
A low-level design (LLD) question with two problems inside. Dispatch: which car answers a call. Scheduling: in what order a car serves its stops.
New to this? Read Swap behaviour and State machines and observers first.
Clarify, interface and state
Ask first:
Read more · 117 words
- How many floors and cars?
- An office with a morning rush, a hotel, a hospital?
- ▲/▼ buttons, or a keypad in the lobby?
- What happens in a car failure or fire alarm?
- Shortest average wait, or shortest longest wait?
The interface
| Call | Returns | Notes |
|---|---|---|
hallCall(floor, dir) | Car id, or null if queued | Idempotent: a lit button stays one call |
carCall(carId, floor) | Adds a stop to that car | |
tick() | Advances every car one step; tests drive it | |
takeOutOfService(carId) | Its hall calls move to other cars | |
returnToService(carId) | Back in the dispatch pool | |
events assigned, queued, cleared, arrived | Lamps, displays and logs observe these |
Each car exposes addStop(floor, dir?), step() and openDoors(), which throws while moving.
Build the design in stages
Step through: building, then requests, then dispatch as a strategy, then the displays. Green marks what the latest stage added.
Read more · 110 words
Do the sum on the building before adding a layer:
| Building | What it needs |
|---|---|
| 1 car, 5 floors | A queue in press order |
| 1 car, 20 floors | LOOK, or it zig-zags |
| 4 cars, 20 floors | Dispatch, one owner per call |
Monday 9 am, 120 people press ▲ in the lobby within five minutes. Two cars with no single owner both answer 5▲, and one rides empty.
What it remembers. All of it fits in the controller's memory. This is one instant.
- A hall call is in exactly one place:
assigned(to one car) orwaiting. - Up calls go to
upStops, down calls todownStops. - After a restart, panels re-send lit calls.
ElevatorSystem owns the floors, the cars and every hall call. A DispatchStrategy (NearestCar or Collective) picks which car answers; each ElevatorCar owns its door and panel and orders its own stops with LOOK. Displays observe the system's events.
- ◆ composition (owns)
- ◇ aggregation
- △ inheritance / implements (dashed)
- → association · ⇢ dependency
// the controller's state at one instant
const state = {
cars: [
{ id: 'A', floor: 3, dir: 'up', state: 'MOVING_UP', upStops: new Set([5, 7]), downStops: new Set([2]) },
{ id: 'B', floor: 0, dir: null, state: 'IDLE', upStops: new Set(), downStops: new Set() },
],
assigned: new Map([['5▲', { call: { floor: 5, dir: 'up' }, carId: 'A' }]]), // owned exactly when it is here
waiting: [{ floor: 9, dir: 'down', key: '9▼' }], // no car can take it yet, oldest first
};Watch it run
Three cars, eight floors. Press ▲ or ▼ to add a rider.
Read more · 85 words
Switch Strategy to replay the same riders, and take car B out of service mid-trip. After five simulated minutes:
- Random traffic: first-come-first-served (FCFS) waits nearly twice as long (about 8 s against 4–5 s).
- Morning rush: FCFS rides run about 30% longer (14 s against 11 s).
- Nearest car and Collective are close.
Hall call to doors open, step by step. Play it, then try all cars busy (the call waits, retried each tick) and car B fails (the call gets a new owner).
- Client
- Service
- Worker
Text description
- Hall panel 5 → ElevatorSystem: hallCall(5, up)
- ElevatorSystem: key 5▲ is new
- ElevatorSystem → Strategy: pick(5▲, cars in service)
- Strategy replies to ElevatorSystem: car B (cost 3)
- ElevatorSystem → Car B: addStop(5, up)
- ElevatorSystem replies to Hall panel 5: lamp 5▲ on
- Car B sends (async) to ElevatorSystem: arrived(5, up)
- ElevatorSystem sends (async) to Hall panel 5: cleared 5▲: lamp off
- Car B panel → Car B: carCall(12)
Step-by-step answer
Order: clarify, split dispatch from scheduling, draw one car's state machine, pick and defend strategies (LOOK per car, nearest car across cars), then walk a scenario and break it (double press, full car, failed car).
Read more · 330 words
Use cases and nouns
Call, ride, doors, display, failures. Nouns:
ElevatorSystem,ElevatorCar,HallCall,CarCall,DispatchStrategy.One car is a state machine
From Event To IDLE a stop on another floor MOVING_UP or MOVING_DOWN IDLE a call at this floor DOORS_OPEN MOVING_UP / MOVING_DOWN reached a floor it must stop at IDLE, then DOORS_OPEN DOORS_OPEN timer done, sensor clear IDLE any fault or maintenance key OUT_OF_SERVICE Safety lives in the car:
openDoors()throws while moving, andstep()never moves with doors open.Two sets of stops per car
LOOK asks: is any stop ahead, and is this floor a stop in my direction?
LOOK, not FCFS or SCAN
- FCFS: press order, zig-zagging.
- Nearest stop first: can starve a far floor.
- LOOK: sweeps up, then down, and turns at the last request.
A car never turns while a stop lies ahead, so every floor is served within two sweeps.
Dispatch: which car answers
Nearest car picks the lowest cost, following each car's sweep, plus a penalty per stop on the way.
// car at 2 sweeping up to 5; call 4▼: up to 5, back to 4 = 4 floors, plus 2 for the stop at 5 cost = routeDistance(car, call.floor, call.dir) + stopCost * stopsBefore;Collective assigns nothing up front. A call waits for a car passing its way. Simpler, but cars bunch.
One owner per call
Presses are messages to one controller loop. The first 5▲ is assigned. The second finds it in
assignedand does nothing. No locks.Doors, sensor and load
A blocked sensor reopens the doors. Over ~80% load the car skips hall calls.
Failures and modes
A failed car hands its calls to others. Fire service only changes which cars dispatch can use.
Displays and lamps
Displays and lamps listen for
arrivedandcleared(an observer).Tests
Inject the tick. Test LOOK order and the double press. Check after every tick: no open doors in motion, one owner per call.
Implementation
The same design in JavaScript, Java or Python. Java and Python add real locks.
Read more · 64 words
#plan()is all of LOOK: stop here if it is a stop in the sweep direction, go on while anything lies ahead, else turn.routeDistance: a car at 2 sweeping up to 5 is 2 floors from 4▲, but 6 from 4▼.
Code it yourself. Try the Elevator Scheduler exercise: one car, a tick at a time, with LOOK and first-come-first-served as swappable policies.
export const Dir = Object.freeze({ UP: 'up', DOWN: 'down' });
export const CarState = Object.freeze({
IDLE: 'IDLE', // stopped at a floor, doors closed
MOVING_UP: 'MOVING_UP',
MOVING_DOWN: 'MOVING_DOWN',
DOORS_OPEN: 'DOORS_OPEN', // only ever entered while stopped at a floor
OUT_OF_SERVICE: 'OUT_OF_SERVICE',
});
// A hall call is a floor plus the arrow someone pressed; a car call is a floor pressed inside one car.
export const hallCall = (floor, dir) => ({ kind: 'hall', floor, dir, key: `${floor}${dir === Dir.UP ? '▲' : '▼'}` });
export const carCall = (carId, floor) => ({ kind: 'car', carId, floor });
export const sign = dir => (dir === Dir.UP ? 1 : -1);
export const opposite = dir => (dir === Dir.UP ? Dir.DOWN : Dir.UP);Capacity estimate
A follow-up: how many cars does a building need?
Read more · 175 words
Size for the up-peak, the morning rush when every trip starts in the lobby. One car's round-trip time (RTT) sets everything.
| Quantity | Estimate |
|---|---|
| Building | 20 floors, 3.5 m each |
| People | 50 per floor = 1,000; 12% in the busiest 5 minutes = 120 |
| Load per trip | 13-person car, 80% full: P = 10 |
| Travel | 2.5 m/s: 1.4 s per floor |
| Stops per trip | S ≈ 8 (10 riders, some share a floor) |
| Time per stop | ~8 s (doors and braking) |
| Transfer | 1.2 s per passenger |
RTT = travel 52 + stops 72 + transfers 24 ≈ 148 s.
- One car: 300 × 10 ÷ 148 ≈ 20 people per 5 minutes.
- Four cars: about 81, one leaving the lobby every ~37 s (offices aim for under 30 s).
- Six cars: about 121, every ~25 s.
The surprise: stopping (72 s) costs more than travelling (52 s). Destination dispatch halves the stops to 4, so four cars then move about 103. The controller is tiny (0.4 presses a second), so correctness is the hard part. The timeline plays one trip.
An idle car at G answers 5▲ and carries two people to 12, using the capacity numbers: 1.4 s per floor, 3 s lost braking and accelerating per leg, doors 2 s to open and 3 s to close, 1.2 s per person. Of 37.6 s, only 16.8 s is full-speed travel; the rest is spent stopping.
Text description
- Motion: G → 5 (0–10 s), 5 → 12 (17.4–30.2 s)
- Doors: open (10–12 s), close (14.4–17.4 s), open (30.2–32.2 s), close (34.6–37.6 s)
- Passengers: 2 board (12–14.4 s), 2 leave (32.2–34.6 s)
Failure modes and trade-offs
Read more · 173 words
| What breaks | Effect | Mitigation |
|---|---|---|
| Doors blocked | Car stuck | Sensor reopens; a slow close after ~20 s, then a fault |
| A car fails | Its calls wait forever | Calls go to other cars; riders exit at the next floor |
| Power cut | Car stops between floors | Brakes hold; battery drives it to the nearest floor |
| Sensor fault | Car loses its position | Stop at the next floor, go out of service |
| Overload | Unsafe to leave | No departure over capacity |
| Controller crash | Assignments lost | Panels re-send; cars serve calls alone |
| Fire alarm | Lifts are dangerous | Recall to the designated floor, doors open |
Trade-offs
| Choice | Pros | Cons |
|---|---|---|
| FCFS | Trivial and fair | Zig-zags; long waits |
| Nearest car | Short waits | Commits at press time |
| Collective | No state to keep | Cars bunch |
| Destination dispatch | Fewer stops | Needs keypads |
| One controller | No double service | Single point of failure |
Follow-up questions
How would destination dispatch change the design?
Riders key in their floor, so HallCall becomes a DestinationRequest (from, to) behind the same strategy. Grouping by floor cuts stops.
How would you add VIP and fire-service modes?
Modes are system state. A VIP key pulls one car out of dispatch. Fire recall parks every car with doors open. LOOK is untouched.
How would you scale this to many buildings?
Keep dispatch local, since safety can't depend on a network. Stream events to a central service, split by building, for dashboards.
How would you save energy or prepare for the morning rush?
Park idle cars by policy (the lobby before 9 am). Add an energy cost so a moving car beats waking a parked one.
How do you test it?
Inject the tick and check the rules over seeded random runs. Replaying one seed compares strategies fairly.
Check yourself
Question 1 of 3Score 2 out of 3 or better to mark this page complete.