AlgoInsight

System Design

Design Uber

Real-time matching between millions of riders and drivers, locations updating every few seconds, all in a 45-minute interview. It's one of the hardest 'Design X' questions - and the one where I've seen the most candidates freeze. Here's the structured way through it.

By Admin2026-06-1816 min readubersystem-designcase-studyinterviewgeospatialreal-time

"Design Uber" - or Lyft, or any ride-sharing service - is one of the questions I respect most as an interviewer, because it's genuinely hard and it has nowhere to hide. There's a real-time matching problem, a geospatial problem, a high-volume data-ingestion problem, and a stateful workflow problem, all in one. I've watched strong engineers freeze on it, not because they lacked the knowledge, but because they had no structure for breaking it down. The candidates who do well aren't the ones who know more - they're the ones who decompose a sprawling problem into tractable pieces.

Let me walk through how I'd want to see this approached, and where, as a Bar Raiser, I'm actually awarding and deducting points.

Scope it down first

Ride-sharing has a hundred features. In 45 minutes you design the core. So the first move is narrowing - out loud - to the functional essentials:

Set aside (mention, then defer) payments, ratings, pricing tiers, and the rest, unless the interviewer steers you there. Then the non-functional requirements that shape everything:

Interview: The first thing I evaluate is whether you can tame the scope. A candidate who tries to design payments, surge pricing, ratings, and matching all at once produces a shallow mess and runs out of time. A candidate who says "let me focus on the core - requesting, matching, and real-time tracking - and set pricing aside unless you want it" shows the judgment that real design requires. Narrowing isn't dodging; it's prioritization, and I reward it. The best candidates state explicitly which features they're deferring and why, so I know it's a deliberate choice rather than an oversight.

The defining challenge: finding nearby drivers

Here's what makes Uber Uber as a design problem, and the part I'm most interested in: how do you efficiently find available drivers near a rider?

The naive approach - check the distance from the rider to every driver - is hopeless at scale. With millions of drivers, scanning all of them for every ride request would be impossibly slow. This is a geospatial proximity problem, and recognizing it as such is the first real signal.

The solution is spatial indexing - most commonly geohashing (or a related structure like a quadtree). Geohashing encodes each location into a short string where nearby locations share a prefix, which turns "find drivers near this point" into a fast prefix lookup instead of a scan of every driver. The system maintains drivers' current locations in a geospatial index, and when a rider requests, it queries the index for drivers in the rider's cell (and neighboring cells, to handle the boundary case) - instantly narrowing millions of drivers to the handful nearby. (See the dedicated guide on geohashing for exactly how this works, including the neighbor-cell detail.)

graph TD
    R[Rider requests ride] --> M[Matching Service]
    M --> GEO[Geospatial Index<br/>drivers by location]
    GEO --> NEAR[Nearby available drivers]
    NEAR --> M
    M --> PICK[Select best driver, dispatch]
    D[Drivers stream location<br/>every few seconds] --> LOC[Location Service]
    LOC --> GEO
    PICK --> RT[Real-time updates<br/>rider ↔ driver]
This diagram shows Uber's core flow. Drivers continuously stream their location every few seconds to a Location Service, which updates a geospatial index of drivers by location. When a rider requests a ride, the Matching Service queries that geospatial index for nearby available drivers, gets the handful that are close, selects the best one, and dispatches. Real-time updates then flow between rider and driver throughout the trip.

Interview: When a candidate hits the "find nearby drivers" step and proposes scanning every driver, that's a red flag - it signals they're not thinking about scale. When they say "I'd use a geospatial index like geohashing so finding nearby drivers is a prefix lookup, not a full scan," that's a strong hire signal. And if they mention checking neighboring cells to avoid missing drivers right across a boundary, that's the kind of detail that tells me they truly understand the technique rather than name-dropping it.

The high-volume problem: ingesting locations

There's a second hard problem hiding here that candidates often miss: every driver sends their location every few seconds. With millions of active drivers, that's a torrent of location updates flowing in constantly - an enormous write volume, far higher than the ride-request volume.

This needs a dedicated Location Service built to ingest a high-throughput stream of updates and keep the geospatial index current. The write load here dwarfs the read load of matching, and a candidate who recognizes that the location-ingestion firehose is a distinct, demanding subproblem - separate from matching - is thinking at the right level. This is also where streaming and high-write-throughput data infrastructure comes in: the location updates are a continuous stream that must be processed in real time to keep "where is every driver right now" accurate.

Matching and the trip lifecycle

Once we have the nearby drivers, the Matching Service selects one - typically the closest, or the one with the shortest estimated arrival, possibly factoring in driver acceptance. It dispatches the request to that driver, handles acceptance or decline (try the next driver if declined), and once matched, hands off to managing the trip.

The Trip Service owns the trip's lifecycle as a stateful workflow: requested → matched → driver en route → in progress → completed. Each transition updates the trip's state, and both rider and driver need the current state reflected in their apps in real time. This is genuinely stateful - unlike a stateless web request, a trip is a long-lived object moving through defined states - and modeling it cleanly (often as a state machine) is part of a strong answer.

Real-time updates: keeping everyone in sync

