Food Delivery Interview Questions | JiQuest

add

#

Food Delivery

System design deep dive · HLD

Design a Food Delivery Platform (DoorDash/Swiggy-like): full high-level design.

Requirements, back-of-envelope capacity estimation, a high-level design diagram, a deeper deployment architecture diagram, sequence diagrams for order placement and delivery-partner dispatch with live tracking, and an entity-relationship diagram for the data model - with the reasoning an interviewer expects behind every box and arrow.

5MOrders / day
4sPartner location refresh
3xPeak-hour order spike
Customer appplace order Order servicerestaurant confirms Dispatch svcfinds nearest partner Partner assignedlive GPS tracking Geo indexRedis geoset ETA pushed to customer

1. Clarify requirements before drawing any box

Food delivery has three actors with conflicting latency needs at once - a customer waiting on an ETA, a restaurant that needs the order the instant it's placed, and a delivery partner whose location must stream in near-real time. Scope has to separate those explicitly.

Functional requirements

Browse & orderSearch nearby restaurants, view menu with live item availability, place an order with items and modifiers.
Assign a delivery partnerAutomatically match the order to a nearby available partner once the restaurant accepts it.
Live trackingCustomer sees the partner's live location and an updating ETA from pickup to drop-off.
Order status updatesPlaced → accepted → preparing → picked up → delivered, each pushed to customer and restaurant.

Non-functional requirements

Low-latency geo search"Restaurants near me" must return in well under a second even with hundreds of thousands of active restaurants.
Near-real-time locationPartner location updates every 3-5 seconds must reach the customer's map with minimal lag, without hammering a relational DB.
Strong consistency for order/paymentAn order must not be double-placed or double-charged; eventual consistency is fine for location, not for money.
Elastic peak scalingLunch and dinner rushes create 3x average load in predictable daily windows.
Explicitly out of scope Restaurant-side POS integration, dynamic surge pricing algorithms, and driver-side route optimization/multi-order batching are named as extensions in the deep-dive section so the core order-to-delivery loop stays the focus.

2. Back-of-the-envelope capacity estimation

These numbers decide whether restaurant search can be served from cache, how big the geospatial index for delivery partners needs to be, and why location updates cannot be synchronous database writes.

MetricAssumptionResulting estimate
Orders placed5 million/day, concentrated in 3-hour lunch/dinner windows~58 orders/sec average, ~170 orders/sec peak (3x during rush)
Active delivery partners500,000 online during peak hoursEach posts location every 4s → ~125,000 location writes/sec at peak
Restaurant searches50 searches per order placed (browsing before deciding)~8,500 searches/sec at peak - read-heavy, cacheable per geohash cell
Storage per order~2 KB (items, modifiers, addresses, status history)5M/day × 2 years × 2KB ≈ 7.3 TB
Location dataNot persisted per-ping; only latest position matters~500K partners × ~150 bytes in an in-memory geo index ≈ 75 MB - trivially cached
Why this matters The 125,000 writes/sec figure for partner locations is the single number that rules out writing every GPS ping straight to the primary order database - it has to land in an in-memory geospatial store instead, with the relational database only ever holding durable order/payment state.

3. High-level design (HLD)

The HLD names the major components and the one-directional data flow between them, without yet committing to region layout, replica counts, or the dispatch algorithm's internals - that belongs in the architecture diagram in the next section.

Customer apporder + tracking Partner appGPS ping every 4s API gateway+ WS gateway Order serviceplace / status FSM Dispatch servicematch partner → order Location serviceingest + broadcast Menu / restaurant svc Geo index (Redis)partner lat/lng Order DBsharded, durable Event busorder.status events Push/notify svccustomer + partner
Stateless servicesFast-path infraDurable storageAsync / edge

What each box owns

Order service

Owns the order status state machine (placed → accepted → preparing → picked up → delivered / cancelled), validates the cart against the live menu and item availability, and is the only service allowed to write to the order database. It publishes every status transition as an event rather than letting downstream services poll it.

Dispatch service

Once a restaurant accepts an order, this service queries the geo index for available partners within a radius, scores candidates (distance, current load, idle time), and assigns the order to one - then keeps re-running if the first candidate doesn't accept within a short timeout window. It is deliberately decoupled from the order service so dispatch logic can be tuned independently.

Location service + geo index

