Sweaty Imposter

Design Uber / Ride Sharing

System DesignGeospatialReal TimeSenior3 min readUpdated

Millions of drivers report their location every few seconds, and a rider expects a match in under two seconds. The design centers on a geo index that takes millions of writes a second and still answers nearby queries fast.

1 · Requirements

The Prompt

Match riders to nearby drivers in <2 seconds, track real-time locations, and handle surge pricing.

Functional

  • Riders request a ride with a pickup and dropoff and get an ETA and fare estimate
  • Match the rider to a nearby available driver
  • Track the driver's live location during the trip
  • Apply surge pricing where demand outruns supply

Non-Functional

  • A match happens in under 2 seconds
  • Location writes keep up with millions of pings a second
  • A driver is never matched to two riders at once

2 · Estimates

Drill This Phase First

Show the Answer and Estimates

Answer

About 2.5M a second

10M divided by 4 seconds is 2.5M writes a second, which is why driver locations live in memory with hourly snapshots to disk.

Writes
10M active drivers pinging every 4 seconds: 2.5M location writes a second into the geo store
Matches
About 100,000 ride requests a minute: roughly 1,700 matches a second and 5,000 at peak
Geo Index
Precision 6 geohash cells of about 1.2 km: about 3M populated cells, an index that fits in 50 GB of Redis
Memory
10M drivers at about 200 bytes each: about 2 GB, kept in memory and snapshotted to disk hourly
Sockets
10M driver and 1M rider connections across about 20,000 gateway nodes
Latency
The 2 second match target keeps the matching service in the same availability zone as the geo index

3 · API

WS /driver/connect: driver pushes { lat, lng, ts } every 4s
POST /rides/request { rider_id, pickup, dropoff } → { ride_id, eta, fare_estimate }
POST /rides/:id/accept (driver) → { rider_contact, route }
GET /rides/:id → { status, driver_loc, eta }
POST /rides/:id/complete → { final_fare, receipt_url }

Data Model

driver_locations (Redis GEO / KV):
  driver_id (PK)  : uuid
  geohash         : string (precision 6)
  lat, lng        : float
  status          : enum(available|on_trip|offline)
  last_ping       : timestamp

rides (wide-column, partition = ride_id):
  ride_id (PK)    : uuid
  rider_id        : uuid
  driver_id       : uuid?
  pickup, dropoff : geo_point
  status          : enum(requested|matched|in_progress|done|canceled)
  created_at      : timestamp
  fare            : decimal

surge_zones (KV, partition = geohash5):
  geohash5 (PK)   : string
  multiplier      : float
  expires_at      : timestamp

4 · High-Level Design

Driver pings write straight into the in-memory geo index. A ride request asks the index for available drivers in the pickup cell and its neighbors, ranks them by ETA, and the match engine claims one atomically before telling both apps.

5 · Deep Dives

Geohash Cells and Neighbors

Bucket drivers by geohash at precision 6, about 1.2 km. A nearby search reads the pickup cell plus its eight neighbors, since a driver 50 meters away can sit across a cell edge. Dense cities can use a quadtree that splits busy cells further.

One Driver, One Rider

Two dispatchers can pick the same driver at the same moment. Claim the driver with an atomic compare-and-set on status, from available to on_trip, and move to the next candidate when the claim fails.

Surge Pricing

Count open requests and available drivers per geohash5 zone from the Kafka stream over a short window. Publish a multiplier per zone with an expiry, and show it to the rider before they confirm, so the quoted fare matches the charged fare.

6 · Wrap Up

Close with the risks you would take on next: a gateway region failing mid-trip, GPS noise on city streets, and sharding the geo index by city.

The Short Version

Geohash/quadtree for nearby-driver lookup; drivers push location over WebSockets; pub/sub broadcasts the match. A key-value cache maps driver_id to last_location.