- Published on
- Views
Design Food Delivery Order Workflow System Design Interview Guide
- Authors

- Name
- Javed Shaikh
← System Design Interview Preparation
This guide walks through Design Food Delivery / Order Workflow 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 food delivery app is an order state machine with three humans in the loop: customer, restaurant, and delivery partner.
Example:
- You pick a restaurant, add a biryani to the cart, pay
- The restaurant accepts and cooks
- A rider is assigned
- You watch status: preparing → picked up → arriving
- If the restaurant is out of chicken, you get a cancel and a refund
This is closer to e-commerce checkout plus live assignment, not a simple CRUD form. Cart and catalog are the front door. The workflow after place order is what interviewers want.
2. Functional Requirements / FR
| Requirement | What it means |
|---|---|
| Browse restaurants | List nearby places, menu, hours, availability. |
| Cart | Add/remove items, quantities, restaurant lock (one restaurant per cart). |
| Place order | Snapshot prices, create order, start workflow. |
| Payment | Charge (or pre-auth) before the restaurant cooks. |
| Restaurant accept / prepare | Dashboard: accept, reject, mark prepared. |
| Delivery assignment | Find a nearby available partner. |
| Status tracking | Customer sees a live timeline. |
| Notifications | Push/SMS on accept, pickup, delay. |
| Menu availability | 86 an item so nobody orders it. |
| Cancel / refund | Rules depend on how far the order has gone. |
Out of scope: full rider GPS platform (point to ride booking), restaurant accounting, ads.
3. Non-Functional Requirements / NFR
| Requirement | Why it matters |
|---|---|
| Idempotent place-order | Double tap should not cook two biryanis. |
| Status freshness | A 10-minute silent order feels stolen. |
| Peak lunch/dinner | QPS is spiky. |
| Consistency on stock | Oversell of a limited thali causes cancel storms. |
| Money correctness | Refunds must match captures. |
| Audit trail | Who rejected, who canceled. |
Interview line: the order row is the source of truth. Everyone else reacts to events.
4. Back-of-the-Envelope Calculation
Traffic assumptions
- 5 million daily active customers
- 10% place an order → 500,000 orders/day
- Browse is heavier: 20 restaurant/menu views per user
- Read/write ratio on catalog is high; order writes are the critical path
QPS
Browse:
5,000,000 × 20 = 100,000,000 views/day
100,000,000 / 86,400 ≈ 1,160 QPS
If peak is 5× (8pm), design for around 6,000 QPS of catalog reads. Cache menus.
Orders:
500,000 / 86,400 ≈ 6 QPS average
Peak 5× ≈ 30 QPS of place-order. Still small. Each order then generates restaurant poll, assignment, and status updates. Multiply by ~20 events → ~600 QPS of workflow writes at peak. The scary part is not raw order QPS. It is fan-out, payments, and support when restaurants reject.
Storage
Order + items ~ 5 KB:
500,000 × 5 KB ≈ 2.5 GB/day
Easy. Keep events for 90 days.
Menu catalog: 100,000 restaurants × 50 items × 500 bytes ≈ 2.5 GB. Cache it.
Cache / memory
Hot menus + restaurant open/closed flags. 100,000 hot restaurants × 20 KB menu ≈ 2 GB. Redis is enough. See distributed cache.
Active order tracking: 50,000 in-flight × 2 KB ≈ 100 MB.
Server estimate
Catalog at 6,000 QPS / 2,000 ≈ 3–5 read servers plus cache.
Order service: a few Java instances, but do not share the place-order database with heavy analytics.
Assignment workers: tens of consumers at peak, not thousands.
These are interview estimates, not exact production numbers.
5. APIs
Browse
GET /api/restaurants?lat=&lng=
GET /api/restaurants/:id/menu
Cart
PUT /api/cart
{
"restaurantId": "r_12",
"items": [{ "itemId": "i_9", "qty": 2 }]
}
Reject mixing two restaurants.
Place order
POST /api/orders
Header: Idempotency-Key: uuid
{ "cartId": "c_1", "paymentMethodId": "pm_4" }
Response: orderId, status: paid_pending_accept (or similar).
Restaurant
POST /api/merchant/orders/:orderId/accept
POST /api/merchant/orders/:orderId/reject
POST /api/merchant/orders/:orderId/ready
Delivery partner
POST /api/delivery/assignments/:id/accept
Customer tracking
GET /api/orders/:orderId
WebSocket optional for live status.
Cancel
POST /api/orders/:orderId/cancel
Errors: 409 restaurant closed, item 86'd, already accepted; 402 payment; 429.
6. Data Model
restaurants / menu_items
Hours, location, available flag per item. Inventory can be a boolean or a count for limited dishes.
carts
cart_id, user_id, restaurant_id, items JSON, updated_at.
orders
| Field | Notes |
|---|---|
order_id | |
user_id | |
restaurant_id | |
status | See workflow |
item_snapshot | Frozen prices and names |
total_cents | |
payment_id | |
delivery_partner_id | Null until assigned |
idempotency_key | Unique |
version |
order_events
Append-only status history for the timeline UI and support.
assignments
Offers to riders: pending, accepted, expired. Same race pattern as ride matching.
Do not mutate menu prices on an in-flight order. The snapshot is the contract.
7. High-Level Design
Order service is the hub. Everything else is a call or an event.
Food Delivery order workflow architecture
Components:
- Customer → API Gateway → Order Service
- Restaurant Service: menu, hours, accept/reject on a dashboard
- Payment Service: authorize/capture/refund
- Order DB: source of truth
- Queue:
OrderPaid,RestaurantAccepted,ReadyForPickup - Assignment Worker: finds a delivery partner
- Notification Service: customer + restaurant + rider
- Restaurant dashboard is just another client of restaurant/order APIs
Happy path:
- Browse cached menus.
- Place order with idempotency key.
- Payment succeeds. Status
waiting_for_restaurant. Notify kitchen. - Restaurant accepts. Event → assignment worker.
- Rider accepts. Status
assigned. - Ready → picked up → delivered.
- Each hop appends
order_eventsand pushes notifications.
Why a queue? Assignment and SMS should not sit inside the payment transaction. Same idea as Shopify-style event-driven design and message queues.
8. Deep Dives
Cart
Cart is ephemeral. At place-order, copy items into item_snapshot. After that, restaurant price changes must not alter this order.
Payment
Authorize first. Capture when the restaurant accepts (or at place-order if the business wants less drop-off). If the restaurant rejects, refund/void. Idempotency on payment ids. Talk about this like a fintech engineer, but keep the ledger in the payment service.
Restaurant accept / prepare
Timeout: if no accept in 3–5 minutes, auto-cancel and refund. Dashboard uses polling or a push channel. Reject reasons: too busy, out of item.
Delivery partner assignment
Reuse the geo idea from ride booking in a lighter form: nearby idle riders, sequential offer, expire, next rider. Do not block the order thread.
Status tracking
Customer reads orders + order_events. Real-time: pub/sub order:{id}. Restaurant dashboard lists open tickets by restaurant_id.
Inventory / menu availability
menu_items.available = false is enough for most interviews. For a 20-plate special, decrement a counter with a check at place-order. Oversell → restaurant rejects → refund.
Refund / cancellation
| State | Customer cancel |
|---|---|
| Waiting for restaurant | Full refund |
| Preparing | Maybe fees; policy |
| Picked up | Usually no full refund |
| Delivered | Support only |
Write the policy. Implement as status checks, not as hope.
Notifications
Every public status change emits one event. Dedup so “preparing” is not sent twice. See notification system.
9. Bottlenecks
- Hot restaurant at 8pm (one shop, thousands of carts)
- Menu cache stampede when a restaurant toggles 50 items
- Assignment when riders are scarce
- Payment provider latency on place-order
- Support tools scanning the main order table
Mitigations: per-restaurant rate limits, cache, timeout+refund, async payment webhooks if the provider is slow, replica for support reads.
10. Tradeoffs
| Choice | Upside | Downside |
|---|---|---|
| Capture pay before accept | Restaurants trust the order | More refunds |
| Capture after accept | Fewer refunds | Fake orders, restaurant risk |
| Strict item inventory | Fewer 86s | Extra complexity |
| Boolean availability | Simple | Oversell |
| Chatty WebSockets | Live UI | Connection cost |
Pick: idempotent place-order, pay then accept with timeout, events for assignment and notify, snapshot cart.
11. Failure Modes
| Failure | Handling |
|---|---|
| Payment succeeds, DB write fails | Idempotency key + payment id reconciliation job |
| Restaurant never accepts | Timer cancels + refund |
| Assignment worker down | Order stays ready_for_assignment. Catch up from queue |
| Rider cancels | New assignment, customer notified |
| Duplicate place-order tap | Same Idempotency-Key returns the same orderId |
| Notification down | Status API still shows truth |
Money: fail toward not cooking unpaid food, and refund if you cannot fulfill.
12. Interview Answer in 10 Minutes
"I would design food delivery as an order workflow, not as a single table update.
Assume 5 million DAU and 500,000 orders a day. Browse is about 100 million views, around 1,160 QPS, about 6,000 QPS at 5× peak — so cache menus. Place-order is only about 6 QPS average and 30 at peak. Storage is a few gigabytes a day. The design work is state, payments, and assignment.
Cart lives until checkout. Place-order uses an idempotency key, snapshots line items, and calls payment. On success the order waits for the restaurant. A timeout refunds. On accept, an event goes to a queue and an assignment worker offers nearby riders.
Restaurant dashboard, customer tracking, and notifications all read the same order status and event log. Inventory is at least an availability flag.
If the worker is down, assignment lags but the order is not lost. If the customer double-taps pay, they still get one order."
Practice this until it is under 10 minutes.
13. Interview Talking Points
- State machine + event log.
- Idempotent checkout.
- Snapshot prices.
- Async assignment.
- Timeouts that refund.
- Notifications are not source of truth.
- Metrics: accept time, reject rate, assignment time, on-time delivery, refund rate, payment failures.
- Classic Java / microservices workflow with Kafka.
14. Follow-up Questions
What if 2,000 people order the same restaurant?
Queue in the kitchen, longer ETA, pause the restaurant, or cap concurrent orders.
How is this different from Uber?
Food has a prepare delay before pickup. Matching too early makes riders wait at the door. Match when the meal is nearly ready.
Catalog search?
Separate from this workflow. Autocomplete is another question: search autocomplete.
Exactly-once assignment?
At-least-once offers + unique accepted partner on the order row (CAS).
Spring implementation?
Order service, outbox to Kafka, consumers for assignment and notify. See Pulse-Guard Kafka for idempotent consumers and scaling event-driven Java.
15. Internal Links
Related JavaThoughts reading:
- System Design Interview Preparation
- Design Ride Booking like Uber
- Design a Notification System
- Design Search Autocomplete
- E-commerce platform system design
- Java
- Spring Boot
- Kafka
- Microservices
- Distributed Systems
- API Gateway
- Event-driven architecture
- 20 system design concepts
Next in this series: Design a Payment System.
