Food Delivery (DoorDash / Swiggy / Uber Eats)#
Problem statement (interviewer prompt)
Design DoorDash / Swiggy / Uber Eats: customers order from local restaurants, the system matches a nearby driver, the driver picks up + delivers, and everyone sees a live ETA. Cover restaurant search, dispatch / matching, live tracking, and surge pricing.
flowchart LR
U([Customer])
R[Restaurant App]
D([Driver App])
ORD[Order Service]
DISP[Dispatch / Matching]
GEO[Geo Index]
PAY[Payment]
U --> ORD --> PAY
ORD --> R
ORD --> DISP --> D
D --> GEO
DISP --> GEO
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class U,D client;
class R,ORD,DISP,GEO,PAY service;
flowchart TB
subgraph Apps[Apps]
CUST([Customer])
REST[Restaurant]
DRV([Driver])
end
subgraph Edge
CDN
GW
WS[WebSocket gateway]
end
subgraph Discover[Discovery]
MENU[Menu / Catalog]
SRCH[Search]
REC([Recommend])
SLA[Promised ETA estimator]
GEOS[Geo serviceability]
end
subgraph Order
ORDSVC[Order Service]
ODB[(Orders)]
SAGA([Order saga])
end
subgraph Restaurant_Side[Restaurant Side]
TAB[Tablet / KDS]
PREP[Prep time tracker]
end
subgraph Dispatch[Dispatch / Matching]
DSVC[Dispatch Service]
GIDX[(Geo index<br/>geohash / S2)]
DRVPRE([Driver presence])
SCORE[Pickup score<br/>distance + cooking time + ratings]
ASSIGN[Assignment]
BATCH[Batching multi-orders]
end
subgraph Live[Live tracking]
LOC([Location ingest<br/>500ms-3s])
ETA[Live ETA]
GEOFENCE[Geofence events]
end
subgraph Pay
PAYSVC[Payment Service]
GTW((Payment provider))
PROMO[Coupons]
WAL[Wallet]
end
subgraph Notif
PUSH[Push]
SMS[SMS]
INAPP[In-app]
end
subgraph Pricing[Pricing / Surge]
BASE[Base fare]
SURGE[Surge / demand-supply]
BG[Background pricing]
end
CUST --> CDN --> GW
GW --> Discover
GW --> Order
Order --> Restaurant_Side
Order --> Dispatch
Dispatch --> Live
Live --- DRV
DRV --> WS --> Live
Order --> Pay
Order --> Notif
Pricing --- Order
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class CUST,DRV,DRVPRE client;
class WS edge;
class REST,MENU,SRCH,SLA,GEOS,ORDSVC,TAB,PREP,DSVC,SCORE,ASSIGN,BATCH,ETA,GEOFENCE,PAYSVC,PROMO,WAL,PUSH,SMS,INAPP,BASE,SURGE,BG service;
class ODB,GIDX datastore;
class REC,SAGA,LOC compute;
class GTW external;
Dispatch logic#
- New order → query geo index for online drivers near restaurant.
- Score candidates: distance, prep time, driver rating, idle time, batch potential.
- Assign top candidate; if reject, fall through next.
- Multi-order batch when along the same route (5-15 min detour OK).
Live location plane#
- Driver app sends location every 1-3 s (faster near pickup/drop).
- Geo index sharded by geohash prefix; updates are write-heavy.
- Customer subscribes to driver's WS channel for live ETA.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
CDN | edge caching for static assets | cdn |
HLD |
Sharding | horizontal partitioning across nodes | database-sharding |
HLD |
LSM vs B-Tree engines | WAL, memtable, SSTables, compaction | storage-engines-lsm-btree |
HLD |
Distributed transactions | 2PC, TCC, sagas, outbox/inbox | distributed-transactions |
HLD |
Realtime protocols | WS / SSE / polling / gRPC streaming | realtime-protocols |
HLD |
Geo indexing | Geohash, Quadtree, S2, H3, R-tree | geo-indexing |
LLD |
REST API design | verbs, statuses, pagination, errors | rest-api-design |
LLD |
Async models | futures / async-await / coroutines / actors | async-models |
Quick reference#
Functional#
- Restaurant search, menu, order.
- Real-time dispatch to drivers; live tracking; ETA.
- Payment, refunds, support.
- Promotions, ratings.
- Restaurant operations (KDS / prep time).
Non-functional#
- Order placement p99 < 1 s.
- Live location updates < 3 s lag.
- City-scale matching < 10 s.
- 99.9% availability for ordering.
Capacity#
- 100M+ orders/month for one big platform.
- Peak hours: thousands of orders/min per city.
- Active drivers: 100k+ per city.
Schema#
orders(id, customer_id, restaurant_id, items[], status, totals, driver_id, ETAs)drivers(id, status, current_loc, vehicle, ratings)presence(driver_id, geohash, ts)Redis geo or KV with TTLrestaurants(id, geo, hours, prep_time)
Trade-offs#
- Centralized matcher simpler but capped by single machine; geo-sharded matcher scales but cross-shard handoffs.
- Batching improves driver utilization but harms ETA reliability.
- Surge pricing balances supply/demand but UX/regulatory issues.
Refs#
- DoorDash, Uber Eats, Swiggy engineering posts on dispatch + ETA models.
- "Uber: Real-time dispatching" KDD papers.
- ByteByteGo "Design DoorDash / Uber Eats".
FAQ#
How does a food delivery app match drivers to orders?#
A dispatch service queries the geo index for nearby drivers, then ranks them by ETA to the restaurant, current load, and route compatibility with adjacent orders for batching.
How is delivery ETA estimated?#
ETA combines restaurant prep time, driver to restaurant travel, food handoff, and driver to customer travel. Each leg is predicted with an ML model trained on historic completion data.
How does the geo index update with thousands of drivers?#
Driver apps push location every 1 to 3 seconds. Updates land in a geohash or H3 indexed store like Redis with TTL, sharded by cell so hot urban cells stay manageable.
How does surge pricing work in food delivery?#
When demand outstrips supply in a region, a multiplier is applied to delivery fees. It raises incentives for drivers to move into the region and gently dampens demand.
How do food delivery apps support order batching?#
The dispatcher looks for orders going from nearby restaurants to nearby drop offs. Batching is bounded by max detour and freshness constraints so customers do not see cold food.
Further reading#
Curated, high-credibility sources for going deeper on this topic.
- ✍️ Blog - DoorDash Engineering - Driver dispatch decisions
- ✍️ Blog - Uber Eats - Trip optimization
- ✍️ Blog - Swiggy - Engineering blog