System design interviews separate senior engineers from everyone else. There's no single correct answer - interviewers evaluate your structured thinking, trade-off analysis, and ability to navigate ambiguity. This lesson is a complete 45-minute simulation.
"Design a food delivery platform like Uber Eats or Deliveroo that connects customers, restaurants, and delivery drivers."
Never start drawing boxes immediately. Ask clarifying questions first:
Functional requirements:
Non-functional requirements:
Why is eventual consistency acceptable for restaurant search but NOT for payment processing?
Define the core contracts before architecture:
# Customer APIs
GET /api/v1/restaurants?lat={}&lng={}&cuisine={} → Restaurant[]
GET /api/v1/restaurants/{id}/menu → Menu
POST /api/v1/orders → Order
GET /api/v1/orders/{id}/track → OrderStatus + DriverLocation
# Restaurant APIs
PATCH /api/v1/restaurants/{id}/menu/items/{itemId} → MenuItem
POST /api/v1/orders/{id}/accept → Order
POST /api/v1/orders/{id}/ready → Order
# Driver APIs
POST /api/v1/drivers/{id}/location → void (fire-and-forget)
POST /api/v1/deliveries/{id}/accept → Delivery
POST /api/v1/deliveries/{id}/complete → Delivery
Key decision: use WebSockets for real-time driver tracking, REST for everything else.
| Service | Responsibility | Database | |---------|---------------|----------| | User Service | Authentication, profiles, addresses | PostgreSQL | | Restaurant Service | Menus, availability, hours | PostgreSQL + Elasticsearch | | Order Service | Order lifecycle, state machine | PostgreSQL (strong consistency) | | Driver Service | Driver location, availability, matching | Redis (location) + PostgreSQL | | Payment Service | Charges, refunds, restaurant payouts | PostgreSQL (ACID required) | | Notification Service | Push, SMS, email notifications | Message queue consumer |
When an order is ready for pickup, the matching service must assign the optimal driver:
Score(driver) = w1 × (1 / distance_to_restaurant)
+ w2 × driver_rating
+ w3 × (1 / current_active_orders)
+ w4 × acceptance_rate
Implementation: query Redis GEORADIUS for drivers within 5km of the restaurant, score each candidate, assign the highest-scoring available driver. If declined, offer to the next driver with a 30-second timeout.
Client ←→ WebSocket Gateway ←→ Driver Service
↑
Redis GEO Store
(GEOADD, GEOPOS)
Drivers publish GPS coordinates every 4 seconds. The WebSocket gateway subscribes to location updates for active orders and pushes them to the customer's device. Redis Pub/Sub handles fan-out efficiently.
EDT is a multi-stage prediction:
EDT = restaurant_prep_time + driver_pickup_travel + dropoff_travel + buffer
Where:
- restaurant_prep_time → ML model trained on historical order data per restaurant
- driver_pickup_travel → distance/traffic API (Google Maps, Mapbox)
- dropoff_travel → same routing API
- buffer → dynamic, increases during rain/peak hours
Payments follow a two-phase pattern:
This handles order modifications, cancellations, and partial refunds gracefully. Use an idempotency key on every payment request to prevent double-charging during retries.
Why use a two-phase payment pattern (authorise then capture) instead of charging immediately?
order_id - orders are independent, horizontal scaling is straightforwardAt peak times (Friday 7pm, major sporting events), order volume can spike 10×:
| Component | Mitigation | |-----------|-----------| | API Gateway | Multiple instances behind a load balancer, health checks | | Kafka | Multi-broker cluster with replication factor 3 | | Redis | Redis Sentinel for automatic failover | | Payment provider | Secondary provider with automatic fallback |
You're scaling the Order Service. Which sharding key provides the BEST write distribution?