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.