Interview Setup
Interview Prompt
Design a food delivery platform like DoorDash or Zomato. Customers browse restaurants, place orders, restaurants prepare food, and dashers pick up and deliver. Support real-time order tracking and intelligent dasher dispatch.
Clarifying Questions (ask before designing)
| Question | Why it matters |
|---|---|
| Single city or multi-city? Peak orders/sec and active dashers? | Handling 30M orders per day alongside 125K location updates per second drives Kafka streaming ingestion and specialized geo-index architecture rather than standard PostgreSQL lat/lng table queries. |
| Can one dasher carry multiple orders (batching)? | Food delivery's primary efficiency advantage over ride-sharing centers on same-restaurant batching, which fundamentally alters the dispatch optimization algorithm. |
| When is payment captured, upon order placement or upon delivery? | Authorizing at checkout and capturing upon delivery confirmation is the production standard, directly impacting distributed saga compensation and restaurant payout settlement. |
| How fresh must dasher location be for customer tracking? | Ingesting 4-second GPS updates across 500K active dashers generates 125K writes per second, which dictates WebSocket fan-out architecture and Cassandra delivery trail persistence. |
Scope
In scope
- Three-sided marketplace (user, restaurant, driver)
- Order dispatch
- Real-time tracking
- Kitchen prep time estimation
- Batching orders
Out of scope (state explicitly)
- Full payment gateway processing and settlement (Payment Gateway)
- Turn-by-turn map rendering and navigation tile generation (Map Rendering and Navigation)
- Driver and rider identity verification and background checks
Functional Requirements
Start by asking your interviewer about customer ordering, restaurant management, delivery assignment, and real-time tracking. Payments and ratings are follow-ups once the order lifecycle is clear.
- Customer: Browse restaurants, view menus, place orders, and track delivery in real-time.
- Restaurant: Manage menu items, accept or reject incoming orders, and update preparation status from preparing to ready.
- Delivery Partner (Dasher): Toggle online availability, accept delivery offers, and navigate to pickup and dropoff destinations.
- Order Lifecycle: Smooth transitions from browsing and cart creation through checkout, restaurant acceptance, food preparation, courier pickup, and final delivery.
- Search & Discovery: Discover restaurants filtered by venue name, cuisine category, rating, and geographic radius.
- Ratings and Reviews: Submit ratings and feedback for both the restaurant and dasher following delivery completion.
- Payment & Settlement: Authorize payment at checkout, handle customer tips, process restaurant food payouts, and credit dasher earnings.
- Promotions: Apply promotional vouchers, percent-off discounts, and free delivery perks at checkout.
- ETA Calculation: Compute accurate delivery arrival estimates combining kitchen cooking, pickup logistics, and travel time.
Non-Functional Requirements
Your interviewer will care most about order-to-delivery time and dispatcher assignment latency on this problem. Peak lunch and dinner rushes are when the architecture gets stress-tested.
- High Availability: 99.99% uptime, maintaining resilience during lunch and dinner meal rushes.
- Low Latency: Search responses return under 200 ms and order placement completes in under 1 second.
- Real-time Tracking: Courier GPS coordinates target 4-second updates during normal movement for active orders, with adaptive frequency based on courier state.
- Scalability: Support 30M+ orders per day across 1M+ active restaurants and 500K concurrent couriers.
- Data Consistency: Strong consistency for order state transitions and financial transactions to eliminate double charges and dropped orders.
- Fault Tolerance: Active orders must survive any individual component failure without data corruption or lost deliveries.
Capacity Estimations
Run this math before you size the dispatcher. Orders per hour and concurrent active deliveries tell you how many dasher location updates you need to ingest per second.
| Metric | Calculation | Value |
|---|---|---|
| Orders / day | Given | 30M |
| Orders / sec | 30M ÷ 86400 (+ peak factor) | ~350 (peak 1,500 during dinner) |
| Restaurants | Given | 1M |
| Active dashers at any time | Given | 500K |
| Dasher location updates / sec | 500K active dashers ÷ 4s active-delivery baseline | 125K |
| Menu items total | Given | 50M |
| Search QPS | Given | 50K |
Architecture Diagram
In the room: walk the order state machine early, highlighting the transitions between placed, accepted, preparing, picked up, and delivered states.
Walk your interviewer through the diagram by order lifecycle. Customers browse cached restaurant menus and place orders that flow to restaurants for kitchen acceptance. Once accepted, the dispatch service matches a nearby dasher using spatial lookups, while both customer and restaurant receive real-time status updates via WebSockets. Payments and customer reviews execute asynchronously after delivery confirmation.
Component Deep Dives
Restaurant & Menu Service
Manages restaurant metadata, multi-tier menu hierarchies, and catalog search indexing for high-throughput browsing. This is the read-heavy catalog pipeline, separate from order fulfillment, dispatch optimization, and real-time courier tracking.
- Menu Hierarchy: Restaurants organize menus into categories, containing individual menu items that support customizable modifiers and add-ons.
- Dynamic Menus: Items update availability dynamically based on kitchen inventory, operating hours, and depleted ingredients.
- Search & Discovery: Elasticsearch indexes restaurant metadata, cuisine classifications, menu items, and dietary tags for low-latency full-text discovery. Menu mutations emit catalog events through the transactional outbox so search indexes can be updated asynchronously without coupling checkout to indexing.
- Personalized Feeds: Recommendation engines highlight popular venues nearby and suggest dishes based on user re-order history.
- Ratings & Reviews: Post-delivery ratings and review submissions are persisted separately and update restaurant and dasher aggregates asynchronously.
Order Service: The Core State Machine
Checkout orchestrates cart validation, payment authorization, and fulfillment dispatch across distributed services.
- Idempotent State Transitions: Each state transition checks the expected order version and writes the new state with its outbox event atomically. A distinct event identifier makes retries and replay buffers idempotent.
- Distributed Sagas: Order fulfillment spans multiple distributed domains including cart checkout, payment processing, restaurant kitchen confirmation, and dasher dispatch. We orchestrate this multi-service workflow with a Saga pattern, executing compensating transactions whenever any downstream step fails.
Dispatch / Assignment Service: Complex Optimization
Matching available couriers to ready orders constitutes the primary operational engine of the platform.
Unlike ride-sharing platforms that match a single passenger with an individual driver in a single hop, food delivery introduces an essential batching opportunity. A single courier can bundle multiple orders originating from the same kitchen or adjacent restaurants along an identical delivery corridor.
Matching Algorithm Execution:
- When an order approaches completion, identify candidate dashers:
- Available idle dashers within the target restaurant search radius.
- Active dashers fulfilling current deliveries whose dropoff trajectory passes adjacent to the pickup restaurant.
- Score each candidate courier using weighted parameters:
- Distance and travel duration to the pickup venue.
- Current concurrent delivery load, strictly capped at two active orders.
- Historical courier dispatch acceptance rate.
- Customer priority tiers such as premium subscription members.
- Batching Optimization: When two orders originate from the same restaurant or venues within 200 meters of each other, evaluate bundling both deliveries to the same courier.
- Assign the highest-scoring candidate and transmit a dispatch offer with a 30-second acceptance timeout.
Proactive Dispatching: Initiate courier search before kitchen preparation finishes. By scheduling the dasher so transit time matches remaining cooking time, the courier arrives at the restaurant precisely as the meal is boxed, shaving critical minutes off customer wait times.
- Predictive Dispatch Model: Evaluates
prep_time_estimate + travel_to_restaurant, dispatching the offer whenprep_time_remaining ≈ travel_to_restaurant.
ETA Service
Delivery ETA combines prep time, pickup transit, and delivery travel, caching route computations to maintain sub-second response times.
Delivery ETA is calculated across four key segments:
total_eta = prep_time + dasher_to_restaurant_time + restaurant_to_customer_time + buffer;- Kitchen Prep Time Estimation: An ML regression model predicts kitchen cooking duration using restaurant historical throughput, day of week, time of day, order item count, and active kitchen queue depth.
- Travel Time Computation: A routing engine such as OSRM or Google Maps computes transit duration factoring in real-time road traffic conditions.
- Logistical Buffer: A 5-minute buffer accounts for parking access, building check-in, and courier handoff logistics.
Payment Service
Idempotency keys prevent duplicate credit card charges and redundant payouts during network retries.
- Payment Authorization and Capture: Authorize the card payment upon order placement, then capture funds only after successful delivery confirmation.
- Multi-Party Split Settlement: Customer charges are split atomically, where the platform retains its marketplace commission, the restaurant receives food reimbursement, and the courier collects delivery earnings plus tips. Explore full settlement architectures in our Payment Gateway design.
- Automated Refunds: Missing items, delayed deliveries, or food quality defects trigger automated refund rules and escalation to manual customer support review.
Dasher Service (Location + Geospatial Index)
Spatial indexing enables sub-millisecond radius lookups over half a million active courier coordinates.
- GPS Telemetry Ingestion: The mobile dasher application streams GPS telemetry every 4 seconds over persistent WebSocket connections to the Dasher Service, updating the spatial index.
- Geospatial Indexing: Redis geospatial indexes store coordinates using
GEOADD dashers:available:{city} {lng} {lat} {dasher_id}to track available dashers and executeGEORADIUSlookups during dispatch. - Courier State Caching: Redis hashes maintain operational state under
dasher:{id}with fields for status, active order assignments, current coordinates, and update timestamps. - Kafka Telemetry Publishing: Every location update publishes to the Kafka
dasher-locationstopic, where streaming workers consume telemetry for tracking and analytics. - Availability Management: Transitioning online or offline adjusts the availability index, while dispatch acceptance updates status to
picking_up, removing the driver from the unassigned pool.
Real-Time Tracking Service
Customers and couriers both require real-time location visibility, where WebSocket push delivers fresh map updates without API polling.
- Operational Purpose: Stream live vehicle coordinates directly to customer map views during active deliveries.
- Stream Processing Pipeline:
- Consume courier location events from the Kafka
dasher-locationstopic. - Filter telemetry down strictly to couriers fulfilling active orders.
- Resolve active customer WebSocket gateway sessions registered to that order.
- Push coordinate updates to the customer client application every 4 seconds.
- Consume courier location events from the Kafka
- Customer Experience: Real-time map displays the courier's live location, vehicle heading, estimated arrival time, and remaining distance.
- Delivery Trail Storage: The system writes historical vehicle routes to Cassandra in the
delivery_trailtable partitioned by order_id with clustering by timestamp, applying a 30-day TTL for dispute resolution and operational analytics.
Notification Service
Channel adapters isolate provider-specific retry logic and delivery receipt protocols.
Consumes lifecycle events from Kafka and coordinates alerts across customers, restaurants, and couriers, as detailed in our dedicated Notification Service deep dive.
- Multi-Party Alerts:
- Customer: Order confirmation, food preparation updates, courier departure notices, two-minute arrival warnings, and post-delivery review prompts.
- Restaurant: Urgent audio alerts for new order tickets and courier arrival notices for food handoff.
- Dasher: Delivery dispatch opportunities with pickup location, dropoff route, and projected earnings.
- Delivery Channels: Mobile push notifications via APNs and FCM, in-app WebSocket banners, and SMS notifications for users without open apps.
- Priority Routing: New restaurant orders receive critical priority with persistent audio alerts, whereas post-delivery rating requests are scheduled as low-priority silent notifications delayed 30 minutes.
Event Bus Design (Kafka)
The distributed event bus decouples producers from consumers and absorbs massive traffic spikes during lunch and dinner rushes, following patterns in Message Queues.
Topic: order-events
Partitions: 64 # partitioned by order_id to preserve per-order lifecycle ordering
Retention: 7 days # dispute replay and saga compensation
Producers: Order Service via transactional outbox (lifecycle: PLACED, CONFIRMED, PREPARING, READY, PICKED_UP, DELIVERED, CANCELLED)
Consumers: Notification Service, Payment Service (capture on DELIVERED), Dispatch Service
Topic: catalog-events
Partitions: 32 # partitioned by restaurant_id
Retention: 7 days # replay for search-index recovery
Producers: Restaurant & Menu Service via transactional outbox (menu, availability, restaurant metadata changes)
Consumers: Search Indexer, recommendation and cache refresh workers
Topic: dasher-locations
Partitions: 256 # partitioned by dasher_id
Retention: 24h # high-volume GPS telemetry
Producers: Dasher Service (GPS pings every 4s via WebSocket)
Consumers: Real-Time Tracking Service, Dispatch geo-index updater, Flink analytics
Topic: dispatch-requests
Partitions: 32 # partitioned by H3 zone_id for single-writer assignment per zone
Producers: Order Service when orders become READY or proactive dispatch is triggered
Consumers: Zone Dispatch Workers (one authoritative writer per zone)
Topic: dispatch-events
Partitions: 32 # partitioned by H3 zone_id for per-zone assignment ordering
Producers: Dispatch Service (assignment offers, accepts, declines, reassignments)
Consumers: Dasher push workers, Order Service (dasher_id assignment updates)
Topic: payment-events
Partitions: 32 # partitioned by order_id
Producers: Payment Service (auth, capture, refund, restaurant and dasher payouts)
Consumers: Order Service (payment_status), finance accounting ledger
Topic: tracking-events
Partitions: 64 # partitioned by order_id
Producers: Real-Time Tracking Service (Kalman-filtered lat/lng, dynamic ETA updates)
Consumers: Customer WebSocket fan-out, Cassandra delivery_trail writer
Configuration:
Replication factor: 3
min.insync.replicas: 2
Synchronous path: validate the idempotency key, persist the order and outbox event in PostgreSQL, coordinate payment authorization through the order saga, and return HTTP 201 Created after successful placement.
Asynchronous path: the outbox publisher emits committed order events to Kafka, while dasher-locations drives real-time tracking and dispatch-events drives courier matchingAPI Design
Client API Type Definitions
RESTful HTTP endpoints and WebSocket streaming contracts cover restaurant discovery, order placement, merchant acceptance, and real-time courier tracking. These TypeScript domain interfaces define the core request and response shapes:
export interface PlaceOrderRequest {
restaurantId: string;
items: Array<{
itemId: string;
quantity: number;
modifiers?: string[];
}>;
deliveryAddress: {
lat: number;
lng: number;
address: string;
};
paymentMethodId: string;
tipAmount: number;
promoCode?: string;
}
export interface PlaceOrderResponse {
orderId: string;
status: "placed" | "confirmed" | "preparing" | "ready" | "picked_up" | "delivered" | "cancelled";
estimatedDeliveryAt: string;
total: {
subtotal: number;
deliveryFee: number;
tax: number;
tip: number;
discount: number;
total: number;
};
}
export interface RestaurantSearchQuery {
lat: number;
lng: number;
cuisine?: string;
sort?: "rating" | "distance" | "delivery_time";
radiusKm?: number;
}
export interface RealtimeTrackingMessage {
orderId: string;
dasherId: string;
lat: number;
lng: number;
heading: number;
speedKmh: number;
estimatedRemainingMinutes: number;
updatedAt: string;
}Search Restaurants
GET /api/v1/restaurants?lat=37.77&lng=-122.42&cuisine=italian&sort=rating&radiusKm=5Place Order
POST /api/v1/orders
{
"restaurant_id": "rest-uuid",
"items": [
{"item_id": "item-1", "quantity": 2, "modifiers": ["extra cheese"]},
{"item_id": "item-2", "quantity": 1}
],
"delivery_address": {"lat": 37.77, "lng": -122.42, "address": "..."},
"payment_method_id": "pm-uuid",
"tip_amount": 5.00,
"promo_code": "SAVE20"
}
Response: 201 Created
{
"order_id": "order-uuid",
"status": "placed",
"estimated_delivery_at": "2026-03-13T11:15:00Z",
"total": {"subtotal": 28.00, "delivery_fee": 3.99, "tax": 2.52, "tip": 5.00, "discount": -5.60, "total": 33.91}
}Restaurant Accept/Reject
POST /api/v1/orders/{order_id}/accept
Idempotency-Key: accept-uuid
If-Match: 7
{ "estimated_prep_time_minutes": 20 }
POST /api/v1/orders/{order_id}/reject
Idempotency-Key: reject-uuid
If-Match: 7
{ "reason": "too_busy" }Track Order (Real-time)
GET /api/v1/orders/{order_id}/track
WebSocket: /ws/v1/orders/{order_id}/trackThe tracking endpoint authorizes access for the customer, restaurant, or dasher associated with the order before opening the WebSocket stream.
Common Error Responses
400 Bad Request: invalid input, missing required fields, or malformed JSON payload 401 Unauthorized: missing or invalid authentication token or API key 403 Forbidden: authenticated caller lacks required permissions for this resource 404 Not Found: requested resource ID does not exist 409 Conflict: duplicate write or version conflict, retry with a unique idempotency key 422 Unprocessable Entity: syntactically valid request failed semantic business validation 429 Too Many Requests: rate limit quota exceeded, client should honor Retry-After header 500 Internal Error: unexpected server failure, retry safely with an idempotency key 503 Service Unavailable: downstream dependency is unavailable or overloaded, retry with exponential backoff 440 Login Timeout: WebSocket connection session expired, client reconnect is required
Data Model
PostgreSQL: Orders (ACID Required)
Order records require ACID state transitions, while restaurant menus, courier coordinates, and tracking streams use different read and write patterns.
CREATE TABLE orders (
order_id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
restaurant_id UUID NOT NULL,
dasher_id UUID,
status VARCHAR(20),
delivery_address JSONB,
subtotal DECIMAL(10,2),
delivery_fee DECIMAL(10,2),
tax DECIMAL(10,2),
tip DECIMAL(10,2),
discount DECIMAL(10,2),
total DECIMAL(10,2),
promo_code VARCHAR(32),
estimated_prep_min INT,
estimated_delivery_at TIMESTAMP,
actual_delivered_at TIMESTAMP,
placed_at TIMESTAMP,
payment_status VARCHAR(20),
idempotency_key VARCHAR(64) NOT NULL UNIQUE,
version BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_customer ON orders (customer_id, placed_at DESC);
CREATE INDEX idx_restaurant ON orders (restaurant_id, placed_at DESC);
CREATE INDEX idx_dasher ON orders (dasher_id, placed_at DESC);
CREATE TABLE order_items (
order_item_id UUID PRIMARY KEY,
order_id UUID REFERENCES orders,
menu_item_id UUID,
item_name VARCHAR(256),
quantity INT,
unit_price DECIMAL(10,2),
modifiers JSONB,
special_notes TEXT
);CREATE TABLE order_outbox (
event_id UUID PRIMARY KEY,
order_id UUID NOT NULL,
event_type VARCHAR(64) NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMP NOT NULL,
published_at TIMESTAMP
);MySQL: Restaurant & Menu Catalog
CREATE TABLE restaurants (
restaurant_id UUID PRIMARY KEY,
name VARCHAR(256),
cuisine_type VARCHAR(64),
address TEXT,
lat DECIMAL(10,7),
lng DECIMAL(10,7),
rating DECIMAL(2,1),
price_range TINYINT,
avg_prep_time INT,
is_active BOOLEAN,
hours JSON
);
CREATE TABLE menu_items (
item_id UUID PRIMARY KEY,
restaurant_id UUID,
category VARCHAR(64),
name VARCHAR(256),
description TEXT,
price DECIMAL(10,2),
image_url TEXT,
is_available BOOLEAN,
modifiers JSON,
INDEX idx_restaurant (restaurant_id)
);Redis: Cart, Dasher Geo, and Dispatch State
# Shopping cart (session-based)
Key: cart:{customer_id}
Value: Hash { restaurant_id, items: JSON, updated_at }
TTL: 3600
# Restaurant menu cache
Key: restaurant:menu:{restaurant_id}
Value: Cached menu and availability JSON
TTL: 60s
Invalidation: catalog-events updates or invalidates the cache
# Dasher geospatial index
GEOADD dashers:available:{city} {lng} {lat} {dasher_id}
# Active orders per dasher
Key: dasher:orders:{dasher_id}
Value: Set of order_ids (max 2)Kafka Event Bus Schemas
Topic: order-events
Partitions: 64 # partitioned by order_id to preserve per-order lifecycle ordering
Retention: 7 days # dispute replay and saga compensation
Producers: Order Service via transactional outbox (lifecycle: PLACED, CONFIRMED, PREPARING, READY, PICKED_UP, DELIVERED, CANCELLED)
Consumers: Notification Service, Payment Service (capture on DELIVERED), Dispatch Service
Topic: catalog-events
Partitions: 32 # partitioned by restaurant_id
Retention: 7 days # replay for search-index recovery
Producers: Restaurant & Menu Service via transactional outbox (menu, availability, restaurant metadata changes)
Consumers: Search Indexer, recommendation and cache refresh workers
Topic: dasher-locations
Partitions: 256 # partitioned by dasher_id
Retention: 24h # high-volume GPS telemetry
Producers: Dasher Service (GPS pings every 4s via WebSocket)
Consumers: Real-Time Tracking Service, Dispatch geo-index updater, Flink analytics
Topic: dispatch-requests
Partitions: 32 # partitioned by H3 zone_id for single-writer assignment per zone
Producers: Order Service when orders become READY or proactive dispatch is triggered
Consumers: Zone Dispatch Workers (one authoritative writer per zone)
Topic: dispatch-events
Partitions: 32 # partitioned by H3 zone_id for per-zone assignment ordering
Producers: Dispatch Service (assignment offers, accepts, declines, reassignments)
Consumers: Dasher push workers, Order Service (dasher_id assignment updates)
Topic: payment-events
Partitions: 32 # partitioned by order_id
Producers: Payment Service (auth, capture, refund, restaurant and dasher payouts)
Consumers: Order Service (payment_status), finance accounting ledger
Topic: tracking-events
Partitions: 64 # partitioned by order_id
Producers: Real-Time Tracking Service (Kalman-filtered lat/lng, dynamic ETA updates)
Consumers: Customer WebSocket fan-out, Cassandra delivery_trail writer
Configuration:
Replication factor: 3
min.insync.replicas: 2
Synchronous path: validate the idempotency key, persist the order and outbox event in PostgreSQL, coordinate payment authorization through the order saga, and return HTTP 201 Created after successful placement.
Asynchronous path: the outbox publisher emits committed order events to Kafka, while dasher-locations drives real-time tracking and dispatch-events drives courier matchingFault Tolerance
| Concern | Solution |
|---|---|
| Order placed but payment fails | Execute distributed saga compensation by cancelling the order and releasing reserved restaurant capacity. |
| Restaurant tablet offline | Send SMS at 2 minutes, place an automated IVR call at 5 minutes, and auto-cancel at 8 minutes if the order remains unacknowledged. |
| Dasher goes offline mid-delivery | Detect courier loss through heartbeat timeouts and automatically reassign the delivery to an available nearby dasher. |
| Duplicate order submission | Require an idempotency key on order placement endpoints to reject repeated checkouts. |
| Payment double charge | Authorize payment at checkout and capture it idempotently after delivery to prevent duplicate debits. |
| Peak load (dinner rush) | Auto-scale the order service fleet, buffer incoming orders in Kafka, and activate payment gateway circuit breakers. |
Saga Pattern for Order Placement
Courier dropouts, restaurant timeouts, and concurrent assignment collisions require idempotent compensation and automated circuit breaking. The saga coordinates multi-service transactions with compensating actions instead of locking distributed databases.
Forward Transactions: 1. Create Order (Order Service) --> Compensation: Mark Order CANCELLED 2. Authorize Payment (Payment Service) --> Compensation: Release Payment Authorization 3. Notify Restaurant (Restaurant Svc) --> Compensation: Cancel Order at Restaurant 4. Dispatch Dasher (Dispatch Service) --> Compensation: Cancel Courier Dispatch Compensation Execution: If Step 3 fails, the saga orchestrator executes compensations in reverse order: Step 2 (Release Payment Authorization) followed by Step 1 (Mark Order CANCELLED).
Additional Considerations
Dynamic Delivery Fees
Dynamic pricing, scheduled orders, multi-restaurant batching, and fraud detection are advanced production considerations. This section begins with delivery fee calculation based on base rates, distance mileage, and peak surge multipliers:
delivery_fee = base_fee + distance_fee + surge_fee base_fee = 2.99 distance_fee = max(0.0, (distance_miles - 3.0) * 0.50) surge_fee = demand_multiplier * 1.00 # applied during peak hours
Restaurant Capacity Management
Protective kitchen throttling mechanisms to prevent operational overload:
- Restaurants configure maximum concurrent active order thresholds, such as 15 simultaneous tickets.
- When a kitchen reaches capacity, search results flag the restaurant as busy and automatically increase estimated prep times.
- This protective throttling prevents kitchen bottlenecks and delivery delays during peak dinner hours.
Batched Delivery Optimization
Route bundling strategies that improve driver economics and customer delivery efficiency:
- A single courier bundles orders originating from Restaurant A and adjacent Restaurant B, delivering both along a shared route.
- The routing engine solves a traveling salesperson heuristic to minimize total transit distance and fuel consumption.
- Customers experience negligible delivery delays while benefiting from reduced, shared delivery fees.
Fraud Detection & Verification
Multi-layered anomaly detection protecting merchants, drivers, and platform economics:
- Mitigate phantom deliveries where couriers falsely mark orders as delivered without completing handoffs.
- Prevent coordinated customer refund abuse and fraudulent non-delivery claims.
- Enforce multi-modal fraud detection via geofence GPS verification at delivery addresses, mandatory photo dropoff proof, and machine learning anomaly scoring.
Dispatch Matching Algorithm: The Core Challenge
Mathematical modeling of the global assignment optimization problem:
Problem Definition:
Given 100 ready orders and 80 available dashers in a city zone, compute optimal assignments.
Naive Approach:
Assign the nearest courier to each restaurant in isolation.
Flaw: Globally suboptimal. Order A takes Dasher X (closest).
Order B's only viable option was also Dasher X, leaving Order B stranded with excessive delay.
Optimal Approach: Hungarian Algorithm (Bipartite Graph Matching, O(n³))
Construct cost matrix: C[i][j] = cost of assigning dasher j to order i
Cost Formula:
Cost = α * distance_to_restaurant + β * estimated_delivery_time + γ * dasher_utilization
Solve for global minimum total assignment cost.
Computational Complexity:
At 100 orders x 80 dashers: 100³ = 1,000,000 operations, executing in under 10 ms.
Execute in 30-second batch windows rather than per-order dispatch to maximize global efficiency.
Scale-Out Approach: Large Metros (10,000 orders x 5,000 dashers)
The Hungarian algorithm becomes computationally prohibitive at O(n³).
Production Strategy:
1. Greedy approximation with local k-opt neighborhood search refinement.
2. Linear programming (LP) relaxation solved via simplex or interior point methods.
Real-Time Hard Constraints:
Pickup wait constraint: Maximum 5 minutes from READY state to courier pickup.
End-to-end food freshness target: Maximum 30 minutes from preparation completion to customer delivery.
Courier capacity limit: Strict maximum of 2 concurrent active deliveries per dasher.
Courier soft preferences: Route distance preferences factored into scoring weights.Interview Walkthrough
- 25-minute cut
Skip arch50/arch75 depth unless interviewing at staff level.
- Functional, non-functional requirements, and order lifecycle overview (3 min)
- Menu caching strategies for read-heavy browsing (6 min)
- Order state machine orchestration and restaurant timeout fallbacks (7 min)
- Geospatial courier dispatch and concurrency locking (6 min)
- Real-time tracking delivery via WebSockets (3 min)
- Walk through the restaurant timeout sequence: an SMS notification at 2 minutes, an automated IVR phone call at 5 minutes, and final auto-cancellation at 8 minutes accompanied by customer notification and saga compensation.
- At peak, menu cache staleness with a Redis TTL of 60 seconds is acceptable because checkout re-validates item availability and price directly against the primary write database.
- Separate the order lifecycle state machine (transitions across placed, confirmed, picked up, and delivered) from the high-throughput real-time tracking stream.
- Explain restaurant menu and catalog browsing as a read-heavy workload leveraging Caching Patterns and Invalidation in contrast with transactional order writes.
- Cover dispatch architecture: match couriers to orders using geospatial lookups combined with precomputed routing service ETAs.
- Discuss live tracking through WebSocket push of courier GPS rather than polling the HTTP API every second.
- Emphasize idempotent order placement and inventory reservation to prevent double charges during client retries.
- Highlight the common pitfall of coupling menu browsing and checkout in a single monolithic database, where catalog reads starve transactional order writes.
Engineering Trade-offs
Race Conditions in Dispatch
Your interviewer will evaluate your dispatch architecture and concurrency controls. Contrast centralized and distributed dispatch strategies and justify the choice for peak dinner rushes. Hundreds of orders can become ready simultaneously during dinner rushes. Concurrent dispatch workers can then attempt to assign the same optimal courier, so the system needs strict synchronization such as Distributed Locking.
Scenario:
Two dispatch workers evaluate distinct orders concurrently.
Both score Dasher X as the optimal candidate for their respective orders.
Without coordination, both attempt to assign Dasher X, causing a race condition.
Solution 1: Distributed Lock (Redis SETNX)
Before dispatching an offer to Dasher X:
SET dasher:lock:{dasher_id} {order_id} NX EX 30
NX: Atomically set only if the key does not already exist
EX: Set a 30-second TTL to prevent deadlock if the worker crashes
Lock Evaluation:
If SET succeeds: Worker owns the dasher assignment and sends the dispatch offer.
If SET fails: Dasher is already locked, so worker advances to the next candidate.
On dasher acceptance or decline: Worker immediately releases the Redis lock.
Trade-offs:
Advantage: Simple implementation with sub-millisecond Redis latency.
Drawback: False exclusion occurs if a dasher is locked for 30 seconds when an order is declined.
Mitigation: Enforce a 15-second response window with immediate lock release on decline.Solution 2: Optimistic Concurrency Control in Database
UPDATE dashers
SET current_order_count = current_order_count + 1,
version = version + 1
WHERE dasher_id = ? AND current_order_count < 2 AND version = ?
Evaluation:
If rows_affected == 1: Assignment succeeds with atomic concurrency protection.
If rows_affected == 0: Concurrent update has occurred, so retry with the next candidate dasher.
Trade-offs:
Advantage: Eliminates external lock management by leveraging transactional database state.
Drawback: Incurs higher latency due to relational database round-trips.
Solution 3: Centralized Single-Writer Queue per Spatial Zone (Production Standard)
Partition the metropolitan area into spatial zones using Uber H3 resolution-7 hexagons.
Assign exactly one dispatch worker per zone to act as the authoritative single writer.
The worker consumes orders from the Kafka partition mapped to that specific zone.
Because processing is serialized per zone, race conditions are eliminated by design.
Trade-offs:
Advantage: Zero lock contention with natural support for 30-second batch matching algorithms.
Drawback: Orders near zone perimeters might miss optimal couriers positioned across the boundary.
Mitigation: Configure zones with a 1 km overlap, enabling workers to query adjacent zone candidates.
Recommendation:
Adopt Solution 3 for large-scale multi-city deployments (DoorDash production pattern).
Use Solution 1 for earlier stage architectures or simpler single-city setups.Order State Machine Failure Scenarios
Handling real-world operational anomalies across disconnected restaurant tablets, offline couriers, failed payment captures, and mid-prep cancellations requires explicit recovery state machines.
Scenario 1: Restaurant tablet disconnects after order confirmation
Detection: Heartbeat signal absent from restaurant tablet for 2 minutes.
Recovery Sequence:
T+2 min: Dispatch automated SMS notification to restaurant contact number.
T+5 min: Trigger automated phone call with IVR prompt asking the kitchen to confirm.
T+8 min: If no response received, automatically cancel order.
Compensation: Refund customer in full and release courier assignment.
Customer UX: Notify customer of restaurant unavailability and recommend nearby alternatives.
Edge Case: Kitchen prepared food before tablet lost connectivity.
When tablet reconnects, system checks cancellation state.
If food was prepared, platform covers costs under partnership SLA guarantees.
Scenario 2: Dasher mobile application crashes during delivery transit (PICKED_UP state)
Detection: GPS telemetry stream absent for 3 consecutive minutes.
Recovery Sequence:
T+3 min: Dispatch push notification and SMS alert to courier.
T+5 min: Trigger automated phone call to courier device.
T+8 min: If courier remains unreachable, evaluate reassignment.
Handling Challenge:
If courier already picked up the food and went offline, customer receives full refund and remake.
If courier reconnects, resume tracking and publish updated ETA.
Prevention:
Require proof of pickup where courier scans or enters the restaurant verification code.
Scenario 3: Payment capture fails following successful delivery
Flow:
Payment authorized at checkout and captured upon courier delivery confirmation.
Handling:
Retry capture 3 times using exponential backoff.
If all retries fail, route transaction to asynchronous payment recovery queue.
Restaurant and dasher receive full payment while the platform absorbs transient risk.
Enforce idempotency key on payment capture to guarantee customers are never charged twice.
Flag account for mandatory upfront payment capture on subsequent orders.
Scenario 4: Customer requests cancellation during food preparation (PREPARING state)
Cancellation Policy by Lifecycle State:
PLACED (unconfirmed): Full refund is issued with zero merchant penalty.
CONFIRMED (not yet cooking): Full refund is issued and restaurant receives nominal courtesy fee.
PREPARING: Partial 50% refund is issued and restaurant retains food preparation reimbursement.
READY or PICKED_UP: No refund is permitted because food is prepared and courier is en route.
Cost Settlement:
Platform absorbs costs for early cancellations as merchant acquisition expense.
Customer covers preparation costs once kitchen cooking is actively underway.
Restaurants always receive full compensation for ingredients and labor once preparation begins.Real-Time Tracking Data Flow
End-to-end telemetry architecture streaming vehicle coordinates from couriers to waiting customers while filtering GPS noise and snapping positions to road networks.
GPS Optimization (Adaptive Frequency)
- Dasher stationary (speed = 0): Update every 30 seconds to conserve mobile battery.
- Dasher walking to restaurant (< 5 km/h): Update every 10 seconds.
- Dasher driving (> 10 km/h): Update every 4 seconds.
- Dasher approaching customer (< 500m): Update every 2 seconds for highly precise final arrival estimation.
GPS Jitter & Noise Handling (Kalman Filtering)
Raw mobile GPS suffers from a ±10–30 meter inaccuracy due to atmospheric distortions and urban canyons caused by multipath reflection off skyscrapers. Without smoothing, the customer interface would display the courier jumping between buildings or crossing rivers erratically.
We apply a Kalman Filter to predict the next logical coordinates using the dasher's previous speed, heading, and current reading, heavily discounting GPS measurements when the sensor reports low confidence.
Map Snapping
After Kalman filtering, the coordinate stream is snapped onto the nearest road segment utilizing the local street map graph database (such as OpenStreetMap or Google Roads API), ensuring the vehicle is cleanly displayed driving down active streets.
Dynamic ETA Updates
Delivery ETAs are not simple linear estimates. We periodically recalculate the remaining trip length by querying real-time traffic segments on the route, adding a 2–3 minute buffer for parking logistics, elevator transits, and building access control.
Review
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Discussion
Share your thoughts, ask questions, or help others.