The brief
Design the backend for a ride-sharing service. Riders request rides; drivers accept; live tracking; payments; ratings.
This is a full-length mock simulating an actual senior interview. Pace yourself.
Phase 1: Requirements (5 min)
Functional
- Rider opens app, sees nearby drivers.
- Rider requests ride (pickup + destination).
- System matches a nearby driver.
- Driver accepts; rider sees driver's location in real-time.
- Trip happens; payment automatic.
- Ratings post-trip.
Non-functional
- Low latency for matching (< 5s).
- Real-time location updates (every 5s while moving).
- Reliable payments.
- Scale: 10M rides/day globally.
Out of scope (clarify)
- Carpool / shared rides (focus on 1-rider-1-driver).
- Surge pricing details.
- Cross-border trips.
Phase 2: Capacity estimation (5 min)
- 10M rides/day = ~115 rides/sec average. Peak: 1000+/sec (rush hours).
- 1M active drivers globally; 100M registered.
- Location updates: 1M drivers × 1 update / 5 sec = 200K updates/sec.
- Each location update ~100 bytes → 20 MB/sec → ~2TB/day for location data.
- Trip data: 10M trips × 5KB = 50 GB/day.
Phase 3: High-level architecture (10 min)
[Rider App] [Driver App]
↓ ↓
[API Gateway] [API Gateway]
↓ ↓
[Trip Service] [Location Service] [Matching Service]
↓ ↓ ↓
[Trips DB] [Geo Index] [Driver Pool]
↓ ↓
[Payment Service]
↓
[Rating Service]
Plus:
- WebSocket / push notification service for real-time updates.
- Map / routing service (third-party: Google Maps or OSM).
- Analytics pipeline (Kafka → data warehouse).
Phase 4: Deep dive 1 — Location service (8 min)
Storing driver locations
Each driver app sends location every 5 seconds:
{driver_id, lat, lng, timestamp}
Volume: 1M drivers × 1 / 5sec = 200K writes/sec. Significant.
Storage:
- Geo-indexed Redis (Redis geospatial commands): lat/lng indexed; query "drivers within X km of point". Fast.
- Cassandra with geohash key: also works; persistent.
Most common: Redis for active locations (hot), Cassandra for history.
Geo queries
"Find drivers within 5km of (lat=12.97, lng=77.59)":
- Geohash: encode lat/lng to a string; nearby points share prefix.
- Or QuadTree (used by Uber's older H3 system).
- Redis GEORADIUS handles this.
Map matching
Driver's raw GPS → actual road segment they're on. Map-matching algorithm + map data (third-party).
Phase 5: Deep dive 2 — Matching (8 min)
When rider requests ride:
- Get rider's location.
- Query Location Service: "drivers within 5km, status=available".
- Score candidates: distance, ETA (using map service), driver rating.
- Send ride request to top driver.
- Driver has 15 seconds to accept; if rejected, try next.
- On accept: mark driver "in trip"; both apps notified.
Matching service is stateful (active dispatches). For HA: state in Redis; matching workers stateless.
Reducing matching latency
- Pre-cache nearby drivers' availability.
- Push notifications (not polling) to driver app.
- Limit candidates to top 5; parallel offer if first doesn't accept fast.
Geographic sharding
Match local-to-local: a rider in Mumbai doesn't need to consider Bengaluru drivers. Shard matching service by city / region.
Phase 6: Deep dive 3 — Trip state machine (5 min)
REQUESTED → MATCHED → DRIVER_EN_ROUTE → ARRIVED →
IN_PROGRESS → COMPLETED → PAID → RATED
Each transition triggers events; multiple services react.
Trip storage: relational DB (Postgres) — strong consistency for state transitions; transactional payment.
CREATE TABLE trips (
trip_id BIGINT PRIMARY KEY,
rider_id BIGINT,
driver_id BIGINT NULL,
state VARCHAR(20),
pickup_location POINT,
destination POINT,
requested_at TIMESTAMP,
started_at TIMESTAMP NULL,
completed_at TIMESTAMP NULL,
fare DECIMAL(10,2) NULL
);
Phase 7: Real-time updates (4 min)
While trip in progress:
- Driver app pushes location every 5s.
- Rider app shows driver's live location on map.
Push to rider via WebSocket:
- Driver location update → Location Service → publish to topic "trip:{trip_id}".
- Rider's app subscribed to that topic.
Architecture: similar to chat app's connection servers.
Phase 8: Payments (3 min)
End of trip:
- Calculate fare (base + distance + time + surge).
- Charge stored payment method (Stripe / Razorpay).
- Disburse to driver (T+N days).
Trip state machine waits for payment confirmation:
- Sync charge attempt; if fails, retry async.
- On hard failure: notify rider; trip marked PAYMENT_FAILED.
Critical: idempotency. Same trip charged twice = customer complaint. Use trip_id as idempotency key.
Phase 9: Ratings & feedback (2 min)
Post-trip:
- Rider rates driver (1-5 stars + tip + review).
- Driver rates rider.
- Stored in ratings table; aggregated for driver scores.
Use eventual consistency for aggregate scores (background job).
Phase 10: Scaling considerations (5 min)
- Location writes: 200K/sec → Redis Cluster sharded by region.
- Matching: stateful service; sharded by city.
- Payments: critical path; SQL with replicas; idempotency.
- WebSocket connections: 10M+ → connection server fleet.
Failure modes
- Location service down: matching can't happen. Mitigate: cached recent locations.
- Payment provider down: queue charges; retry; degrade gracefully.
- Driver app loses connection mid-trip: ride continues; reconnect on resume.
Common follow-ups
- Surge pricing: dynamic multiplier based on demand:supply ratio per region. Updated every N min.
- Carpooling: matching algorithm extended to multiple riders; route optimization.
- Multi-region: data residency; regional sharding.
- Fraud detection: real-time ML scoring on trip patterns.
Self-rubric
- Clarified requirements (5 min)
- Capacity estimation
- High-level architecture with all major components
- Deep dive: location service (geo-indexing, write throughput)
- Deep dive: matching (algorithm, sharding by region)
- Trip state machine
- Real-time updates (WebSocket)
- Payments with idempotency
- Identified failure modes and mitigations
- Stayed within 45 minutes
Deliverables
- Full architecture diagram.
- Deep dive on 2-3 components (location, matching, payments).
- Discussion of failure modes.
- A retrospective: where you spent time, what you skipped, what you'd add with more time.
What you'll learn
The Uber problem combines:
- Geo / location indexing.
- Real-time updates (WebSocket).
- Stateful matching service.
- Payment systems (idempotency critical).
- Multi-region scaling.
It tests breadth: can you design across the full stack? Most senior interviews are like this. Practice this one cold; many companies riff on it (Lyft, DoorDash, Swiggy variants).