Uber / Lyft / Ola#
Problem statement (interviewer prompt)
Design Uber / Lyft: riders request rides, the system finds a nearby driver in seconds, computes fare with surge, tracks the trip live, and charges on completion. Handle 10M+ rides/day, location pings every 1-3s, and dispatch decisions in <5s.
flowchart LR
R([Rider])
D([Driver])
DISP[Dispatch]
GEO[Geo Index]
PRICE[Pricing / Surge]
PAY[Payment]
R --> DISP --> D
D --> GEO
DISP --> GEO
DISP --> PRICE
R --> PAY
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class R,D client;
class DISP,GEO,PRICE,PAY service;
flowchart TB
subgraph Apps
RAPP([Rider app])
DAPP([Driver app])
end
subgraph Edge
CDN
LB
GW[Edge gateway<br/>per-region]
WS[WebSocket]
end
subgraph Geo[Geo Indexing - hot path]
LOC([Location Ingest])
GIDX[(Driver presence<br/>S2 cell / geohash)]
AVAIL([Available drivers per cell])
ETA1([Ego-driver ETA estimator])
end
subgraph Dispatch[Dispatch / Matching]
DSVC[Dispatch Service]
POOL[Candidate pool fetch]
SCORE[Score: distance, ETA, ratings, surge]
OFFER([Offer to driver - 15s])
BATCH[Batch / pool rides]
end
subgraph Pricing
BASE[Base fare]
SURGE[Surge model]
EST[Trip estimate]
end
subgraph Trip
LIFE[Trip lifecycle SM]
NAV[Nav + route]
TKM([Telematics ingest])
RECEIPT[Receipt + tax]
PAYSVC[Payment]
PG((Payment provider))
end
subgraph Safety
BG[Background checks]
PIN[Trip PIN]
SOS[Emergency / panic]
end
subgraph Data
KAFKA[[Kafka event bus]]
FS[Feature store]
ML[ML platform]
OBS[Metrics / traces]
end
RAPP --> CDN --> LB --> GW
DAPP --> LB
DAPP --> WS --> LOC --> GIDX
RAPP --> Dispatch
Dispatch --> Geo
Dispatch --> Pricing
Dispatch --> Trip
Trip --> PAYSVC --> PG
Trip --> KAFKA
Geo --> KAFKA --> FS --> ML
ML --> Pricing
Safety --- Trip
OBS --- Dispatch
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class RAPP,DAPP,AVAIL,ETA1,OFFER client;
class GW edge;
class WS,DSVC,POOL,SCORE,BATCH,BASE,SURGE,EST,LIFE,NAV,RECEIPT,PAYSVC,BG,PIN,SOS,ML service;
class GIDX,FS datastore;
class KAFKA queue;
class LOC,TKM compute;
class PG external;
class OBS obs;
Geo index#
- Driver location every 1-3 s → server.
- Index sharded by S2 cell ID; updates write-heavy.
- Active drivers stored in Redis-like KV with
(driver_id, cell, ts); TTL purges stale.
Dispatch algorithm#
- Customer requests ride at point P.
- Geo lookup returns drivers in expanding S2 ring around P.
- Score each candidate (ETA + last assignment time + ratings).
- Offer to top driver; 15 s timeout; on reject, next.
- Update driver presence → unavailable on accept.
Surge pricing#
- Per S2 cell × minute: demand/supply ratio.
- Multiplier applied at quote time; shown to user before confirm.
Trip lifecycle#
- States: REQUESTED → DRIVER_ASSIGNED → EN_ROUTE → ARRIVED → IN_PROGRESS → COMPLETED / CANCELLED.
- Each transition emits an event; finance + analytics consume.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
CDN | edge caching for static assets | cdn |
HLD |
Sharding | horizontal partitioning across nodes | database-sharding |
HLD |
Pub/Sub & message brokers | topics, consumer groups, delivery semantics | pub-sub-pattern |
HLD |
Observability | metrics, logs, traces, SLOs | observability |
HLD |
Realtime protocols | WS / SSE / polling / gRPC streaming | realtime-protocols |
HLD |
Geo indexing | Geohash, Quadtree, S2, H3, R-tree | geo-indexing |
Quick reference#
Functional#
- Request ride, match driver, compute fare.
- Live tracking, ETA.
- Pay, rate, tip.
- Surge pricing.
- Driver onboarding, payouts.
Non-functional#
- Dispatch decision < 5 s.
- Driver location lag < 3 s.
- 99.99% on hot path.
Capacity#
- Millions of active drivers globally.
- 10M+ rides/day.
- Location ingests: millions/s peak.
Schema#
drivers(id, status, vehicle, ratings)presence(driver_id, cell, lat, lng, ts)Redis Geo / KVtrips(id, rider_id, driver_id, pickup, drop, fare, status_history[])payments(...)
Trade-offs#
- Centralized matcher simpler but bottleneck → matcher sharded by city / region.
- Eventual presence consistency is fine; double-assign protected by atomic update.
- Surge balances supply/demand but unpopular; cap and explain.
Refs#
- Uber Engineering blog (Schemaless, Ringpop, Cadence, M3, H3 hex grid).
- Lyft engineering (Envoy origin) blog.
- ByteByteGo "Design Uber", Alex Xu Vol 2.
FAQ#
How does Uber match riders to nearby drivers in seconds?#
Drivers stream GPS pings to a geo-sharded index like S2 or H3. When a rider requests, the dispatcher queries the cell, scores candidates by ETA and acceptance probability, and offers the trip.
How does Uber handle 10 million rides per day at scale?#
Services are sharded by city or region so most traffic stays local. Dispatch, pricing, payments, and trip state run as independent microservices behind their own data stores.
How does surge pricing work?#
When demand exceeds supply in a cell over a short window, a multiplier raises the price. Higher pay attracts drivers from adjacent cells and gently discourages low priority trips.
How does Uber track an active trip in real time?#
The driver app pushes location every 1 to 3 seconds via WebSocket. A trip state machine updates ETA, route, and milestones. The rider subscribes to the same channel for live updates.
How are payments charged after a trip ends?#
Trip cost is computed from time, distance, surge, and tolls. The payment service authorizes and captures asynchronously with idempotent retries, then triggers receipts and driver payouts.
Further reading#
Curated, high-credibility sources for going deeper on this topic.
- ✍️ Blog - Uber Engineering blog
- 📑 Docs - Uber H3 - hexagonal hierarchical spatial index
- ✍️ Blog - Uber - Schemaless: high-availability datastore