Elevators move roughly one billion people every day, and almost nobody thinks about how. That invisibility is the point — and it is exactly what great software should aspire to.

Beneath every quiet, unremarkable lift ride is a scheduling engine solving a real-time optimisation problem: multiple competing requests, limited resources, physical constraints, and zero tolerance for starvation. Sound familiar? It should. The same class of problem shows up in message queues, database connection pools, API rate limiters, and multi-tenant SaaS schedulers.

The Classic Algorithms

FCFS — First Come, First Served

The naive approach. Requests are served in the order they arrive. Simple to implement, catastrophic in practice. A single request to the top floor while everyone else needs floors 2–5 penalises every subsequent rider. In software terms, this is a synchronous task queue with no prioritisation — fine for toy projects, painful at scale.

SCAN (The Elevator Algorithm)

The disk scheduling world named this one directly after elevators. The car travels in one direction, serving all requests along the way, then reverses. It eliminates the worst-case latency of FCFS and distributes wait times more fairly. Most real elevator controllers use a variant of this.

In software, SCAN maps neatly onto batched processing with directional sweeps — think of how LSM-tree databases (used by RocksDB, Cassandra, and LevelDB) write data sequentially in one direction before compacting. Random writes become sequential I/O. Throughput climbs. The physics analogy holds.

LOOK and C-LOOK

Refinements of SCAN. Instead of travelling to the physical end of the shaft before reversing, the car only goes as far as the last active request. C-LOOK (Circular LOOK) jumps back to the lowest pending floor after reaching the highest, without serving requests on the return trip.

This is the principle behind circular work-stealing queues in thread pools — workers process tasks in sweeps, and idle workers steal from the front of a neighbour's queue rather than waiting. Go's goroutine scheduler and Java's ForkJoinPool both borrow from this logic.

The Harder Problem: Multiple Cars

A single elevator is a constrained optimisation. A bank of elevators is a distributed systems problem.

Modern buildings deploy group control systems that coordinate several cars simultaneously. The goals are familiar to any backend engineer:

  • Minimise average wait time across all users (p50 latency)
  • Bound worst-case wait time (p99 latency, no starvation)
  • Balance load across available resources
  • Adapt to traffic patterns — morning rush upward, evening rush downward, midday random

This is precisely what a load balancer does, except the "servers" have physical momentum, cannot teleport, and share a single vertical axis.

State-of-the-art systems use predictive models trained on historical traffic data. A controller that knows occupancy peaks at 9:00 AM can pre-position cars at lobby level before the rush. In SaaS terms, this is predictive autoscaling — spinning up compute capacity ahead of forecasted demand rather than reacting to it after latency has already spiked.

What Software Teams Should Take From This

# A simplified LOOK scheduler for a task queue
import collections

def look_scheduler(requests: list[int], current: int, direction: int) -> list[int]:
    above = sorted(r for r in requests if r >= current)
    below = sorted((r for r in requests if r < current), reverse=True)
    if direction == 1:   # moving up
        return above + below
    else:                # moving down
        return below + above

A few concrete takeaways for engineering teams:

  • Model resource limits explicitly. Elevators cannot ignore the shaft. Software schedulers that ignore CPU, memory, or I/O ceilings create the same kind of cascading delays.
  • Direction matters. Batching work by type or destination — rather than serving every request in pure arrival order — reduces context-switching overhead and improves cache locality.
  • Starvation is a product bug, not just an engineering bug. An elevator that never reaches floor 14 is not a technical curiosity; it is a broken product. The same holds for API request queues that deprioritise free-tier users to the point of uselessness.
  • Observability is the floor indicator panel. You cannot improve what you cannot see. Real-time visibility into queue depth, wait times, and resource utilisation is the equivalent of the display above every elevator door.

The Reliability Dimension

Elevators are also a case study in fault tolerance. Modern systems run redundant braking mechanisms, dual power feeds, and self-diagnostic routines. They degrade gracefully — a car taken offline routes its pending floor calls to neighbours.

This is circuit-breaker and fallback design made physical. When building microservices, the question is the same: if this service becomes unavailable, what happens to in-flight requests? Are they rerouted, queued, or dropped? Elevators solved this with hardware constraints forcing careful answers. Software teams often have the luxury of ignoring the question until production punishes them for it.

Why This Matters for Your Project

Whether you are building a multi-tenant SaaS platform, a mobile app with background sync, or an ML inference pipeline, the scheduling and resource-allocation problems your system faces are older than computing itself. The engineers who designed elevator group controllers in the 1970s and 1980s worked through the same trade-offs — fairness versus throughput, simplicity versus optimality, reactive versus predictive. Revisiting their solutions is not nostalgia; it is applied systems thinking. The next time you reach for a priority queue or design a job scheduler, you are, in a very real sense, writing elevator code.

Source: Hacker News — https://john.fun/elevators