MindForge
MindForge
DashboardHelp & Support
Dashboard
MindForge

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Design an Elevator System
Course contents

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Browsing without an account — progress is saved in this browser only.Sign in to save it

Design an Elevator System

Reading55 min

If the parking lot was about allocation, the elevator is about scheduling. The hard part isn't the classes — it's that correct behaviour is really an algorithm: which car answers which request, and in what order. Candidates who jump straight into writing classes, without first pinning down that algorithm, end up with a design that can't answer a simple question like "why did car 2 get sent there?"

Step 1 — Requirements and the two kinds of request

What the system needs to do:

  • A person standing on a floor presses up or down — this is called an external or hall request.
  • A person already inside a car presses a destination floor — this is an internal or car request.
  • Cars move between floors and open their doors at each stop.
  • A dispatcher decides which car answers each hall request.
  • There's an emergency stop, manual door controls, and a maintenance mode.

The one distinction that shapes everything else: an external request carries a floor and a desired direction, but no destination. An internal request carries a destination, but no direction of its own. If you model every request as "just a floor," you can't express something as basic as "this car is already heading up, so it shouldn't grab a down request from floor 8 just yet" — and that idea is really the entire job of elevator scheduling.

The clarifying questions:

Question Answer used here What it changes
How many cars? Several, sharing one bank A Dispatcher becomes its own object
Which scheduling rule? Send the nearest suitable car, then sweep inside each car Strategy — this is the extension point
Can a car skip floors? Yes — express cars, restricted floors Each car gets its own serviceable_floors
Is there a capacity limit? Yes, a weight or headcount limit A full car refuses new internal requests
Real hardware, or a simulation? Simulated with a step() tick Testable, with no real threads needed

Say the scope: "I'll model both kinds of requests, how each car moves and holds state, and a pluggable dispatcher, all driven by a discrete step() so the behaviour is fully testable. Door timing, the actual motor control, and physical safety interlocks are out of scope."

Remember: an external request carries a direction. An internal one carries a destination. Mixing them into one shape loses the exact information the dispatcher needs.

Sign in to try this knowledge check.

Step 2 — Entities and states

Concept Kind of thing Notes
Direction (UP / DOWN / IDLE) Enum The car's current direction of travel
DoorState (OPEN / CLOSED) Enum
ElevatorState (MOVING / STOPPED / MAINTENANCE) Enum Decides which operations are even legal — this is really a small State machine
Request (external) / Destination (internal) Value objects Never change once created
ElevatorCar Entity Owns its own stops, position, direction, and capacity
DispatchStrategy Interface Nearest car, least busy, zoned, energy-aware — these will vary
ElevatorSystem Service / facade Holds every car, routes hall requests, drives the tick forward
                    ┌────────────────────┐
                    │  ElevatorSystem    │
                    │ + request(floor,   │◆──1..*── ElevatorCar
                    │     direction)     │             - current_floor
                    │ + step()           │             - direction: Direction
                    └─────────┬──────────┘             - up_stops / down_stops
                              │ 1                      - state: ElevatorState
                       DispatchStrategy                + add_stop(floor)
                         △ (interface)                 + step()
                         │
            NearestCar / LeastBusy / Zoned

Why each car keeps two separate ordered sets of stops (up_stops, down_stops) instead of one plain queue: a real elevator doesn't serve requests in the order they arrived — it sweeps in one direction, then the other. Keeping "stops I'll hit going up" separate from "stops I'll hit coming down" is what makes that sweep trivial to write and even easier to explain out loud.

Remember: an elevator sweeps, it doesn't queue. Two direction-based sets, not one FIFO list.

Sign in to try this knowledge check.

Step 3 — The scheduling algorithm

This is the real heart of the problem, and there are three named answers worth having ready:

Algorithm The rule What it does
FCFS Serve requests in the order they arrived Simple, and genuinely terrible — the car bounces back and forth
SCAN (the "elevator algorithm") Keep going one direction, stopping at everything along the way, then reverse at the very last floor What real elevators actually do; wait times stay bounded
LOOK Like SCAN, but reverse at the last requested floor, not the physical top or bottom SCAN without the wasted travel — this is the practical choice

Inside one car: LOOK. Across several cars: a dispatch strategy that scores every car against a given hall call. Here's the scoring rule worth stating:

For each car:
  if car is IDLE                                  → cost = |car.floor - request.floor|
  if car moving TOWARD the request AND same direction
                                                  → cost = |car.floor - request.floor|
  otherwise (moving away, or the opposite direction)  → cost = |distance| + a large penalty
Pick the lowest cost; break ties by fewest pending stops.

That single rule captures exactly the behaviour people expect from a real elevator bank: a car already heading your way, passing your floor, picks you up along the way. A car heading the other direction doesn't turn around just for you.

