- Published on
- Views
Design Ride Booking like Uber System Design Interview Guide
- Authors

- Name
- Javed Shaikh
← System Design Interview Preparation
This guide walks through Design Ride Booking like Uber the way you would in a backend or Java interview. The numbers are interview estimates. They help you show your thinking. They are not a production capacity plan.
1. Problem
A ride app matches a rider who wants to go from A to B with a nearby driver who can take the trip, then tracks the car until drop-off and charges the rider.
Example:
- Rider opens the app at the airport
- App shows ETA and a price
- Rider confirms
- System finds a driver 4 minutes away
- Driver accepts, rider watches the car move
- Trip ends, payment captures, both rate each other
The hard parts are live location, matching under time pressure, and trip state that must not get stuck. Maps, pricing, and payments are supporting services.
2. Functional Requirements / FR
| Requirement | What it means |
|---|---|
| Request ride | Pickup, dropoff, ride type (mini / sedan). |
| Driver location updates | Drivers send GPS often while online. |
| Nearby driver search | Find available cars around pickup. |
| Matching | Offer the trip to one (or a few) drivers. |
| Trip lifecycle | requested → accepted → arrived → in_trip → completed / canceled. |
| Pricing basics | Estimate up front, final fare at end, surge as a multiplier. |
| Payment | Pre-auth or charge after trip. |
| Notifications | Driver offer, rider “driver arriving.” |
| Real-time tracking | Rider sees driver location during pickup and trip. |
| Driver cancellation | Rematch or fail the trip with a clear state. |
Out of scope: driver onboarding KYC, full routing engine internals, carpooling matching details.
3. Non-Functional Requirements / NFR
| Requirement | Why it matters |
|---|---|
| Low matching latency | Riders cancel if matching takes a minute. |
| Location freshness | Stale GPS matches a driver who already left. |
| Correctness of trip state | Double-assigning two riders to one car is a support nightmare. |
| High write volume | Location pings dominate QPS. |
| Availability | Regional outage should not strand in-trip users if possible. |
| Audit | Fares and cancellations need a log for disputes. |
Interview line: location writes are huge and lossy. Trip state is smaller and must be correct.
4. Back-of-the-Envelope Calculation
Traffic assumptions
- 1 million concurrent online drivers in a large region (interview-scale global peak; you can also say 100k for one city)
- Each driver sends location every 4 seconds
- 10 million trips per day
- Each trip: ~10 rider/driver API calls besides GPS
Location QPS (the big number)
1,000,000 drivers / 4 seconds = 250,000 location writes/sec
If you only have 100,000 online drivers:
100,000 / 4 = 25,000 QPS
Say the city-scale number out loud, then mention the global number. This traffic belongs in a location service + geo index, not in the trip table.
Trip QPS
10,000,000 trips/day / 86,400 seconds = around 116 QPS
If peak is 5×, design for around 600 QPS of match/complete APIs. Easy compared to GPS.
Read/write: location is write-heavy. Trip APIs are mixed but small.
Storage
Trip row ~ 1 KB:
10,000,000 × 1 KB ≈ 10 GB/day
Fine for a database cluster.
Location: do not store every ping forever in SQL.
250,000 pings/sec × 50 bytes ≈ 12.5 MB/s ≈ 1 TB/day
Keep latest location in memory/Redis. Archive a sample for analytics.
Cache / memory estimate
Geo index: 1 million drivers × 100 bytes (id, lat, lng, status, geohash) ≈ 100 MB. Tiny. The trick is update rate, not size.
Trip cache for active trips: 100,000 active × 2 KB ≈ 200 MB.
Server estimate
Location ingest: 25k–250k QPS. UDP/HTTP to a dedicated fleet. At 10k QPS per location server:
25,000 / 10,000 ≈ 3 servers (city)
250,000 / 10,000 ≈ 25 servers (large)
Ride/match API: a handful of Spring Boot servers. Matching is CPU + geo queries.
These are interview estimates, not exact production numbers.
5. APIs
Rider: request ride
POST /api/rides
{
"pickup": { "lat": 19.0896, "lng": 72.8656 },
"dropoff": { "lat": 19.0760, "lng": 72.8777 },
"product": "sedan"
}
Returns rideId, status: matching, etaSec, quote.
Driver: location
POST /api/drivers/me/location
{ "lat": 19.09, "lng": 72.86, "heading": 180, "ts": 1720000000 }
High volume. Batch or compact payloads. Auth as the driver.
Driver: accept / decline
POST /api/rides/:rideId/accept
Rider: cancel
POST /api/rides/:rideId/cancel
Trip status
GET /api/rides/:rideId
Tracking (often WebSocket / SSE)
GET /api/rides/:rideId/location-stream
Pushes driver coordinates to the rider.
Payment is an internal call to a payment service, not a public charge API from the rider app besides “pay this ride.”
Errors: 409 already accepted by someone else, 404, 429.
6. Data Model
rides
| Field | Notes |
|---|---|
ride_id | |
rider_id | |
driver_id | Null until accept |
status | See lifecycle |
pickup, dropoff | Lat/lng + optional place id |
product | |
quote_cents | Up-front estimate |
fare_cents | Final |
version | Optimistic lock for accept |
created_at |
ride_events
Append-only: matched, accepted, canceled_by_driver, completed. Dispute log.
drivers_live (memory / Redis)
driver_id, geohash, lat, lng, status (idle, offered, busy), updated_at.
TTL: if no ping in 15 seconds, treat as stale / offline.
quotes / payments
ride_id, preauth_id, capture_id, status. Payment service owns the money schema.
Do not put GPS history in rides.
7. High-Level Design
Split location, matching, trip state, and payment.
Ride Booking like Uber architecture
Components:
- Rider App → API Gateway → Ride Service
- Driver App sends GPS → Location Service → Geo Index (Redis GEO, geohashes, or similar)
- Matching Service reads the geo index, filters idle drivers, ranks by ETA
- Notification Service pushes the offer to the driver (and status to the rider)
- Trip DB stores lifecycle
- Payment Service pre-auths at request, captures at complete
Match flow:
- Rider requests. Ride row =
matching. Quote from pricing (distance × rate × surge). - Matching asks geo index: idle drivers in nearby geohashes.
- Offer the best driver. Status
offered. - If accept with
versioncheck, setdriver_id, statusaccepted. - If timeout or decline, offer the next driver.
- During trip, rider subscribes to location of that one driver, not the whole geo index.
Why not one service? GPS QPS would drown trip transactions. This is microservices for a real reason.
Notifications: reuse the idea from notification system. Location-heavy products also show up in Where Is My Train backend.
8. Deep Dives
Nearby driver search
Geohash the pickup. Query this cell and neighbors. Filter idle and fresh pings. Rank by haversine or a cheap ETA. Do not run Google Directions for 200 candidates.
Matching service
Options:
- Sequential offer: one driver, 15 second timer
- Broadcast: several drivers, first accept wins (need a compare-and-set on
rides)
Sequential is simpler on support. Broadcast is faster at airports. Mention both; pick sequential for v1.
Trip lifecycle
State machine in the ride service. Illegal jumps (completed → matching) are 409. Use version so two accepts cannot both succeed.
Pricing basics
Estimate = f(distance, time, product, surge). Surge from a demand/supply ratio per geohash, cached for 1 minute. Final fare can differ slightly. Show the quote so users are not surprised. Disputes go to ride_events.
Payment
Pre-auth when requesting so you do not dispatch to a driver for a declined card. Capture on complete. If payment fails after the trip, mark payment_failed and retry. Do not invent a ledger in this interview unless asked — point to a payment service.
Real-time tracking
After accept, the rider only needs one driver id. Pub/sub channel ride:{id} is enough. Do not stream all city GPS to every phone.
Driver cancellation
If status is accepted or arrived, clear driver_id, set matching, notify rider, rematch. If in_trip, that is a different policy (maybe complete with partial fare). Say the policy out loud.
Maps / location service
You still need a maps provider for turn-by-turn and distance. Your geo index is for who is nearby, not for navigation.
9. Bottlenecks
- Location ingest QPS
- Hot geohash (airport, stadium)
- Match storms after a concert
- Notification delay making offers expire
- Trip DB if you write GPS into it by mistake
Mitigate: dedicated location cluster, shard geo by region, rate-limit match retries, priority push, keep GPS out of SQL.
10. Tradeoffs
| Choice | Upside | Downside |
|---|---|---|
| Sequential offer | Fair, simple | Slower match |
| Broadcast offer | Fast | Two drivers race |
| Redis GEO | Fast nearby | Memory, regional shard |
| DB spatial index | Durable | Too slow for 4s pings |
| Strongly consistent accept | No double book | Extra latency |
Pick: in-memory geo, CAS accept, sequential offer, payments as another service.
11. Failure Modes
| Failure | Handling |
|---|---|
| Location service down | Stop new matches. In-trip tracking degrades; drivers can still complete via last-known + driver app. |
| Matching down | Queue requests, show “looking.” |
| Trip DB down | No new rides. In-memory in-trip state is dangerous; fail closed for money events. |
| Driver never responds | Timeout, next driver. |
| Payment capture fails | Trip completed operationally; billing retry. |
| Split brain two drivers | Unique driver_id + version. Second accept 409. |
Never leave a ride in matching forever. TTL and a sweeper job.
12. Interview Answer in 10 Minutes
"I would split Uber into location, matching, ride state, and payment.
Location is the scale problem. 100,000 online drivers pinging every 4 seconds is about 25,000 QPS. A million drivers would be 250,000 QPS. That data lives in a geo index with TTL. Trip volume is much smaller: 10 million trips a day is about 116 QPS, maybe 600 at peak.
Rider calls ride service. We store a ride row, get a price quote, pre-auth payment, then matching queries nearby idle drivers. We offer one driver through notifications. Accept is an optimistic lock so two drivers cannot win.
During the trip the rider subscribes to that driver's location stream. Complete captures payment. If the driver cancels before pickup, we rematch.
I would not put GPS pings in Postgres. I would not run matching inside the location ingest path."
Practice this until it is under 10 minutes.
13. Interview Talking Points
- GPS QPS vs trip QPS — different systems.
- Geo index with freshness TTL.
- State machine + optimistic lock on accept.
- Payment pre-auth before dispatch.
- Rematch on driver cancel.
- Pub/sub per ride for tracking.
- Metrics: match time, offer accept rate, ping freshness, cancel rate, payment success.
- Queues for offers and notifications: Kafka, event-driven architecture.
14. Follow-up Questions
Airport lot with 500 idle cars?
Smaller geohash, supply cap, maybe a queue of riders. Do not notify 500 drivers.
How do you compute ETA?
Straight-line first. Refine with traffic for the chosen driver only.
Pooling / UberPool?
Extra matching constraints. Skip unless asked.
Multi-city?
Shard by region. A driver in Pune is not in the Mumbai index.
Java stack?
Ride service and matching in Spring Boot, Redis GEO, Kafka for offer events, WebSockets for tracking.
15. Internal Links
Related JavaThoughts reading:
- System Design Interview Preparation
- Design a Notification System
- Design Food Delivery / Order Workflow
- Design a Distributed Cache
- Java
- Spring Boot
- Kafka
- Microservices
- Distributed Systems
- API Gateway
- Event-driven architecture
- Inside Where Is My Train: backend and accuracy
- 20 system design concepts
Next in this series: Design Food Delivery / Order Workflow.