Ingests a GPS ping from every online partner every 3-5 seconds and writes only the latest position into an in-memory geospatial store (Redis GEOADD/GEOSEARCH), never into the durable order database. It also fans out position updates to any customer currently tracking a partner assigned to their order, over a WebSocket/long-lived push connection.

Menu/restaurant service, event bus, and push notifications

The menu service is the source of truth for what's orderable right now (a restaurant can 86 an item mid-shift) and is read-heavy, so it sits behind a cache. The event bus decouples the order service from every consumer of its status changes - push notifications, analytics, partner payout accounting - so a slow consumer never blocks the order flow.

4. Detailed architecture diagram

The architecture diagram takes every HLD box and answers "how is this actually deployed?" - in particular, how the system survives a 3x lunch-rush spike and keeps live tracking smooth without overloading the durable database.

Edge layer GeoDNS / LB API gateway WebSocket gatewaysticky per connection Autoscalerscheduled pre-scale for rush hours City cell: metro-A (geo-partitioned) Order svc ×8 pods Dispatch svc ×10 pods Geo index shard (metro-A) Menu cache (Redis) City cell: metro-B (independent capacity) Order svc ×5 pods Dispatch svc ×6 pods Geo index shard (metro-B)GEOSEARCH radius queryTTL-expired if no ping in 30s Order storage tier Shard: metro-A Shard: metro-B sharded by city/region, each with read replicas Async status pipeline Kafka topic Notify workers push to customer + partner apps Analytics / ETA model ETA prediction service traffic + historical prep-time features
DecisionChoiceReasoning
Geographic partitioningIndependent capacity per city/metro cellDemand is inherently local (a rush in one city doesn't affect another), so scaling per-city avoids over-provisioning globally and contains a hot-city incident.
Location storageIn-memory geospatial index (Redis GEO), not the order DB~125,000 writes/sec of ephemeral position data would overwhelm a durable relational store; only the latest position ever matters, so TTL-expiring keys are sufficient.
Order DB sharding keyHash of city/region idAlmost every query ("this restaurant's open orders", "this customer's order history") is naturally scoped to one city, avoiding cross-shard joins.
Status notificationsAsync via Kafka + push workersThe order service's write path must not block on delivering a push notification to a possibly-offline device.

5. Sequence diagrams for the two critical flows

A sequence diagram is where an interviewer checks whether the design actually separates "the restaurant must confirm before we dispatch" from "the partner's location is a separate, high-frequency stream."

5.1 Place an order against a restaurant's menu

Customer Order svc Menu cache Restaurant Dispatch 1. POST /orders {items, restaurant_id} 2. validate items in stock 3. all available 4. push new order (status: placed) 5. accept, prep_time=18min 6. request partner assignment 7. 201 Created {status: accepted, eta}

Step 4-5 is synchronous with a short timeout: if the restaurant doesn't respond in time, the order auto-cancels rather than leaving the customer waiting indefinitely. Dispatch (step 6) is only triggered after acceptance, not at order placement, because assigning a partner before the restaurant confirms would waste the partner's time on an order that might get rejected.

5.2 Delivery-partner assignment and live tracking

Dispatch svc Geo index Partner app Location svc Customer 1. GEOSEARCH near restaurant, 3km 2. ranked candidates 3. offer order (10s to accept) 4. accepted 5. GPS ping every 4s 6. GEOADD (overwrite latest position) 7. push location + ETA steps 5-7 repeat continuously until delivery

Step 3 is drawn as a single offer for clarity, but the real flow retries against the next-ranked candidate if the first partner doesn't accept within the timeout window. Steps 5-7 run on an entirely separate, high-frequency loop from the order status updates - location never touches the order database, and a customer's map simply subscribes to whichever partner id is currently assigned to their order.

6. Entity-relationship (ER) diagram and schema

The data model has to answer three questions: how is an order's line-item detail stored without duplicating menu data, how is a delivery partner's current assignment tracked, and why isn't live GPS position a column in any of these tables.

orders PK id BIGINT FK customer_id BIGINT FK restaurant_id BIGINT FK partner_id BIGINT NULL status ENUM total_amount BIGINT delivery_address JSON placed_at TIMESTAMP order_items PK id BIGINT FK order_id BIGINT menu_item_id BIGINT qty, unit_price INT/BIGINT restaurants PK id BIGINT name VARCHAR lat, lng DOUBLE avg_prep_time INT (min) is_open BOOLEAN rating DECIMAL delivery_partners PK id BIGINT name, vehicle VARCHAR status ENUM(idle,on_delivery,offline) rating DECIMAL (live lat/lng lives in Redis, not here) 1N N1 1N one restaurant serves many orders; one order has many line items; one partner delivers many orders over time, but at most one active order at a time

Key modeling decisions

order_items snapshots price at order timeunit_price is copied from the menu at the moment of ordering, so a later menu price change never retroactively alters a past order's total.
partner_id on orders is nullableAn order exists (placed, even accepted) before a partner is assigned; the foreign key is only populated once dispatch succeeds.
Live location is never a columnlat/lng for a delivery partner in motion lives entirely in the Redis geo index; the relational schema only stores the partner's static profile and status enum.
status is a small enum, not free textA constrained state machine (placed/accepted/preparing/picked_up/delivered/cancelled) lets the application enforce valid transitions and lets the DB index efficiently on "orders currently active per restaurant."
Storage choiceUse whenWatch out for
Relational (Postgres/MySQL), sharded by cityOrders and payments need transactional integrity and the query pattern is naturally scoped to one city/region.Cross-city queries (a partner who happens to move between two metro cells) need application-level stitching.
In-memory geospatial (Redis GEO)Location data is high-frequency, ephemeral, and only the latest value matters.Not durable by default - acceptable here since a lost position is simply overwritten by the next ping seconds later.

7. Deep dives interviewers actually probe

How does the dispatch algorithm pick a delivery partner - nearest available, or something smarter?

Pure nearest-partner assignment is simple but greedy - it can leave a partner who just finished a delivery two blocks away idle while a farther one gets sent, and it ignores batching opportunities (one partner picking up two nearby orders from the same restaurant cluster). A practical middle ground scores candidates on a weighted combination of distance, current idle time, and whether the order could be batched with one they're already carrying, re-evaluated on a short interval rather than solved as a one-shot global optimization, which would be too slow to compute per order at this scale.

Why isn't every GPS ping written to the durable order database?

At 500,000 concurrent partners pinging every 4 seconds, that's ~125,000 writes/sec of data where only the single latest value is ever useful - the history of exactly where a partner was 30 seconds ago has no product value. Writing that volume to a relational store would require sharding and scaling infrastructure sized for a workload that's fundamentally disposable; an in-memory geospatial store with a short TTL (so a partner who goes offline silently disappears from search results) is both cheaper and a better fit for the access pattern.

What happens if the restaurant runs out of an item after the order is placed but before it's prepared?

The menu check at order time (step 2 of the placement sequence) reduces but doesn't eliminate this race, since preparation can reveal a stock-out the system didn't know about. The design handles this as a first-class order-service transition - a "partial fulfillment" or "item substitution requested" state that requires a customer decision (accept substitute / remove item / cancel whole order) rather than silently failing, with the restaurant able to trigger it via their partner dashboard at any point before pickup.

How do you keep live tracking smooth without every customer polling the location service?

Customers hold a long-lived WebSocket (or server-sent-events) connection through the WebSocket gateway, and the location service pushes an update only when the assigned partner's position actually changes meaningfully - it never asks the client to poll. Because each customer only cares about exactly one partner's position at a time, the fan-out is naturally narrow (one publisher, one or two subscribers per partner), unlike a broadcast/chat system where fan-out to many subscribers is the hard problem.

What's the single biggest bottleneck during a 3x lunch-rush spike?

Not the geo index or location ingestion, both of which scale horizontally by sharding per city. The real pressure point is the dispatch service's matching loop when supply (idle partners) is temporarily outstripped by demand (orders needing assignment) in one hot neighborhood - the system needs graceful degradation (wider search radius, slightly worse ETAs, surge-adjusted partner incentives) rather than a hard failure, and that logic has to be tested under simulated load, not just designed on paper.

8. Summary: what a strong answer covers

Separated order status from live locationGeo-partitioned by city, not globally shardedIn-memory geospatial index for ephemeral GPS data Dispatch decoupled from order serviceAsync status notificationsExplicit handling of mid-prep stock-outs
Interview tip When asked to design a food delivery platform, the strongest signal is recognizing that "location" and "order state" have completely different consistency and durability requirements, and architecting two separate data paths for them instead of forcing both through one database.
No comments
Leave a Comment