Design Uber π
rider β driver matching Β· real-time location Β· hellointerview breakdown
1Requirements
Functional
- Pickup + dest β fare estimate
- Request ride from that estimate
- Match rider β nearby available driver
- Driver accepts/declines β pickup β dropoff
β ratings, scheduling, ride tiers
Non-functional
- Match < 1 min β or fail
- Strong consistency β 1 driver, 1 ride
- Peak: 100k reqs, same location
β GDPR, redundancy, CI/CD
2Entities & data model
Ride rideId Β· riderId Β· driverId? Β· fareId
source Β· destination Β· status
Fare id Β· userId Β· source Β· destination
price Β· eta
Driver id Β· name Β· vehicle
Rider id Β· β¦
Location driverId Β· lat/long Β· ts
driverId is nullable until matched
Fare = the quote Β· Ride = the trip Β· Location = the hot, high-write one
status: requested β matched β accepted β in-progress β completed
3API
POST /fare {pickupLocation, destination} β Fare getFareEstimate()
POST /rides {fareId} β Ride
POST /drivers/location {lat, long} β OK updateLocation()
PATCH /rides/:rideId {accept | deny} β Ride acceptOrDeclineRide()
β
Never take driverId/riderId from the body β read it from the JWT.
A client-supplied id lets anyone move any driver.
4High-level design
βββββββββββββββ 3rd-party Maps βββ
Rider ββ βββββββββββββββββ β β
ββββΆβ API GATEWAY ββββΆ RIDE SERVICE ββββββββββββββΆ DB (Dynamo/Postgres)
Driver ββ β routingΒ· auth β fare est Β· create ride Fare + Ride
β² β rate-limit β triggers match workflow
β βββββββββββββββββ β
β β βΌ
β β [ MATCH QUEUE ]
β β β
β β βΌ βββΆ DISTRIB. LOCK (Redis)
β β MATCHING SERVICE βββ€ "driver already asked?"
β β βββΆ LOCATION DB (Redis)
β ββββΆ LOCATION SERVICE ββββββββββββββΆ "closest drivers"
β updateLocation()
βββ NOTIFICATION SVC (APNs/FCM) βββ "accept / decline?"
β
Redis appears twice β different jobs
- Location DB β geohash sorted set, "who is near this point"
- Distributed Lock β driverId key + TTL, "is this driver already claimed"
- 10 M drivers Β· 5 s ping β 2 M writes/sec Β· naive DynamoDB β $200k/day
Deep dives
the part the interview is actually scored on
1Geospatial β 2 M writes/sec
Why it breaks: B-trees order in 1-D. "Drivers within 2 km" is a 2-D range query β the index can't serve it.
- Redis geo β GEOADD write Β· GEOSEARCH radius read
- Geohash packs lat/long β 52-bit int = sorted-set score
- GEOADD overwrites β always current position, no history to prune
- TTL drops drivers offline > 30 s automatically
- Durability? don't care β one 5 s ping cycle rebuilds everything
alt (good, not great): Postgres + PostGIS, quadtree index
2Adaptive ping intervals
- Fixed 5 s for everyone wastes battery + bandwidth
- Client varies rate by speed, heading, nearby demand, idle vs active
- β
not every optimisation is server-side
3Double-booking a driver
Race: two Matching instances pick the same driver at the same instant.
lock(driverId, TTL 10s) β notify β accept β update ride + release
ββ silence β TTL expires β driver free
- Redis distributed lock, key = driverId, TTL 10 s
- TTL does double duty: mutual exclusion and the response timeout
- Service crashes holding it? Expires itself β no orphaned driver
4Peak demand β lose nothing
- Kafka ahead of Matching; commit offset only after a successful match
- Crash before commit β redelivered, not lost
- Autoscale workers on queue depth Β· partition geographically
- FIFO β priority queue so one slow request can't head-of-line block
5Driver doesn't respond
- Durable execution β Temporal / AWS Step Functions
- send to ranked driver β wait 10 s β accept β Β· decline/timeout β next driver
- State persisted β survives crash, resumes where it stopped
- Removes the hand-rolled delay-queue + race-condition mess
6Scaling for latency
- Geo-shard services, queues, DBs β shorter clientβserver hop
- Read replicas for read throughput Β· consistent hashing to balance
- Scatter-gather only when a radius crosses a shard boundary
Level bar
- E4: working HLD, know you need a geo index, some driver lock
- E5: 2+ deep dives in detail, argue trade-offs
- L8+: raise 3+ unprompted, from real experience
Recall in 20 s
Redis geohash β 2M w/s Β· lock + TTL β no double-book Β·
Kafka, late offset commit β no lost reqs Β· durable workflow β timeouts Β·
geo-shard β latency.