Design a Mobile Food Delivery App: DoorDash and Uber Eats

“Design a food-delivery app” is the consumer-side mobile-system-design counterpart to the courier-side question. The interviewer wants to see how you handle restaurant search at scale, cart state across devices, real-time order tracking, and the unforgiving UX of “where is my food right now.”

Clarify scope

  • Eater app only, or also restaurant tablet and courier app?
  • Single market or global? (Localization, currencies, address formats)
  • Group ordering for office lunches?
  • Subscription tier (DashPass / Uber One)?

Core flows

  1. Discover: search/browse restaurants by location and cuisine
  2. Configure: pick items, modifiers, special instructions; cart updates
  3. Pay: address, tip, payment method, place order
  4. Track: real-time updates from “preparing” through “delivered”
  5. Rate and reorder

The home feed is the heaviest screen. Components:

  • Geocode the user’s current location (or a saved address)
  • Server returns ranked restaurants within delivery radius
  • Personalization: cuisine preferences, past orders, time-of-day signals
  • Carousels: “Fast near you”, “Sponsored”, “New”, “Reorder”

Caching strategy: cache the home payload by (location grid cell, time bucket) on the client; refresh stale entries silently in the background. Cell-based caching means thousands of users in the same area share a cache and the server gets fewer requests.

Cart state

The cart needs to survive app kill, switch to web, and roaming back. Two storage layers:

  • Local SQLite/CoreData: source of truth offline; hydrates UI instantly
  • Server-backed cart tied to user ID: enables cross-device, web↔app continuity

Sync rule: optimistic local update, debounced PUT to server, conflict-resolve by latest-write-wins per item or merge by union.

Real-time tracking

The most-watched screen in the app. Status transitions:

  • Order placed → confirmed → preparing → courier assigned → picked up → en route → delivered
  • ETA estimate refreshed on each transition and on courier location updates

Transport: WebSocket while screen is foregrounded; falls back to silent push for background updates so the app can update notifications without keeping a socket open. Same pattern as Uber rider — see the Uber design doc on this site for the tradeoffs.

Maps and ETA

Show the courier moving toward the eater. Smooth client-side animation between server samples (linear interpolation between last two known positions). ETA comes from a routing service that knows traffic and the courier’s task list (multi-drop trips).

Payment and tipping

  • Apple Pay / Google Pay first; saved cards as fallback
  • Tip can be edited until courier picks up — design a small grace window with optimistic update + server validation
  • Refund flow for missing items: photo upload, automated triage, instant credit for trusted accounts

Offline and degraded modes

  • Hard offline: show cached menu, disable “Place order” button
  • Slow network: skeleton states; do not block on images for the menu render
  • Restaurant offline: graceful “Sorry, this restaurant is currently unavailable” without crashing the cart

Notifications

  • Push for each status transition (preparing, picked up, en route, delivered)
  • Live Activity (iOS) / Live Update (Android 14+) so users see ETA on the lock screen
  • Marketing pushes are separate; respect quiet hours and per-category opt-outs

Scale considerations

  • Search ranking: precomputed at the cell level, refreshed every few minutes; per-user re-rank in a small final step
  • Order routing: dispatch service runs on the backend; the app just observes
  • Surge / market dynamics: the app shows the price but is not aware of the algorithm

What separates senior from staff

Junior candidates list features. Senior candidates discuss the cart-sync conflict model and the foreground/background WebSocket-vs-push tradeoff. Staff candidates discuss multi-tenancy of the courier (multi-restaurant multi-drop trips) and how the eater UI represents that without lying about ETA.

Frequently Asked Questions

Why split eater and courier into separate apps?

Different UX, different permissions, different background needs. Couriers run a navigation app for hours; eaters need an instant-load home feed. Sharing a binary would compromise both.

How do I handle scheduled orders?

Same data model with a scheduled_for timestamp. Server enqueues a delayed dispatch. The app shows “Scheduled for 7 PM” prominently so the user is not surprised.

What about the restaurant tablet?

Out of scope for the eater question, but mention you would design it as a thin client over the order pipeline with a printer integration and noise-tolerant UI.

Scroll to Top