Trade-offs worth mentioning out loud: optimising purely for average wait time can leave one floor at the far end of the building waiting forever, so real systems add a small penalty that grows with how long a request has waited, so it eventually wins regardless of distance. Zoning — assigning cars to specific floor ranges — cuts down travel in tall buildings, at the cost of flexibility whenever there's an unusual rush.

Remember: a car already moving toward a request in the same direction costs nothing extra to serve. That's the cheapest case, and it should almost always win.

Sign in to try this knowledge check.

Step 4 — The implementation

Python
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum


class Direction(Enum):
    UP = 1
    DOWN = -1
    IDLE = 0


class ElevatorState(Enum):
    STOPPED = "stopped"
    MOVING = "moving"
    MAINTENANCE = "maintenance"


@dataclass(frozen=True)
class HallRequest:                       # external: a floor plus the desired direction
    floor: int
    direction: Direction


class ElevatorCar:
    def __init__(self, car_id: int, floor: int = 0, capacity: int = 8):
        self.car_id, self.current_floor, self.capacity = car_id, floor, capacity
        self.direction = Direction.IDLE
        self.state = ElevatorState.STOPPED
        self.occupants = 0
        self.up_stops: set[int] = set()      # floors to serve while going up
        self.down_stops: set[int] = set()    # floors to serve while going down
        self.log: list[str] = []

    # ---- requests -------------------------------------------------------
    def add_stop(self, floor: int) -> None:
        if self.state is ElevatorState.MAINTENANCE:
            raise RuntimeError(f"car {self.car_id} is out of service")
        if floor == self.current_floor and self.direction is Direction.IDLE:
            return                                    # already here, doors will just open
        (self.up_stops if floor > self.current_floor else self.down_stops).add(floor)
        if self.direction is Direction.IDLE:
            self.direction = Direction.UP if floor > self.current_floor else Direction.DOWN
            self.state = ElevatorState.MOVING

    @property
    def pending(self) -> int:
        return len(self.up_stops) + len(self.down_stops)

    def is_full(self) -> bool:
        return self.occupants >= self.capacity

    # ---- movement (LOOK) ------------------------------------------------
    def _next_stop(self) -> int | None:
        if self.direction is Direction.UP:
            ahead = [f for f in self.up_stops if f > self.current_floor]
            if ahead:
                return min(ahead)
            return max(self.down_stops) if self.down_stops else None   # time to reverse
        if self.direction is Direction.DOWN:
            below = [f for f in self.down_stops if f < self.current_floor]
            if below:
                return max(below)
            return min(self.up_stops) if self.up_stops else None       # time to reverse
        return None

    def step(self) -> None:
        """Move one floor, or stop and open the doors if this floor is a stop."""
        if self.state is ElevatorState.MAINTENANCE:
            return
        target = self._next_stop()
        if target is None:
            self.direction, self.state = Direction.IDLE, ElevatorState.STOPPED
            return

        if target == self.current_floor:
            self.up_stops.discard(target)
            self.down_stops.discard(target)
            self.log.append(f"car{self.car_id}: doors open at {target}")
            if self.pending == 0:
                self.direction, self.state = Direction.IDLE, ElevatorState.STOPPED
            return

        step = 1 if target > self.current_floor else -1
        self.current_floor += step
        self.direction = Direction.UP if step == 1 else Direction.DOWN
        self.state = ElevatorState.MOVING
        if self.current_floor in self.up_stops or self.current_floor in self.down_stops:
            self.up_stops.discard(self.current_floor)
            self.down_stops.discard(self.current_floor)
            self.log.append(f"car{self.car_id}: doors open at {self.current_floor}")


class DispatchStrategy(ABC):
    @abstractmethod
    def choose(self, cars: list[ElevatorCar], req: HallRequest) -> ElevatorCar | None: ...


class NearestSuitableCar(DispatchStrategy):
    PENALTY = 1_000

    def _cost(self, car: ElevatorCar, req: HallRequest) -> int:
        distance = abs(car.current_floor - req.floor)
        if car.direction is Direction.IDLE:
            return distance
        moving_toward = (
            (car.direction is Direction.UP and req.floor >= car.current_floor) or
            (car.direction is Direction.DOWN and req.floor <= car.current_floor)
        )
        if moving_toward and car.direction is req.direction:
            return distance                       # free: served along the way
        return distance + self.PENALTY            # would need to turn around

    def choose(self, cars, req):
        eligible = [c for c in cars
                    if c.state is not ElevatorState.MAINTENANCE and not c.is_full()]
        if not eligible:
            return None
        return min(eligible, key=lambda c: (self._cost(c, req), c.pending, c.car_id))


