Food delivery
Restaurants, menus, orders and couriers: an order lifecycle with three parties, and how to keep their views consistent.
On the roadmap:Software Design · step 5: Low-level design: bookings and payments
Lesson 9 of 16 · 11 min read · quiz · exercise
The problem
Customers browse restaurants, build an order from a menu, pay and track delivery. Restaurants accept and prepare orders. Couriers pick up and deliver them.
Entities
Figure SDA-LLD-09-1 · Food delivery classes · © forcsguy.com
OrderItem copies the name and price at purchase time. Later menu edits must not change old orders.
The order lifecycle
Figure SDA-LLD-09-2 · Order status · © forcsguy.com
Each party may trigger only some transitions: the restaurant accepts and marks ready, the courier marks picked up and delivered, the customer may cancel early. Keep the allowed-transition table in one place.
const next: Record<string, Record<string, string>> = {
PLACED: { restaurantAccepts: "ACCEPTED", restaurantRejects: "CANCELLED", customerCancels: "CANCELLED" },
ACCEPTED: { startPreparing: "PREPARING" },
PREPARING: { courierPicksUp: "PICKED_UP" },
PICKED_UP: { courierDelivers: "DELIVERED" },
};
function apply(order: Order, event: string) {
const to = next[order.status]?.[event];
if (!to) throw new Error(`${event} not allowed in ${order.status}`);
order.status = to; order.notifyAll(); // observers: customer, restaurant, courier apps
}
Assigning a courier
Figure SDA-LLD-09-3 · Courier assignment · © forcsguy.com
Matching (nearest, fastest, fairest) is a strategy, as in the cab-booking lesson. Batching two orders for one courier is an extension with its own route check.
Design notes
- Price calculation (items, taxes, fees, discount) lives in one service used by cart, checkout and invoice.
- Menu availability: items can sell out; check at add-to-cart and again at placement.
- Idempotent placement: a double tap must not create two orders; use a client-generated order key.
- Notifications use the Observer pattern on status changes.
- Failures: payment fails (do not place), restaurant unresponsive (timeout and refund), courier cancels (reassign).
Real-world reading
Related reading
- Marc Brooker's BlogMarc Brooker · brooker.co.zaBlogs
- PagerDuty BlogPagerDuty engineers · PagerDutyBlogs
- Chaos Monkey (source)Netflix · GitHubGitHub
- AWS Architecture BlogAWS architects · Amazon Web ServicesBlogs
Show 6 moreShow fewer
- Workload isolation using shuffle shardingAmazon Builders' Library · Amazon Web ServicesBlogs
- Scaling Live Videos to a Billion Users at Facebook - Sachin Kulkarni, Director of Engineering at FacebookFacebookYouTube
- Delivery Hero BlogDelivery Hero engineers · Delivery HeroBlogs
- Neural Architecture Search (NAS) for Prohibited Item Detection at MercariMercari engineers · MercariCase Studies
- Kotlin Engineering BlogKotlin engineers · KotlinBlogs
- Groww BlogGroww engineers · GrowwBlogs
Check your understanding
Question 1 of 3
0 correct so far
Why is the order state machine shared by three parties?
Practise it: URL shortener
Everything below is already set up for this lesson: change the design, run the traffic, and see whether you meet the goal.
- 1The task
- 2Build and test
Read-heavy, a few links are very popular.
Add or remove parts, move traffic up and down, and break something to see what gives way first.
Try it yourself
Add scheduled orders (for 7pm tomorrow) and promo codes. Where do they plug in?
Click what a strong design here would need:
More in Design practice
© 2026 forcsguy.com. Original lesson text and diagrams. Books and sites linked above belong to their authors.