Machine Coding Problem

Food Delivery (Zomato-lite)

maco30maco60macoAllmarketplacestrategy-patternobserver-patternthread-safe-resource-acquisitionstate-transition-guards
Commonly Asked By:DoorDashUberSwiggyZomato

Requirements & System Scope

Functional Scope (In-Scope)

  • Order Status State Machine: Guarantee logical sequence state transitions: PLACED โ†’ CONFIRMED โ†’ PREPARING โ†’ PICKED_UP โ†’ DELIVERED.
  • Driver Proximity Strategies (Strategy Pattern): Support switching algorithms (e.g. Nearest-first, Highest Rated Driver) dynamically at runtime.
  • Observer Notifications: Enable live subscribers (Customer Alerts, Kitchen Dashboards) to receive event triggers dynamically upon order status transitions.
  • Atomic Resource Booking: Prevent double driver assignments on overlapping concurrent customer orders using atomic checks.
  • Real-Time ETA metrics: Calculate driver distances relative to target kitchens.

Explicit Boundaries (Out-of-Scope)

  • No Live SMS Phone Gateways: Excludes direct integration with Twilio, Sinch, or mobile network SMS headers.
  • No Real Card clearing Payment Gateway: Bypasses Stripe APIs, card charge authorizations, or actual bank settlement networks.

Class Diagram & Entity Relationships

Structural layout showing relationships between restaurant orders, delivery strategies, and drivers:

Loading...
  • Strategy Encapsulation: DeliveryAssignmentStrategy isolates vehicle matching logic from the core order operations.
  • Observer updates: Status changes broadcast notifications immediately to registered customer and restaurant dashboard list subscribers.

Design Patterns & SOLID Principles

  • Strategy Design Pattern (Driver Allocators): Decoupling matching metrics inside separate strategy modules lets systems swap algorithms at runtime without changing order lifecycles.
  • Observer Pattern (Order States): Publishes live order lifecycle updates directly to multiple downstream notification triggers seamlessly (satisfies Open-Closed Principle).
  • Single Responsibility Principle (SRP):Driver handles vehicle availability; Order tracks preparation status; DeliveryAssignmentStrategy resolves geospatial mapping coordinates.

Core Execution Workflows

Order Placement & Delivery Cycle

  1. Customer completes checkout: placeOrder(restaurantId, restaurantLocation).
  2. Instantiate new Order in status PLACED and attach event observers.
  3. Invoke matching strategy: findDriver(order, activeDrivers).
  4. Strategy analyzes active coordinates, selecting the closest/highest-rated qualified driver.
  5. Attempt atomic acquisition: candidate.acquire().
    1. If success, assign candidate ID to order, advance order status to CONFIRMED.
    2. Otherwise, place order in assignment queue for retry cycles.
  6. Kitchen prepares order, driver signals status updates (PREPARING โ†’ PICKED_UP โ†’ DELIVERED).

Concurrency & Thread Safety Strategy

The major concurrency vulnerability is the driver double-booking race:

  • Compare-And-Swap (CAS) Driver Acquisition: Guard isAvailable updates behind synchronized locks to ensure only one order can claim a driver at a time.
  • Fine-Grained Order Locks: Update order metrics under isolated locks per order id rather than utilizing full database level locks.

Complete Clean Code Blueprint

Clean reference designs showing delivery matching flows in Java and Python:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

enum OrderStatus { PLACED, CONFIRMED, PREPARING, PICKED_UP, DELIVERED }

class Location {
    private final double lat;
    private final double lon;

    public Location(double lat, double lon) {
        this.lat = lat;
        this.lon = lon;
    }

    public double getLat() { return lat; }
    public double getLon() { return lon; }

    public double distanceTo(Location other) {
        return Math.sqrt(Math.pow(this.lat - other.getLat(), 2) + Math.pow(this.lon - other.getLon(), 2));
    }
}

class Driver {
    private final String id;
    private final String name;
    private final double rating;
    private Location location;
    private boolean isAvailable = true;
    private final ReentrantLock lock = new ReentrantLock();

