System Design Arena

System Design: Ride-Hailing Service (like Uber)

Use an interview-style checklist to reason through matching, geo-spatial indexing, surge pricing, and operational metrics.

Functional RequirementsNon-Functional RequirementsAPI Endpoints

Case Study

Ride-Hailing Service

Same conversation, diagrams, and wrap-up you expect—now framed with clearer scaffolding and iconography.

Prompt

Build a marketplace that pairs riders with nearby drivers while streaming live locations, computing surge, and settling payments globally.

Interview snapshot

  • • Topic: Ride-Hailing Service
  • • Expected depth: 45 - 60 minutes
  • • Focus areas: APIs, scale estimation, resilient architecture
  • • Wrap-up: risk, monitoring, disaster recovery

Key takeaways

  • Rider app: request, cancel, share trip, rate driver.
  • Location latency < 3 s; ride assignment < 2 s.
  • POST /api/rides - rider request with pickup/dropoff, preferences.

🎙️ Interview mode

Practice this like a real interview

Don't read the answer first. Work the framework against the bare prompt, then compare against what the interviewer expected at each step.

🎯 Start guided practice (free)

Scorecard

What the interviewer is evaluating

Requirements8/10
Scale estimation8/10
API design6/10
Architecture9/10
Trade-offs9/10
Failure handling8/10

Staff-level signal

Does the candidate recognize that matching is a geospatial streaming problem with a freshness deadline — and design the location pipeline before drawing any services?

🧠 Staff engineer judgment

Driver locations are ephemeral — losing 30 seconds of them is harmless. Do not design durable storage for data whose value expires in 4 seconds.

Calibration

The common (bad) answer

Rider app → API → Matching Service → Driver app
            ↓
        Database of drivers

❌ Why this scores poorly: Treats matching like a database query. With 1M drivers updating location every 4 seconds, "SELECT nearest driver" against a table is dead on arrival.

✓ What a strong answer adds

  • Size the location firehose first: drivers × update rate = the real design constraint.
  • Use geo-indexing (geohash/S2 cells) held in memory, not a relational table.
  • Separate the always-on location stream from the request/response trip lifecycle.
  • Explain surge/matching as an eventually-consistent view — riders see ~4-second-old truth.
  • Design the trip state machine with idempotent transitions for retries.

Build it up

Step-by-Step Walkthrough

Ride-hailing is a moving-data problem: both sides of the marketplace are GPS dots that will not sit still. Build it up in stages and narrate why each piece exists.

1

Naive matching: query all drivers

Rider requests a trip; the server scans the drivers table for anyone nearby, picks the closest, done. With ten drivers in one city this is fine.

The bottleneck is obvious the moment you say it: “find nearby” over raw lat/lng columns is a full scan, and driver positions change every few seconds, so the table is also write-hot.

2

Index space itself: geohash / H3 cells

