# Low-Level Design: Elevator System (OOP Interview)

Source: https://www.techinterview.org/post/3233463919/lld-elevator-system/
Updated: 2026-07-05 · techinterview.org

## Low-Level Design: Elevator System

The elevator system is a classic OOP interview problem testing state machines, dispatching algorithms, and clean class design. The interviewer wants to see: how you model the core entities, how requests are dispatched across multiple elevators, and how the elevator decides which floors to service in what order.

## Core Classes

### Direction and State Enums


```
from enum import Enum

class Direction(Enum):
    UP = "UP"
    DOWN = "DOWN"
    IDLE = "IDLE"

class ElevatorState(Enum):
    MOVING = "MOVING"
    STOPPED = "STOPPED"
    MAINTENANCE = "MAINTENANCE"
```


### Request


```
from dataclasses import dataclass

@dataclass
class ExternalRequest:
    """Button pressed in the hallway."""
    floor: int
    direction: Direction

@dataclass
class InternalRequest:
    """Button pressed inside the elevator."""
    floor: int
```


### Elevator


```
class Elevator:
    def __init__(self, elevator_id: int, total_floors: int):
        self.elevator_id = elevator_id
        self.current_floor = 1
        self.direction = Direction.IDLE
        self.state = ElevatorState.STOPPED
        self.destinations = set()   # floors to stop at

    def add_destination(self, floor: int) -> None:
        self.destinations.add(floor)

    def move(self) -> None:
        """Advance one floor in current direction (SCAN algorithm)."""
        if not self.destinations:
            self.direction = Direction.IDLE
            self.state = ElevatorState.STOPPED
            return

        if self.direction == Direction.UP or self.direction == Direction.IDLE:
            above = [f for f in self.destinations if f > self.current_floor]
            if above:
                self.direction = Direction.UP
                self.state = ElevatorState.MOVING
                self.current_floor += 1
            else:
                self.direction = Direction.DOWN
                self.move()  # re-evaluate downward
        else:
            below = [f for f in self.destinations if f < self.current_floor]
            if below:
                self.direction = Direction.DOWN
                self.state = ElevatorState.MOVING
                self.current_floor -= 1
            else:
                self.direction = Direction.UP
                self.move()  # re-evaluate upward

    def step(self) -> list[int]:
        """
        Process one time step. Move toward next destination,
        stop if current floor is a destination.
        Returns list of floors where doors opened this step.
        """
        opened = []
        if self.current_floor in self.destinations:
            self.destinations.remove(self.current_floor)
            self.state = ElevatorState.STOPPED
            opened.append(self.current_floor)

        if self.destinations:
            self.move()

        return opened

    def cost_to_serve(self, request_floor: int, direction: Direction) -> int:
        """
        Estimate steps needed to serve this request.
        Used by dispatcher for assignment scoring.
        """
        distance = abs(self.current_floor - request_floor)
        # Penalize if elevator is going in opposite direction
        if self.direction != Direction.IDLE and self.direction != direction:
            return distance + 20  # detour penalty
        return distance
```


### ElevatorSystem (Dispatcher)


```
class ElevatorSystem:
    def __init__(self, num_elevators: int, num_floors: int):
        self.elevators = [
            Elevator(i, num_floors) for i in range(num_elevators)
        ]
        self.num_floors = num_floors

    def request_elevator(self, floor: int, direction: Direction) -> None:
        """External request: person on a floor presses UP or DOWN."""
        best = min(
            self.elevators,
            key=lambda e: e.cost_to_serve(floor, direction)
        )
        best.add_destination(floor)
        print(f"Elevator {best.elevator_id} assigned to floor {floor} ({direction.value})")

    def select_floor(self, elevator_id: int, floor: int) -> None:
        """Internal request: person inside elevator presses a floor button."""
        if not (1 <= floor <= self.num_floors):
            raise ValueError(f"Invalid floor: {floor}")
        for elevator in self.elevators:
            if elevator.elevator_id == elevator_id:
                elevator.add_destination(floor)
                return

    def step(self) -> None:
        """Advance simulation by one time step."""
        for elevator in self.elevators:
            opened = elevator.step()
            for floor in opened:
                print(f"Elevator {elevator.elevator_id} doors open at floor {floor}")

    def status(self) -> list[dict]:
        return [
            {
                'id': e.elevator_id,
                'floor': e.current_floor,
                'direction': e.direction.value,
                'state': e.state.value,
                'destinations': sorted(e.destinations),
            }
            for e in self.elevators
        ]
```


