Build a Ride-Sharing Backend
Instructions
Ride-sharing systems like Uber and Lyft are among the most frequently asked system design interview questions. They test your understanding of geospatial indexing, real-time matching, dynamic pricing, and state machines. In this lab, you'll build the core backend components that power a ride-sharing service.
Architecture Overview
rideshare/main.py (simulation entry point)
├── rideshare/geo/geohash.py — Geohash encoding/decoding
├── rideshare/geo/spatial_index.py — Spatial index for nearby queries
├── rideshare/matching/matcher.py — Ride matching algorithm
├── rideshare/matching/eta_calculator.py — ETA calculation (Haversine)
├── rideshare/pricing/surge_calculator.py — Surge pricing
├── rideshare/pricing/fare_calculator.py — Fare computation
└── rideshare/tracking/ride_tracker.py — Ride lifecycle state machine
Step-by-Step Instructions
FILE 1: rideshare/geo/geohash.py
Implement geohash encoding and decoding for geospatial indexing.
- Encode latitude/longitude to a geohash string of configurable precision (default 6)
- Use bit interleaving: alternate between longitude and latitude bits
- Decode a geohash string back to approximate latitude/longitude (center of the cell)
- Calculate the 8 neighboring geohash cells for a given geohash
- Use the Base32 alphabet:
0123456789bcdefghjkmnpqrstuvwxyz
FILE 2: rideshare/geo/spatial_index.py
Build a spatial index that uses geohashes for fast nearby queries.
- Insert drivers with their current position (lat, lng)
- Remove drivers when they go offline or get matched
- Query nearby drivers within a given radius using geohash prefix + neighbor expansion
- Calculate actual distances using Haversine to filter false positives from geohash approximation
- Return results sorted by distance from the query point
FILE 3: rideshare/matching/matcher.py
Implement the ride matching algorithm that finds the optimal driver.
- Score drivers based on a weighted combination: distance (40%), ETA (30%), rating (30%)
- Filter out drivers beyond maximum pickup distance
- Filter out drivers whose vehicle type doesn't match the ride request
- Return the best match or None if no drivers are available
- Support different vehicle types: economy, comfort, premium
FILE 4: rideshare/matching/eta_calculator.py
Calculate estimated time of arrival for drivers.
- Use the Haversine formula to compute great-circle distance between two points
- Apply a road-factor multiplier (default 1.4) since roads aren't straight lines
- Apply a traffic multiplier based on time of day (rush hour = higher multiplier)
- Return ETA in minutes with both distance and time components
FILE 5: rideshare/pricing/surge_calculator.py
Implement dynamic surge pricing based on supply and demand.
- Calculate demand/supply ratio for a geographic zone (geohash prefix)
- Map ratio to surge multiplier: ratio < 1.0 → 1.0x, ratio 1.0-2.0 → linear 1.0x-2.0x, ratio > 2.0 → capped
- Apply configurable minimum (1.0) and maximum (3.0) surge caps
- Track request counts and available drivers per zone
- Provide a method to decay old demand data over time
FILE 6: rideshare/pricing/fare_calculator.py
Calculate ride fares with all pricing components.
- Base fare + (distance_km × per_km_rate) + (duration_min × per_min_rate)
- Apply surge multiplier from the surge calculator
- Apply minimum fare floor (no ride cheaper than the minimum)
- Support different rate cards per vehicle type (economy, comfort, premium)
- Return a breakdown showing base, distance, time, surge, and total
FILE 7: rideshare/tracking/ride_tracker.py
Implement the ride lifecycle as a state machine.
- States: REQUESTED → MATCHED → DRIVER_EN_ROUTE → ARRIVED → IN_PROGRESS → COMPLETED or CANCELLED
- Validate state transitions (e.g., cannot go from REQUESTED directly to IN_PROGRESS)
- Track timestamps for each state transition
- Support cancellation from REQUESTED, MATCHED, DRIVER_EN_ROUTE, or ARRIVED states
- Calculate ride duration and wait time from timestamps
FILE 8: rideshare/main.py
Wire everything together with a simulation.
- Seed sample drivers at various locations
- Create ride requests from sample riders
- Run the matching algorithm to pair riders with drivers
- Calculate surge pricing and fares
- Execute the full ride lifecycle from request to completion
- Print a summary of each ride with timing, fare breakdown, and driver details
Hints
- The geohash Base32 alphabet is
0123456789bcdefghjkmnpqrstuvwxyz(32 characters, no a, i, l, o) - Haversine formula:
a = sin²(Δlat/2) + cos(lat1) × cos(lat2) × sin²(Δlng/2),c = 2 × atan2(√a, √(1-a)),d = R × cwhere R = 6371 km - For geohash neighbors, each geohash cell has exactly 8 neighbors (N, NE, E, SE, S, SW, W, NW)
- Average driving speed in a city: ~30 km/h (used for ETA from distance)
- Surge pricing is per-zone: use geohash prefix (e.g., precision 4) as the zone identifier