Divide the map into cells (geohash prefixes or Uber's H3 hexagons). Drivers are bucketed by the cell they are in; “nearby” becomes “this cell plus its neighbors” — a handful of key lookups instead of a scan.

Keep this hot index in memory (Redis), not the database: it is rebuilt continuously from location pings and loses nothing important if it restarts.

?Why hexagons come up

Squares have neighbor-distance asymmetry (corner neighbors are √2 farther). Hexagons make “adjacent cell” mean roughly the same distance in every direction — which is why H3 exists. Mentioning this earns real credit.

3

Make dispatch a stateful conversation

Matching is not a query, it is a negotiation: offer the trip to the best driver, wait ~10 seconds, cascade to the next on decline. That state machine needs somewhere durable to live so an instance crash does not orphan a rider.

Trip state (requested → offered → accepted → in-progress → completed) goes to a transactional store; the offer timers and cascade logic live in the dispatch service.

4

Surge, ETAs, and the read fan-out

Pricing needs supply/demand per cell (computed from the same geo index), ETAs need a routing engine over road-network data, and riders watching the car crawl toward them generate a huge stream of location reads.

Location fan-out to watching riders goes over persistent connections (WebSocket/gRPC streams) fed by the location service — never by polling the trip store.

!The consistency trap

Do not promise strong consistency on locations — stale-by-two-seconds is fine everywhere except payment and trip state. Splitting the system by consistency need (transactional trips vs. streamed locations) is the senior signal.

Blueprint

Architecture Diagram

The narrated components above, laid out as an actual flow — so you can see how a request moves through the system, not just read a list of pieces.

Ride Request

1

Clients

rider + driver apps

2

API Gateway

WebSockets terminate at regional real-time clusters

Location Ingest

1

Location Ingest Service

writes to GeoRedis/S2 index

2

Kafka

publishes for analytics/surge pricing

Matching

1

Matching Service

consumes rider requests

2

Bipartite Match

pairs against driver availability

Ride State Machine

1

Temporal / Cadence

workflow engine drives ride state machine + timeout handling

Payments

1

Payments Service

interacts with PCI vault, ledger

2

Payout System

settles driver earnings

Flow Diagram

How the API maps to this flow

  1. 1POST /api/ridesrider request with pickup/dropoff, preferences.
  2. 2POST /api/rides/{id}/acceptdriver confirms, locking the ride.
  3. 3POST /api/locationsbatched driver GPS updates.
  4. 4POST /api/rides/{id}/completemarks ride finished, triggers billing.
  5. 5GET /api/rides/{id}/trackingrider fetches state when sockets unavailable.

?But why does this actually hold up at scale?

5M DAU -> 833K location updates/sec globally; sharded by city reduces to ~167K/sec shard.

Video walkthrough

Design Uber — System Design Interview breakdown

Hello Interview · ex-Meta staff engineer · 1h 3m

Matching and geospatial indexing done at interview pace, with the trade-offs argued out loud.

Work the prompt yourself first — guided practice grades your own answer step by step. Watching someone else design it feels like progress and teaches you far less.

Interview flow

Dialogue timeline

Interviewer

What flows must we support?

Candidate

Rider requests, driver accepts, turn-by-turn tracking, payment capture, driver payout. Also pooled rides and cancellations.

Interviewer

Where does scale bite first?

Candidate

Location ingest. 1M drivers pinging every few seconds -> hundreds of thousands of writes/sec into a geo index.

Interviewer

Walk me through matching.

Candidate

Rider request hits marketplace service, which queries nearby drivers via S2 grid in Redis, scores them, and pushes offers over WebSockets. A workflow engine tracks ride state.

Interviewer

How do you keep payments and compliance sane?

Candidate

Payments microservice talks to PCI vault, stores minimal tokens, emits ride receipts, and enforces data residency by region. Events flow to a ledger service for auditing.

Scoping

Requirements & trade-offs

Functional Requirements

  • Rider app: request, cancel, share trip, rate driver.
  • Driver app: toggle availability, accept/decline, navigate, receive payouts.
  • Marketplace: nearest-driver matching, ETA recalculation, surge pricing.
  • Payments: capture fares, tips, refunds, settlements to driver wallet.

Non-Functional Requirements

  • Location latency < 3 s; ride assignment < 2 s.
  • Availability 99.95% with graceful degradation (fallback SMS updates).
  • Strong observability: replay ride timeline, detect fraud, capture audit trail.
  • Regional compliance: data residency (EU vs US), receipts retained 7+ years.

Blueprint

Architecture modules

Module 1

API Endpoints

  • POST /api/rides - rider request with pickup/dropoff, preferences.
  • POST /api/rides/{id}/accept - driver confirms, locking the ride.
  • POST /api/locations - batched driver GPS updates.
  • POST /api/rides/{id}/complete - marks ride finished, triggers billing.
  • GET /api/rides/{id}/tracking - rider fetches state when sockets unavailable.

Module 2

Back-of-the-Envelope

  • 5M DAU -> 833K location updates/sec globally; sharded by city reduces to ~167K/sec shard.
  • Ride requests peak 50K/sec; need stateless API tier with autoscaling.
  • Driver presence: 1M drivers * 2 KB session ~ 2 GB in memory/Redis.
  • Kafka topic for telemetry sized for 1M msgs/sec (3 brokers * 3 replicas).

Module 3

System Diagram Notes

  • Clients -> API gateway; WebSockets terminate at regional real-time clusters.
  • Location ingest service writes to GeoRedis/S2 index + publishes to Kafka for analytics/surge.
  • Matching service consumes rider requests + driver availability to run bipartite matching.
  • Temporal/Cadence workflow drives ride state machine + timeout handling.
  • Payments service interacts with PCI vault, ledger, payout system.
  • Analytics lake stores immutable ride events for ML and finance.

Module 4

Design Playbook

  • Clarify SLAs (booking time, ETA accuracy).
  • Define data models: driver session, rider request, ride event log.
  • Explain geospatial indexing + fallback when cells sparse.
  • Cover failure scenarios: driver drops offline, duplicate charges, surge spikes.
  • Discuss monitoring: city-level supply/demand, fraud signals, payments reconciliation.
  • Wrap with DR strategy: active-active for marketplace, active-passive for PCI zone.