## Dispatching Algorithm: SCAN (Elevator Algorithm)

The SCAN algorithm is how real elevators work: the elevator travels in one direction, servicing all requests along the way, then reverses at the extreme. This minimizes average wait time and prevents starvation (unlike FCFS).

  - **FCFS (First Come First Served):** Simple but causes backtracking — elevator might go from floor 1 to 10, then back to 3, then to 8.

  - **SCAN:** Go in one direction until no more requests in that direction, then reverse. Serves requests in order of floor number within each direction.

  - **LOOK:** Like SCAN but reverses at the last requested floor (not at the physical extreme). More efficient than SCAN.

## Multi-Elevator Dispatching

The `cost_to_serve()` method is the key for assigning requests. In a real system, the score combines:

  - **Distance** to the requested floor

  - **Direction alignment:** An elevator already going up toward floor 7 is a better choice for a floor-7-up request than an elevator going down

  - **Load:** An elevator with 0 destinations is preferred over one with 10 pending stops

A simple heuristic: cost = |current_floor - request_floor| + (20 if direction_mismatch else 0) + len(destinations) * 2. Assign the request to the elevator with the minimum cost.

## Usage Example


```
system = ElevatorSystem(num_elevators=3, num_floors=20)

# External requests (hallway buttons)
system.request_elevator(floor=5, direction=Direction.UP)
system.request_elevator(floor=12, direction=Direction.DOWN)

# Internal request (inside elevator 0)
system.select_floor(elevator_id=0, floor=15)

# Simulate
for _ in range(20):
    system.step()

print(system.status())
```


## Interview Follow-ups

  - **Emergency mode:** Add a priority level to requests. Fire alarm requests override all others and all elevators go to floor 1.

  - **Maintenance mode:** Set `ElevatorState.MAINTENANCE` to exclude an elevator from dispatching without removing it from the system.

  - **Weight sensor:** Elevator tracks current load; if at capacity (e.g., 80% full), skip external requests but still serve internal requests (passengers inside).

  - **Why SCAN over FCFS?** SCAN reduces average wait time and prevents starvation. FCFS can cause an elevator to thrash between floors 1 and 20 on every request.

🏢 Asked at: [Atlassian Interview Guide](https://techinterview.org/atlassian-interview-guide/)

🏢 Asked at: [Shopify Interview Guide](https://techinterview.org/shopify-interview-guide/)

🏢 Asked at: [Apple Interview Guide 2026: iOS Systems, Hardware-Software Integration, and iCloud Architecture](https://techinterview.org/apple-interview-guide/)

🏢 Asked at: [Uber Interview Guide 2026: Dispatch Systems, Geospatial Algorithms, and Marketplace Engineering](https://techinterview.org/uber-interview-guide/)

🏢 Asked at: [Airbnb Interview Guide 2026: Search Systems, Trust and Safety, and Full-Stack Engineering](https://techinterview.org/airbnb-interview-guide/)

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

See also: [Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence](https://www.techinterview.org/post/3233460274/netflix-interview-guide-2026-streaming-architecture-recommendation-systems-and-engineering-excellence/)

See also: [Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering](https://www.techinterview.org/post/3233460275/scale-ai-interview-guide-2026-data-infrastructure-rlhf-pipelines-and-ml-engineering/)
