AlgoPlusAlgoPlus
Learn/System Design
Lesson

Design Uber (Ride-Sharing)

Match a rider to the nearest driver across millions of moving cars using a geospatial index, absorb a firehose of GPS updates, and dispatch.

11 min read Watch it move Build it

Uber answers one question fast, over and over: *which drivers are near this rider, right now?* The hard parts are that there are millions of cars, all moving, each streaming its location, and 'nearest' must be computed in milliseconds. The heart of the design is a geospatial index.

Why a distance scan doesn't work

The naive design queries all drivers, computes the distance to each, and picks the closest. That is O(number of drivers) per request — hopeless past one small city. We need to look at only the drivers physically nearby, which means indexing space itself.

Geospatial indexing — the family and Uber's choice

The general idea is to tile the map into cells and bucket each driver by cell, so 'nearest' becomes 'read my cell and its neighbours'. There are several schemes in this family: geohash (recursively halve lat/long into a string prefix), quadtree (recursively split a square into 4), and Google's S2 (project the sphere onto cube faces). Uber built and open-sourced its own: H3, which tiles the globe with hexagons.

Why hexagons
Uber chose hexagons because all six neighbours sit the same distance from the centre. A square grid has two neighbour distances (edge vs corner) and a triangle grid has three, which distorts 'nearby'. A nearest-driver query just reads the rider's hexagon plus its k-ring (the surrounding ring of cells). Note: this is H3, not S2/geohash/quadtree — S2 was Uber's *earlier* 2015 platform.

The location firehose

Every driver streams GPS continuously — Uber cited roughly every 4 seconds, targeting on the order of 1M writes/sec in 2015. Do the math: 1M active drivers pinging every 4s is ~250K updates/sec just to keep the index current, and that data is short-lived (last known position only). A normal disk-backed database cannot keep up; this wants an in-memory, sharded index.

  1. 1Driver updates enter through the edge API gateway — the single front door for the mobile apps.
  2. 2They feed a Geospatial Service, split across worker machines by Ringpop — Uber's library combining consistent hashing (spread cells across nodes) with SWIM gossip (nodes track who is alive and detect failures).
  3. 3Each update rewrites the driver's current H3 cell in the index.
  4. 4A ride request reads the rider's cell + k-ring to get candidate drivers.

Dispatch and pricing

Matching is DISCO (Dispatch Optimization). Rather than just grabbing the nearest idle car, it plans ahead — forward-dispatch — for example a car about to drop off a rider a block away may beat an idle car that is farther. Surge pricing is computed per hexagon, comparing supply against demand in that cell to set a local multiplier. Each concern is its own service so they scale independently.

What's public and what isn't
H3, Ringpop, and DISCO's forward-dispatch idea are documented. The exact production H3 resolution and the modern matching topology are not public — don't assert specific numbers Uber never published.
OperationTimeSpace
Naive nearest scan · distance to every car — too slowO(drivers)O(1)
Nearest via H3 · read a constant few cells≈ O(cell + k-ring)O(drivers)
Location update · rewrite one cell, in-memoryO(1)O(drivers)
Check yourself
Why does a hexagonal grid like H3 make 'nearest driver' cleaner than a square grid?