Design a Food-Delivery-Like System

Problem Design a food ordering and delivery platform: customers browse restaurants, place an order, pay, and track the delivery in real time while a courier is assigned and routed.

Functional requirements

  • Browse/search restaurants by location, cuisine, rating; view menus with live item availability.
  • Place an order with items, address and payment; receive confirmation and an ETA.
  • Restaurant accepts/rejects and progresses order state (preparing → ready).
  • Assign a nearby delivery partner and track their live location on a map.
  • Notify customer, restaurant and courier at each state transition.
  • Handle cancellations and refunds.

Non-functional requirements

  • ~20M DAU, ~2M orders/day with a sharp dinner peak: ~60% of orders land in a 3-hour window, giving ~1,500 orders/sec at peak.
  • Browse/search traffic is ~50x order traffic: ~75k QPS read at peak.
  • ~300k active couriers emitting a GPS ping every 4 s ≈ 75k location writes/sec.
  • Order-placement p99 < 500 ms; live-tracking location freshness < 5 s.
  • Payment and order state must be exactly-once and durable; browse can tolerate a few seconds of staleness.

Key components

  • Catalog/Search service: restaurant + menu data, geo-index (geohash/S2 cells) over restaurant locations, heavily cached (Redis + CDN for menu images).
  • Order service: owns the order lifecycle state machine (Placed → Confirmed → Preparing → Out for Delivery → Delivered / Cancelled), persisted in a sharded transactional store keyed by order_id.
  • Delivery-assignment service: consumes ready-for-pickup events, queries the courier geo-index, scores candidates, and offers the job.
  • Location service: high-write ingest of courier pings into an in-memory geo store; fan-out to tracking clients over WebSocket/SSE.
  • Payment service: talks to the PSP, idempotency keys per order, saga-style compensation for refunds.
  • Notification service and a message queue (Kafka) carrying order-state events between all of the above.
  • Core entities: Customer, Restaurant, MenuItem, Order, OrderItem, DeliveryPartner, Assignment.

Deep dives / trade-offs

  • Courier assignment: greedy nearest-courier is simple but yields poor global utilization and starves distant orders. Batched assignment over a short window (a few seconds) using a bipartite matching / Hungarian-style solver improves throughput at the cost of latency and complexity. Discuss batching couriers across multiple orders from the same restaurant.
  • Order state machine: enforce legal transitions server-side, make every transition idempotent (retried webhooks from restaurants and PSPs are the norm), and persist transitions as events so tracking and analytics can replay them.
  • Location ingest at 75k writes/sec: do not write every ping to the primary DB. Keep the hot location in Redis with TTL, sample to durable storage for post-hoc route analysis, and push to the customer over a persistent connection rather than polling.
  • Consistency: payment authorization, order creation and inventory/availability decrement span services — walk through the saga and what happens when the courier is assigned but the payment later fails.
  • Hot restaurants: a single popular restaurant on a Friday night is a natural hotspot for both the menu cache and the order shard.

What's evaluated Structured decomposition into services, a clearly stated order lifecycle, and concrete pseudocode for the critical paths (placeOrder, assignDeliveryPartner) rather than prose hand-waving.

asked …
LeaderboardSalaryAccount