Ride Matching Service Low-Level Design: Driver-Rider Matching, ETA Calculation, and Surge Pricing

Ride Matching Service Low-Level Design

A ride matching service connects riders with nearby drivers in real time, computing ETAs, scoring candidates, and handling surge pricing. This LLD covers driver location tracking, the dispatch loop, and the driver state machine.

Driver Location Tracking

Active drivers send a GPS update every 5 seconds from the mobile app. The location service writes each update to a Redis geospatial index:

GEOADD drivers_available {longitude} {latitude} {driver_id}

Only drivers in the AVAILABLE state are in the drivers_available key. When a driver is dispatched or goes offline, they are removed with ZREM drivers_available {driver_id}. Redis geospatial commands provide O(N+log(M)) nearby lookups without a specialized spatial DB.

Driver State Machine

AVAILABLE → DISPATCHED → ENROUTE_TO_RIDER → TRIP_IN_PROGRESS → COMPLETED
                      ↘ (no accept, timeout) → AVAILABLE

State is stored in Redis as driver:{id}:state for low-latency reads. Transitions are written to the driver DB table as well for audit and analytics. Invalid transitions (e.g., COMPLETED → TRIP_IN_PROGRESS) are rejected at the service layer.

When a rider requests a trip, the matching service runs:

GEORADIUS drivers_available {rider_lng} {rider_lat} 5 km
  ASC COUNT 20

This returns up to 20 available drivers within 5km, sorted by distance. The 20-candidate limit caps the scoring workload while providing enough candidates to handle fast acceptance.

Match Scoring

Each candidate driver is scored:

score = w1 * (1 / eta_minutes)
      + w2 * driver_rating        // 0-5 scale
      + w3 * acceptance_rate      // 0-1 ratio

ETA weight (w1) is highest — a nearby driver with a 2-minute ETA beats a highly rated driver 8 minutes away. For the top 5 candidates after fast scoring, the routing API is called to get accurate ETA (traffic-aware). The remaining candidates use the straight-line / average speed estimate.

Dispatch Algorithm

  1. Score and rank all candidates.
  2. Atomically mark the top driver as DISPATCHED using Redis SET NX: SET driver:{id}:state DISPATCHED NX EX 15. If another process already dispatched this driver, move to the next candidate.
  3. Send push notification to the driver with trip details.
  4. Start a 10-second acceptance timer. If the driver does not accept within 10 seconds, set state back to AVAILABLE and dispatch to the next candidate.
  5. On accept: create trip record, notify rider of matched driver and ETA.

The NX flag ensures only one dispatch wins the race for a given driver even under concurrent rider requests.

ETA Calculation

Two-tier ETA approach: fast estimate using Haversine distance divided by average city speed (used for all 20 candidates in scoring); accurate routing API call (Google Maps / Mapbox) used only for top-5 candidates to get traffic-aware ETA shown to the rider. The routing API result is cached per (driver_location_cell, destination_cell) for 60 seconds to reduce API cost.

Surge Pricing

The city is partitioned into geohash cells (~1km x 1km). Every minute, a background job computes per-cell supply/demand:

active_riders    = pending_requests in cell in last 5 min
active_drivers   = AVAILABLE drivers in cell
ratio            = active_riders / max(active_drivers, 1)
surge_multiplier = max(1.0, ratio / target_ratio)

The target ratio is tuned per city (typically 0.5-0.7). Surge multipliers are stored in Redis and displayed to riders before they request. The multiplier is locked to the rider's request time so the fare does not change mid-trip.

Trip Pricing

fare = (base_fare
      + per_mile_rate * distance_miles
      + per_minute_rate * duration_minutes)
     * surge_multiplier

Distance and duration are computed from the actual GPS track, not the route estimate, to ensure accurate billing for detours or traffic.

Driver Cancellation Handling

If the driver cancels while ENROUTE_TO_RIDER, the system: resets driver state to AVAILABLE (but logs a cancellation event against the driver quality score); immediately re-runs the matching algorithm for the rider using the same flow; notifies the rider that a new driver is being found. Too many cancellations penalize the driver's acceptance_rate weight in future scoring.

Geofencing

Drivers are only added to the drivers_available index if their current location falls within the service area polygon. On each location update, a point-in-polygon check runs against the city's GeoJSON boundary. Drivers who drift outside the service zone are automatically removed from the available pool and notified.

See also: Uber Interview Guide 2026: Dispatch Systems, Geospatial Algorithms, and Marketplace Engineering

See also: Lyft Interview Guide 2026: Rideshare Engineering, Real-Time Dispatch, and Safety Systems

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

Scroll to Top