class ElevatorSystem:
    def __init__(self, cars: list[ElevatorCar], strategy: DispatchStrategy):
        self.cars, self.strategy = cars, strategy

    def hall_request(self, floor: int, direction: Direction) -> ElevatorCar:
        car = self.strategy.choose(self.cars, HallRequest(floor, direction))
        if car is None:
            raise RuntimeError("no car available")
        car.add_stop(floor)
        return car

    def step(self, ticks: int = 1) -> None:
        for _ in range(ticks):
            for car in self.cars:
                car.step()


# --- an en-route car beats an idle one ---------------------------------------
a = ElevatorCar(1, floor=3); a.add_stop(10)          # car 1: at floor 3, heading UP to 10
b = ElevatorCar(2, floor=8)                          # car 2: idle at floor 8
system = ElevatorSystem([a, b], NearestSuitableCar())

chosen = system.hall_request(5, Direction.UP)
assert chosen is a                                    # picked up on the way, not the idle car

# --- the car sweeps: floor 5, then floor 10, never 10 then back --------------
system.step(12)
assert "car1: doors open at 5" in a.log
assert a.log.index("car1: doors open at 5") < a.log.index("car1: doors open at 10")

# --- a car in maintenance is never dispatched ---------------------------------
a.state = ElevatorState.MAINTENANCE
assert system.hall_request(2, Direction.DOWN) is b

print(a.log)
print("car2 sent to floor 2:", sorted(b.down_stops))
Sign in to try this knowledge check.

Steps 5 and 6 — Concurrency, edge cases, and extensions

The shared, changeable data: each car's set of stops, and the dispatcher's view of where every car currently is. Two people pressing buttons at the same instant can both try to change one car's stops at once.

Setting What protects it
One controller process A lock per car around add_stop/step — each car's stops are its own, so no single global lock is needed
Real hardware Each car acts as its own independent actor or thread, with a command queue — the dispatcher just posts messages, and the car fully owns its own state. This removes shared state entirely, rather than just protecting it
Dispatcher decisions Work from a consistent snapshot; a slightly stale position only costs a little efficiency, never correctness

The actor-with-a-queue answer is the stronger one here: get rid of the sharing, don't just guard it.

Edge cases worth raising yourself:

Case How it's handled
Car is full Skip it in dispatch (is_full), and it won't accept a new hall call either — the passenger presses the button again
Request for the floor the car is already on Just open the doors, no new stop needed
Every car is in maintenance Dispatch returns nothing; the system should show "no service" instead of silently doing nothing
Emergency stop A state transition that clears all pending stops and refuses new ones — a real state change, not a boolean flag
Power failure or reboot Cars return to the nearest floor and open their doors
A far floor keeps getting skipped Add a term to the cost function that grows with wait time, so waiting eventually beats distance
Two cars arrive for one call Cancel the duplicate stop the moment either car opens its doors for that hall request

Extensions, and how the design absorbs each one:

  • Express elevators, or restricted floors — a serviceable_floors set per car, checked inside choose. No new classes needed.
  • Zoning (low-rise and high-rise banks) — a ZonedDispatch strategy. One new class.
  • Peak-hour behaviour (morning rush: park idle cars near the lobby) — another strategy, plus a rule for where idle cars wait.
  • Energy-aware routing — a strategy that weights both travel distance and how often doors open and close.
  • Displays showing car position — Observers watching each car's floor change.

Every single one of those is a new strategy or a new observer — which is exactly the point to make when you wrap up: the scheduling rule was the thing most likely to change, so it went behind an interface from the start.

Remember: the best concurrency answer here removes the shared state entirely — give each car its own actor and a command queue, instead of guarding a lock.

Sign in to try this knowledge check.

Quick recap

  • Pin down the algorithm before you write a single class. The elevator is a scheduling problem, and a design that can't explain why a specific car got chosen has missed the actual question.
  • There are two kinds of request, not one. External requests carry a floor plus a direction; internal ones carry a destination — and that direction field is exactly what makes serving a request along the way possible.
  • LOOK inside one car, cost-based scoring across cars, and say the "already moving toward it in the same direction is free" rule out loud.
  • Two direction-based sets of stops make the sweeping behaviour trivial to write, and much easier to defend than a single plain queue.
  • Prefer actors with command queues over locks whenever every piece of state has a natural, single owner — it's the concurrency answer that removes the problem instead of just guarding against it.
PreviousDesign a Parking LotNextDesign a Movie Ticket Booking System

Course progress

0%
0 of 177 modules complete

On this page

  • Step 1 — Requirements and the two kinds of request
  • Step 2 — Entities and states
  • Step 3 — The scheduling algorithm
  • Step 4 — The implementation
  • Steps 5 and 6 — Concurrency, edge cases, and extensions
  • Quick recap