Low-Level Design: Ride-Sharing Dispatch System — Matching, Routing, and Surge Pricing

Core Entities

Driver: driver_id, name, vehicle_type (ECONOMY/COMFORT/XL), status (OFFLINE, AVAILABLE, ON_TRIP), current_location (POINT: lat/lng), last_location_update, rating, acceptance_rate. Rider: rider_id, name, payment_method_id, rating, created_at. Trip: trip_id, rider_id, driver_id, status (REQUESTED, MATCHING, DRIVER_ASSIGNED, DRIVER_EN_ROUTE, IN_PROGRESS, COMPLETED, CANCELLED), pickup_location (POINT), dropoff_location (POINT), vehicle_type, estimated_pickup_seconds, estimated_duration_seconds, base_fare, surge_multiplier, final_fare, requested_at, matched_at, pickup_at, dropoff_at. DriverLocation: driver_id, location (POINT), heading, speed, updated_at. (Separate high-write table, updated every 4 seconds.)

Driver Location Tracking

class DriverLocationService:
    def update_location(self, driver_id: int, lat: float, lng: float,
                        heading: float, speed: float):
        # Store in Redis geospatial index for fast nearby queries
        self.redis.geoadd("driver_locations", lng, lat, driver_id)
        self.redis.hset(f"driver:{driver_id}", mapping={
            "lat": lat, "lng": lng, "heading": heading,
            "speed": speed, "updated_at": time.time()
        })
        # Also write to DB for trip history and analytics (async)
        self.kafka.produce("driver-locations", {
            "driver_id": driver_id, "lat": lat, "lng": lng,
            "heading": heading, "ts": time.time()
        })

    def get_nearby_drivers(self, lat: float, lng: float,
                            radius_km: float,
                            vehicle_type: str) -> list[dict]:
        # GEORADIUS returns drivers within radius sorted by distance
        nearby = self.redis.georadius(
            "driver_locations", lng, lat, radius_km, "km",
            withcoord=True, withdist=True, sort="ASC", count=20
        )
        result = []
        for driver_id, dist, coords in nearby:
            meta = self.redis.hgetall(f"driver:{driver_id}")
            if meta.get("status") == "AVAILABLE" and 
               meta.get("vehicle_type") == vehicle_type:
                result.append({
                    "driver_id": driver_id,
                    "distance_km": dist,
                    "lat": float(meta["lat"]),
                    "lng": float(meta["lng"])
                })
        return result

Matching Algorithm

class DispatchService:
    def match_driver(self, trip: Trip) -> Optional[int]:
        nearby = self.location_svc.get_nearby_drivers(
            trip.pickup_lat, trip.pickup_lng,
            radius_km=5.0, vehicle_type=trip.vehicle_type
        )
        if not nearby:
            return None

        # Score drivers: weight by ETA (primary) and acceptance rate (secondary)
        for d in nearby:
            eta = self._estimate_eta(d["lat"], d["lng"],
                                     trip.pickup_lat, trip.pickup_lng)
            d["score"] = eta - (d["acceptance_rate"] * 30)  # lower = better

        nearby.sort(key=lambda x: x["score"])

        # Offer to top N drivers sequentially (waterfall dispatch)
        for candidate in nearby[:3]:
            response = self._offer_trip(candidate["driver_id"], trip, timeout=15)
            if response == "ACCEPT":
                return candidate["driver_id"]
            elif response == "REJECT":
                # Mark as rejected for this trip to avoid re-offering
                self.redis.setex(
                    f"trip:{trip.trip_id}:rejected:{candidate['driver_id']}",
                    300, "1"
                )
        return None  # no driver accepted; retry or cancel

Surge Pricing

class SurgePricingService:
    def compute_surge(self, lat: float, lng: float,
                       vehicle_type: str) -> float:
        # Define geographic cell (H3 hexagonal grid or simple grid)
        cell = self._lat_lng_to_cell(lat, lng, resolution=9)

        # Count available drivers and pending requests in this cell
        available_drivers = self.redis.get(f"supply:{cell}:{vehicle_type}") or 0
        pending_requests  = self.redis.get(f"demand:{cell}:{vehicle_type}") or 0

        available_drivers = int(available_drivers)
        pending_requests  = int(pending_requests)

        if available_drivers == 0:
            ratio = float("inf")
        else:
            ratio = pending_requests / available_drivers

        # Surge multiplier table
        if ratio < 0.5:   return 1.0   # excess supply
        if ratio < 1.0:   return 1.2
        if ratio < 2.0:   return 1.5
        if ratio < 3.0:   return 2.0
        return 2.5  # severe shortage

    def update_supply_demand(self, cell: str, vehicle_type: str,
                              delta_supply: int, delta_demand: int):
        pipe = self.redis.pipeline()
        pipe.incrby(f"supply:{cell}:{vehicle_type}", delta_supply)
        pipe.incrby(f"demand:{cell}:{vehicle_type}", delta_demand)
        pipe.expire(f"supply:{cell}:{vehicle_type}", 300)
        pipe.expire(f"demand:{cell}:{vehicle_type}", 300)
        pipe.execute()

Trip Lifecycle and Fare Calculation

Trip state transitions: REQUESTED (rider submits) -> MATCHING (dispatch in progress) -> DRIVER_ASSIGNED (driver accepted) -> DRIVER_EN_ROUTE (driver heading to pickup) -> IN_PROGRESS (trip started, rider in car) -> COMPLETED (dropoff) or CANCELLED. Fare calculation: base_fare = base_amount + (per_km_rate * distance_km) + (per_minute_rate * duration_minutes). Final fare = base_fare * surge_multiplier. Minimum fare enforced. Surge multiplier snapshotted at trip request time – rider sees and accepts it. Cancellation fee: if rider cancels after driver is en route for > 2 minutes, charge a flat fee. Rating: both driver and rider rate after completion; ratings are exponential moving average to prevent gaming by single reviews.

Uber system design interviews cover driver dispatch and matching. See common questions for Uber interview: ride dispatch and driver matching system design.

Lyft system design rounds cover dispatch and matching algorithms. Review design patterns for Lyft interview: ride-sharing dispatch system design.

DoorDash system design covers delivery dispatch and driver assignment. See patterns for DoorDash interview: delivery dispatch and assignment system design.

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

Scroll to Top