Design Uber/Lyft — Ride-Hailing System Architecture
Designing a ride-hailing service like Uber or Lyft tests your ability to build real-time location-based systems. You need driver matching, route optimization, surge pricing, and ETA calculation — all at global scale.
What You’ll Learn
You’ll master GPS polling vs WebSocket strategies, driver matching algorithms, surge pricing models, ETA calculation using routing engines, and QuadTree geospatial indexing.
Why This Problem Matters
Uber processes 25 million trips daily across 10,000+ cities. The architecture combines real-time streaming, geospatial indexing, marketplace economics, and machine learning. At DodaTech, similar geolocation patterns appear in Doda Browser’s location-aware features.
Architecture
flowchart TB
User[User App] -->|Request ride| LB[Load Balancer]
Driver[Driver App] -->|GPS updates| LB
LB --> API[API Gateway]
API --> DM[Driver Matching Service]
API --> Pricing[Surge Pricing Service]
API --> ETA[ETA Service]
DM --> Quad{QuadTree Index}
Quad --> Redis[Redis Cache]
DM --> Trips[(Trips DB)]
Pricing --> Analytics[(Analytics)]
ETA --> Maps[Maps/Routing API]
Location Tracking
Two approaches for sending GPS data:
| Method | Frequency | Battery | Use Case |
|---|---|---|---|
| HTTP Polling | Every 3-5 seconds | Moderate | When connection is unreliable |
| WebSocket | Continuous stream | Lower | Real-time tracking |
Uber sends GPS pings every 4 seconds via WebSocket during active trips, every 30 seconds when idle.
# GPS update handler (simplified)
async def handle_gps_update(driver_id: str, lat: float, lon: float):
# Update QuadTree index
quad_tree.update(driver_id, lat, lon)
# Cache in Redis with expiration
await redis.setex(
f"driver:location:{driver_id}",
30, # TTL 30 seconds
f"{lat},{lon}"
)
# Calculate new ETA for active rides
active_ride = await get_active_ride(driver_id)
if active_ride:
eta = await calculate_eta(lat, lon, active_ride.pickup_lat, active_ride.pickup_lon)
await notify_user(active_ride.user_id, eta)QuadTree for Geospatial Indexing
Finding nearby drivers requires efficient spatial search. A QuadTree divides the map into quadrants:
class QuadTree:
def __init__(self, bounds: tuple, capacity: int = 10):
self.bounds = bounds # (min_lat, max_lat, min_lon, max_lon)
self.capacity = capacity
self.drivers = []
self.divided = False
def search(self, bounds: tuple) -> list:
"""Return drivers within the given bounds."""
if not self.intersects(bounds):
return []
result = [d for d in self.drivers if d.within(bounds)]
if self.divided:
for child in self.children:
result.extend(child.search(bounds))
return resultFor each ride request, query a 5km x 5km quadrant around the pickup point. Expand radius if too few drivers.
Driver Matching
- Rider requests ride at location (lat, lon)
- Query QuadTree for drivers within 5km
- Filter out drivers who are offline, already matched, or not accepting rides
- Calculate ETA for each candidate
- Send ride request to top 3-5 nearest drivers simultaneously
- First driver to accept gets the match
- Notify remaining drivers “ride no longer available”
ETA Calculation
Use a routing engine (like OSRM, Google Maps API) to compute:
- Pickup ETA: Time from current driver location to pickup point
- Dropoff ETA: Time from pickup to destination
async def calculate_eta(from_lat, from_lon, to_lat, to_lon):
# Returns duration in seconds
response = await routing_service.route(
from_lat, from_lon, to_lat, to_lon,
profile="driving"
)
return response["duration"]Surge Pricing
Surge pricing balances supply and demand:
surge_multiplier = base_price * max(1.0, demand / supply * sensitivity_factor)- Demand: Ride requests in the last 5 minutes in a geographic zone
- Supply: Available drivers in the same zone
- Sensitivity: Configurable per city (usually 1.5-2.0)
Common Mistakes
1. Polling GPS Too Frequently
Every 1-second GPS drains the phone battery in under 2 hours. Use adaptive polling: faster during active trips, slower when idle.
2. Not Filtering GPS Noise
GPS accuracy varies (5-50m). Apply Kalman filters or simple averaging to prevent driver positions from jumping.
3. Sending Ride Requests to All Nearby Drivers
Blasting every nearby driver creates bad UX — 20 drivers decline before one accepts. Use sequential targeting or a small batch.
4. Recalculating ETA from Scratch Each Time
Cache route results. The driver’s position changes continuously, but the route only needs recalculation every 10-15 seconds.
5. Ignoring Map Matching
GPS points often land off-road. Snap to road segments using map matching (Hidden Markov Model) for accurate ETAs.
6. Single Region for Surge Pricing
Large cities need micro-zones (500m x 500m). Surge in one neighborhood ≠ surge across the entire city.
7. Not Handling Driver Cancellation
Drivers may cancel after accepting. Implement a “re-match” flow that immediately finds the next best driver.
Practice Questions
1. How do you find nearby drivers efficiently?
Use a QuadTree or geohash spatial index. Don’t scan all drivers.
2. Why not broadcast a ride to every nearby driver?
It creates a poor experience — drivers see a ride and it’s gone. Use sequential or batched matching.
3. What’s the difference between pickup ETA and dropoff ETA?
Pickup ETA = driver location → pickup point. Dropoff ETA = pickup → destination (often pre-computed).
4. How does surge pricing work?
Increases prices when demand exceeds supply in a geographic zone. Encourages more drivers to enter the zone.
5. Challenge: Implement ride cancellation flow.
Handle a rider canceling within 5 minutes (no fee) vs after 5 minutes (fee). Notify the driver. If the driver has already arrived, charge the full fare.
Mini Project: Driver Location Server
Build a WebSocket server that:
- Accepts driver GPS coordinates (simulate with a script)
- Stores latest location in Redis with 30s TTL
- Exposes a REST endpoint:
GET /nearby?lat=...&lon=...&radius=... - Returns drivers within the radius using a simple grid index
- Simulate 100 drivers moving randomly and query nearby results
What’s Next
Congratulations on completing this Uber design! Here’s where to go from here:
- Practice daily — Design one microservice per day
- Build a project — Implement a matching algorithm
- Explore related topics — Geospatial databases, real-time streaming
- Join the community — Share your system designs and get feedback
Remember: every expert was once a beginner. Keep designing!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro