Core Entities
Order: order_id, customer_id, restaurant_id, driver_id, items[], total_cents, status, placed_at, estimated_delivery_at. OrderStatus (state machine): PLACED -> CONFIRMED -> PREPARING -> READY_FOR_PICKUP -> PICKED_UP -> EN_ROUTE -> DELIVERED / CANCELLED. Driver: driver_id, current_location (lat/lng), status (AVAILABLE, ON_DELIVERY), current_order_id. TrackingUpdate: update_id, order_id, driver_location, timestamp, event_type (LOCATION_UPDATE, STATUS_CHANGE).
Order State Machine
Each transition is triggered by a specific actor:
- PLACED -> CONFIRMED: restaurant accepts the order (or auto-confirm after 2 minutes)
- CONFIRMED -> PREPARING: restaurant starts preparing
- PREPARING -> READY_FOR_PICKUP: restaurant marks food as ready
- READY_FOR_PICKUP -> PICKED_UP: driver confirms pickup at restaurant
- PICKED_UP -> EN_ROUTE: driver departs restaurant (or combined with PICKED_UP)
- EN_ROUTE -> DELIVERED: driver confirms delivery at customer location
- Any state -> CANCELLED: customer or restaurant cancels (with rules per state)
Invalid transitions are rejected. Store current_status and status_history (for audit and customer UI timeline).
Real-time Location Tracking
Driver app sends GPS coordinates every 5 seconds while on delivery. WebSocket connection from the customer app to a Location Service receives live driver location. Architecture: Driver app -> POST /location (HTTP or WebSocket) -> Location Service stores in Redis (GEOADD active_drivers lng lat driver_id) and publishes to a channel (Redis Pub/Sub or Kafka topic per order). Customer app subscribes to the order channel and receives location updates. Location Service fans out to all subscribers for that order_id. If the customer loses connection, on reconnect the app gets the current driver location from Redis GEOPOS.
ETA Calculation
ETA = preparation_time_remaining + pickup_travel_time + delivery_travel_time. Components: preparation_time_remaining: estimated from restaurant history (average prep time for this restaurant at this time of day) minus time elapsed since PREPARING. pickup_travel_time: routing API call (Google Maps, Mapbox) from driver current location to restaurant. delivery_travel_time: routing API call from restaurant to customer. Update ETA every 30 seconds as driver location changes. Cache routing API results for (origin_geohash, dest_geohash, time_of_day) with 5-minute TTL to reduce API costs. Display ETA to the customer as a range (“arriving in 15-20 minutes”) to account for uncertainty.
Push Notifications for Status Changes
class OrderNotifier:
STATUS_MESSAGES = {
"CONFIRMED": "Your order has been confirmed!",
"PREPARING": "Restaurant is preparing your food",
"READY_FOR_PICKUP": "Driver is picking up your order",
"PICKED_UP": "Your food is on the way!",
"DELIVERED": "Your order has been delivered. Enjoy!",
"CANCELLED": "Your order has been cancelled",
}
def on_status_change(self, order_id, new_status):
order = self.db.get_order(order_id)
message = self.STATUS_MESSAGES.get(new_status)
if message:
self.push.send(order.customer_id, message,
data={"order_id": order_id,
"status": new_status})
Cancellation Rules
Customer can cancel: any time before PREPARING (full refund). After PREPARING: cancellation fee or no refund (restaurant has started cooking). After PICKED_UP: cannot cancel (driver is en route). Restaurant can cancel: before PICKED_UP (trigger driver reassignment or refund). Driver can cancel: before PICKED_UP (trigger driver reassignment). Store cancellation_reason and cancelled_by for auditing. On cancellation: process refund via payment service, notify the other parties, release driver (set status to AVAILABLE).
Scaling
At 1M concurrent active orders: 1M drivers sending location every 5 seconds = 200K writes/second to Redis. Use Redis Cluster. Customer WebSocket connections: use a WebSocket gateway (Socket.io cluster or AWS API Gateway WebSocket) that fans out to all subscribers for an order. Kafka for durable order event streaming (order state changes are events consumed by notification service, ETA service, analytics). Database: shard orders by order_id hash. Read replicas for customer-facing queries (order status, history).
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How does real-time driver location tracking work in a food delivery app?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Driver app sends GPS coordinates every 5 seconds via HTTP POST or WebSocket to a Location Service. The Location Service: (1) Updates driver position in Redis using GEOADD (for proximity queries by the dispatch system). (2) Publishes a location update event to a Redis Pub/Sub channel or Kafka topic keyed by order_id. Customer app maintains a WebSocket connection to a Tracking Service. The Tracking Service subscribes to the order’s location channel and pushes updates to the customer’s WebSocket. If the customer disconnects: on reconnect, they request the current driver position (GEOPOS or Redis hash lookup) — no missed updates matter since only the current position is displayed. Architecture ensures sub-second delivery of location updates from driver to customer.”
}
},
{
“@type”: “Question”,
“name”: “How do you calculate delivery ETA accurately?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “ETA = preparation_time_remaining + pickup_travel_time + delivery_travel_time. Preparation time: use the restaurant’s historical average prep time for this order size and time of day (ML model trained on historical data). Subtract time already elapsed since PREPARING status. Pickup travel time: route from driver current GPS to restaurant using a routing API (Google Maps Distance Matrix, Mapbox). Delivery travel time: route from restaurant to customer address. Recompute ETA every 30-60 seconds as the driver moves. Cache routing API results by (origin_geohash_6, destination_geohash_6, hour_of_week) with a 5-minute TTL to reduce API costs. Display as a range (add 5-10 minute buffer) to set user expectations conservatively.”
}
},
{
“@type”: “Question”,
“name”: “How does the order cancellation policy work at different stages?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Cancellation rules depend on order state. PLACED: free cancellation, full refund. CONFIRMED: free cancellation if within 2 minutes, otherwise small fee. PREPARING: restaurant has started cooking — charge a cancellation fee (typically the base cost), refund the remainder. READY_FOR_PICKUP: cannot cancel without paying full restaurant cost (food is made). PICKED_UP or EN_ROUTE: cannot cancel — driver is on the way. On cancellation: issue a refund (synchronously for the customer-facing response), update order status to CANCELLED, send notifications to all parties (customer, restaurant, driver), release the driver (set status to AVAILABLE and clear current_order_id). Log cancellation_reason and cancelled_by for analytics and dispute resolution.”
}
},
{
“@type”: “Question”,
“name”: “How do you handle driver reassignment when a driver cancels a delivery?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “When a driver cancels after accepting but before pickup: transition order back to READY_FOR_PICKUP (if food is ready) or PREPARING. Mark the cancelling driver as AVAILABLE. Trigger re-dispatch: find the nearest available driver using GEORADIUS and offer the order. Store the list of drivers who already cancelled (cancelled_drivers set) to avoid re-offering to them. If no driver accepts within 5 minutes: escalate to a manual queue for operations review. Notify the customer that their order is being reassigned with an updated ETA. Track driver cancellation rate — drivers with high cancellation rates get penalized in the dispatch scoring algorithm. Allow the customer to cancel penalty-free if reassignment takes longer than N minutes.”
}
},
{
“@type”: “Question”,
“name”: “What happens when the restaurant does not confirm an order within 2 minutes?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Auto-confirmation or auto-cancellation. Option 1 (auto-confirm): if the restaurant does not respond within 2 minutes, auto-confirm the order and alert the restaurant. Good for restaurants with consistent menus and reliable prep times. Option 2 (auto-cancel): cancel the order, refund fully, notify the customer. Used when restaurant acceptance is critical (limited capacity, custom items). Implement via a background job: SELECT orders WHERE status = PLACED AND placed_at < NOW() – INTERVAL 2 minutes. For each: check if restaurant is still OPEN and has AVAILABLE capacity. If yes and using auto-confirm policy: transition to CONFIRMED and notify. If no: cancel with full refund. The background job runs every 30 seconds using SELECT FOR UPDATE SKIP LOCKED to safely process with concurrent workers."
}
}
]
}
Asked at: DoorDash Interview Guide
Asked at: Uber Interview Guide
Asked at: Lyft Interview Guide
Asked at: Snap Interview Guide