Throughout the trip, the rider watches the driver approach on the map, and during the ride both see the live location. This is real-time, two-way communication, which points to WebSockets (or a similar persistent connection) between the apps and the servers - the driver's location streams to the rider live, and trip state changes push to both instantly. (See the guides on WebSockets and real-time communication for the mechanics; the key point in the design is recognizing that polling won't deliver the live map experience, and a persistent push channel will.)

Deep dive: when matching gets messy

The happy path - find nearby drivers, pick one, done - is the easy version. A strong candidate anticipates where matching gets messy, because real ride-matching is full of edge cases:

Naming even a couple of these unprompted tells me a candidate has thought past the demo into the messy reality, which is exactly where senior-level design lives.

Deep dive: the geo-sharding challenge

Here's a partitioning question I like to push senior candidates on: how do you shard the location and matching data across machines at massive scale? You can't keep every driver's location on one machine. The natural answer is to partition geographically - by region, or by geohash prefix - so a given area's drivers and the requests in that area are handled by the same partition, keeping "find drivers near this rider" a local operation within one partition rather than a query spanning the world.

But geographic sharding has a classic complication: load is uneven. A dense city center generates far more rides and location updates than a rural area, so some geographic shards run red-hot while others sit nearly idle. A naive equal-area partition creates hot spots. Handling this - partitioning by load rather than equal area, or splitting hot regions into finer partitions - is an advanced point, and a candidate who raises the uneven-load problem with geographic sharding is operating at a genuinely senior level. It's the kind of detail that, as a Bar Raiser, moves my assessment up a notch.

Deep dive: estimating the scale

A quick estimate grounds the design and shows you size before you build. Say there are a few million active drivers at peak, each sending a location update every few seconds - that's on the order of a million location updates per second flowing in continuously. Ride requests are far less frequent than location updates (people request rides occasionally; drivers stream location constantly), which confirms that the location-ingestion path, not the matching path, carries the heaviest write load. These numbers justify the dedicated high-throughput Location Service and the geospatial index optimized for constant updates. The asymmetry is the key insight: the firehose is locations, not ride requests, and the architecture should reflect that.

Deep dive: pricing and surge (if asked)

If the interviewer steers toward pricing, surge is the interesting part. Surge pricing raises prices when demand in an area exceeds available supply, to balance the two. Designing it means continuously computing, per geographic area, the ratio of ride requests to available drivers, and applying a price multiplier when demand outstrips supply. Notice this leans on the same real-time, geographic data the rest of the system already maintains - it's a real-time aggregation over the location and request streams, computing supply-versus-demand per area. A candidate who connects surge back to the streaming, geospatial infrastructure already in the design - rather than treating it as a bolt-on - shows they see the system as a coherent whole.

What I'd push a senior candidate on

For a senior or staff-level candidate, after the core design, I probe the harder realities. How do you keep the geospatial index consistent when locations update a million times a second - is slight staleness acceptable (yes, a driver's location being a second or two old is fine)? How do you handle a region's servers failing without losing in-progress trips (trip state must be durable and recoverable)? How do you roll out to a new city (the geographic partitioning makes this natural - a city is a partition)? These questions don't have single right answers; what I'm assessing is whether the candidate can reason about the operational realities of a system at this scale - consistency trade-offs, failure recovery, gradual rollout - rather than just produce a clean architecture diagram. The diagram is table stakes at senior level; the judgment about running it in production is what earns the rating.

Putting the architecture together

Pulling the pieces into a coherent whole:

Subproblem The trap The strong answer
Find nearby drivers Scan every driver Geospatial index (geohashing)
Ingest locations Treat as low-volume Dedicated high-throughput Location Service
Match a driver Hand-wave it Closest/ETA-based dispatch with decline handling
Track the trip Stateless thinking Stateful lifecycle (state machine)
Live map Polling WebSockets / persistent push

Each piece maps to a hard problem, and naming them separately is exactly how you keep a sprawling question tractable.

Interview: The candidates who succeed on "Design Uber" do one thing consistently: they decompose. They name the subproblems - geospatial search, location ingestion, matching, trip state, real-time updates - and tackle each in turn, rather than trying to hold the whole thing in their head at once. As a Bar Raiser, that decomposition is the single behavior I'm most looking for, because it's exactly what designing a real system requires. The knowledge matters, but the structure is what separates the candidates who thrive from the ones who freeze.

The bottom line

Designing Uber means solving four hard problems at once: finding nearby drivers efficiently (a geospatial index like geohashing, not a scan), ingesting a high-volume firehose of location updates (a dedicated Location Service), matching and dispatching drivers (with all its messy edge cases - declines, no-availability, race conditions, cancellations), and managing a stateful trip lifecycle with real-time updates flowing to both sides (WebSockets). The reason it's a great interview question is that it has no single trick - it rewards the ability to break a large, real-time, geospatial problem into tractable pieces and address each with the right tool.

Decompose first, name each subproblem, reach for the right building block for each, and the question that makes people freeze becomes a structured, answerable design. The candidates who handle it well don't necessarily know more than those who freeze - they simply refuse to hold the whole sprawling problem in their head at once, and instead solve it one tractable piece at a time. We work through case studies like this end to end across System Design Fundamentals.


Go deeper

More articles