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).
Asked at: DoorDash Interview Guide
Asked at: Uber Interview Guide
Asked at: Lyft Interview Guide
Asked at: Snap Interview Guide