    public Driver(String id, String name, double rating, Location location) {
        this.id = id;
        this.name = name;
        this.rating = rating;
        this.location = location;
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public double getRating() { return rating; }
    public Location getLocation() { return location; }
    
    public void setLocation(Location location) {
        this.location = location;
    }

    public boolean isAvailable() { return isAvailable; }

    public boolean acquire() {
        lock.lock();
        try {
            if (isAvailable) {
                isAvailable = false;
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    public void release() {
        lock.lock();
        try {
            isAvailable = true;
        } finally {
            lock.unlock();
        }
    }
}

interface OrderObserver {
    void onOrderStatusChanged(String orderId, OrderStatus status);
}

class CustomerNotifier implements OrderObserver {
    @Override
    public void onOrderStatusChanged(String orderId, OrderStatus status) {
        System.out.println(String.format("[SMS to Customer] Order %s is now %s!", orderId, status));
    }
}

class RestaurantNotifier implements OrderObserver {
    @Override
    public void onOrderStatusChanged(String orderId, OrderStatus status) {
        System.out.println(String.format("[Dashboard to Restaurant] Order %s advanced to status %s.", orderId, status));
    }
}

class Order {
    private final String id;
    private final String restaurantId;
    private final Location restaurantLocation;
    private OrderStatus status = OrderStatus.PLACED;
    private Driver assignedDriver;
    private final List<OrderObserver> observers = new CopyOnWriteArrayList<>();
    private final ReentrantLock lock = new ReentrantLock();

    public Order(String id, String restaurantId, Location restaurantLocation) {
        this.id = id;
        this.restaurantId = restaurantId;
        this.restaurantLocation = restaurantLocation;
    }

    public String getId() { return id; }
    public String getRestaurantId() { return restaurantId; }
    public Location getRestaurantLocation() { return restaurantLocation; }
    
    public OrderStatus getStatus() { 
        lock.lock();
        try { return status; } finally { lock.unlock(); }
    }
    
    public Driver getAssignedDriver() { 
        lock.lock();
        try { return assignedDriver; } finally { lock.unlock(); }
    }

    public void addObserver(OrderObserver observer) { observers.add(observer); }
    public void removeObserver(OrderObserver observer) { observers.remove(observer); }

    public boolean updateStatus(OrderStatus next) {
        lock.lock();
        try {
            if (next.ordinal() == this.status.ordinal() + 1) {
                OrderStatus oldStatus = this.status;
                this.status = next;
                notifyObservers(next);
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    public void assignDriver(Driver driver) {
        lock.lock();
        try {
            this.assignedDriver = driver;
        } finally {
            lock.unlock();
        }
    }

    private void notifyObservers(OrderStatus status) {
        for (OrderObserver obs : observers) {
            obs.onOrderStatusChanged(id, status);
        }
    }
}

// โ”€โ”€โ”€ STRATEGY PATTERN (DRIVER MATCHING) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
interface DeliveryAssignmentStrategy {
    Driver findDriver(Order order, List<Driver> drivers);
}

class NearestDriverStrategy implements DeliveryAssignmentStrategy {
    @Override
    public Driver findDriver(Order order, List<Driver> drivers) {
        Driver closest = null;
        double minDistance = Double.MAX_VALUE;

        for (Driver d : drivers) {
            if (d.isAvailable()) {
                double dist = d.getLocation().distanceTo(order.getRestaurantLocation());
                if (dist < minDistance) {
                    minDistance = dist;
                    closest = d;
                }
            }
        }
        return closest;
    }
}

class HighestRatedDriverStrategy implements DeliveryAssignmentStrategy {
    @Override
    public Driver findDriver(Order order, List<Driver> drivers) {
        Driver selected = null;
        double maxRating = -1.0;

        for (Driver d : drivers) {
            if (d.isAvailable()) {
                double dist = d.getLocation().distanceTo(order.getRestaurantLocation());
                if (dist < 10.0 && d.getRating() > maxRating) { // within 10km limit
                    maxRating = d.getRating();
                    selected = d;
                }
            }
        }
        return selected;
    }
}

class FoodDeliveryService {
    private final List<Driver> drivers = new CopyOnWriteArrayList<>();
    private final Map<String, Order> orders = new ConcurrentHashMap<>();
    private DeliveryAssignmentStrategy assignmentStrategy;

    public FoodDeliveryService(DeliveryAssignmentStrategy initialStrategy) {
        this.assignmentStrategy = initialStrategy;
    }

    public void addDriver(Driver d) { drivers.add(d); }
    public void setStrategy(DeliveryAssignmentStrategy strategy) { this.assignmentStrategy = strategy; }

    public Order placeOrder(String restaurantId, Location restaurantLoc) {
        String id = "ORD-" + UUID.randomUUID().toString().substring(0, 8);
        Order order = new Order(id, restaurantId, restaurantLoc);
        orders.put(id, order);

        // Attach system observers
        order.addObserver(new CustomerNotifier());
        order.addObserver(new RestaurantNotifier());

        System.out.println("[Service] Order placed: " + id + ". Finding driver...");

        Driver candidate = assignmentStrategy.findDriver(order, drivers);
        if (candidate != null && candidate.acquire()) {
            order.assignDriver(candidate);
            order.updateStatus(OrderStatus.CONFIRMED);
            System.out.println("[Service] Assigned Driver " + candidate.getName() + " to Order " + id);
        } else {
            System.out.println("[Service] No drivers available for Order " + id);
        }

        return order;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== Food Delivery Concurrent System ===");
        FoodDeliveryService service = new FoodDeliveryService(new NearestDriverStrategy());

        service.addDriver(new Driver("DRV-1", "John (Nearest)", 4.2, new Location(1.0, 1.0)));
        service.addDriver(new Driver("DRV-2", "Adam (High Rating)", 4.9, new Location(4.0, 4.0)));

        Location pizzaHub = new Location(1.2, 1.2);

        System.out.println("\n--- Placing Order using NearestDriverStrategy ---");
        Order order1 = service.placeOrder("PIZZA_HUB", pizzaHub);

        System.out.println("\n--- Switching Strategy to HighestRatedDriverStrategy ---");
        service.setStrategy(new HighestRatedDriverStrategy());
        service.addDriver(new Driver("DRV-3", "Sarah", 4.9, new Location(1.3, 1.3)));

        Order order2 = service.placeOrder("BURGER_LAND", new Location(2.0, 2.0));
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...