Skip to content

Ride Booking System Design (HLD & LLD)

A ride booking platform (for example, Uber or Ola) is a classic system design problem that evaluates the ability to design scalable, low-latency, highly available distributed systems.

  1. User requests a ride.
  2. Find nearby drivers.
  3. Driver accepts or rejects a ride.
  4. Fare estimation and ETA.
  5. Real-time driver tracking.
  6. Start and end ride.
  7. Payment processing.
  8. Ride history.
  • Ratings
  • Surge pricing
  • Cancellation
  • Notifications
  • Driver availability
Requirement Description
Availability Very high
Scalability Millions of users
Low Latency Driver matching under 1 second
Real-time Updates Continuous location streaming
Fault Tolerance Ride state should not be lost
Consistency Payments must be strongly consistent
flowchart TD
    A["Mobile Apps<br/>Rider & Driver"] --> B[API Gateway]
    B --> C[Ride Service]
    B --> D[Driver Service]
    B --> E[Matching Service]
    B --> F[Pricing Service]
    B --> G[Payment Service]

    C --> H[Kafka / Message Queue]
    D --> H
    E --> H
    F --> H
    G --> H

    H --> I[Notification Service]
    H --> J[(Databases)]

Responsibilities:

  • Authentication
  • Rate limiting
  • Routing
  • Request validation

Examples:

  • Kong
  • Nginx
  • Spring Cloud Gateway

Manages the ride lifecycle.

Ride states:

REQUESTED
DRIVER_ASSIGNED
DRIVER_ARRIVING
STARTED
COMPLETED
CANCELLED

Tracks:

  • Driver location
  • Driver availability
  • Driver profile

Driver states:

OFFLINE
AVAILABLE
BUSY

Responsibilities:

  1. Find nearby drivers.
  2. Rank them by distance.
  3. Send ride requests.
  4. First driver to accept wins.

Possible technologies:

  • Redis GEO
  • Elasticsearch
  • PostGIS

Drivers publish their location every 3-5 seconds.

Driver App -> Location Service -> Redis GEO

Redis example:

Terminal window
GEOADD drivers 12.9716 77.5946 driver_123

Nearby search:

Terminal window
GEORADIUS drivers
fare = base_fare
+ (distance * price_per_km)
+ (time * price_per_min)
+ surge_multiplier

Surge factor:

demand / supply

Responsibilities:

  • Authorization
  • Capture
  • Refunds

Handles:

  • Driver requests
  • Ride updates
  • Push notifications
  • SMS

Rider
-----
id
name
phone
rating
created_at
Driver
------
id
name
car_details
rating
status
Ride
-----
id
rider_id
driver_id
pickup_location
drop_location
status
fare
start_time
end_time
Payment
-------
id
ride_id
amount
status
method
transaction_id
erDiagram
RIDER ||--o{ RIDE : books
DRIVER ||--o{ RIDE : serves
RIDE ||--|| PAYMENT : has
sequenceDiagram
participant Rider
participant RideService
participant Matching
participant Driver
participant Payment

Rider->>RideService: Request Ride
RideService->>Matching: RideRequested
Matching->>Driver: Ride Offer
Driver-->>Matching: Accept
Matching-->>RideService: Driver Assigned
RideService-->>Rider: Driver Details
Driver->>RideService: Start Ride
Driver->>RideService: End Ride
RideService->>Payment: Charge Fare

Steps:

  1. POST /rides
  2. Create ride with REQUESTED.
  3. Publish RideRequested.
  4. Match nearby drivers.
  5. Driver accepts via POST /rides/{id}/accept.
  6. Ride progresses to DRIVER_ASSIGNED, DRIVER_ARRIVING, STARTED, COMPLETED.
  7. Trigger payment.
class Ride {
Long id;
Long riderId;
Long driverId;
Location pickup;
Location drop;
RideStatus status;
double fare;
LocalDateTime startTime;
LocalDateTime endTime;
}
enum RideStatus {
REQUESTED,
DRIVER_ASSIGNED,
DRIVER_ARRIVING,
STARTED,
COMPLETED,
CANCELLED
}
stateDiagram-v2
[*] --> REQUESTED
REQUESTED --> DRIVER_ASSIGNED
DRIVER_ASSIGNED --> DRIVER_ARRIVING
DRIVER_ARRIVING --> STARTED
STARTED --> COMPLETED
REQUESTED --> CANCELLED
DRIVER_ASSIGNED --> CANCELLED
class RideService {
RideRepository rideRepository;
MatchingService matchingService;
public Ride requestRide(RideRequest request){
Ride ride = createRide(request);
matchingService.matchDriver(ride);
return ride;
}
}
class MatchingService {
DriverRepository driverRepository;
public Driver matchDriver(Location pickup){
List<Driver> drivers =
driverRepository.findNearestDrivers(pickup);
return drivers.get(0);
}
}
  • Stateless Ride Service
  • Stateless Driver Service
  • Stateless Matching Service
rides_1
rides_2
rides_3
  • Driver locations
  • Active rides
  • Fare estimation

For one million drivers updating every three seconds:

  • Approximately 333K updates/second

Solution:

  • Kafka ingestion
  • Stream processing
  • Redis GEO indexing

Preventing Driver Acceptance Race Conditions

Section titled “Preventing Driver Acceptance Race Conditions”

Use an atomic update:

UPDATE ride
SET driver_id = ?
WHERE id = ?
AND driver_id IS NULL;

Only the first successful transaction assigns the driver.

  • Retry
  • Circuit Breaker
  • Timeouts
  • Dead Letter Queue

Offer rides to multiple nearby drivers simultaneously.

if demand > supply
surge = 1.5x

Predict high-demand zones.

Layer Technology
Mobile Android / iOS
API Gateway Kong / Nginx
Backend Java Spring Boot
Messaging Kafka
Cache Redis
Database MySQL / Cassandra
Geo Index Redis GEO
Observability Prometheus + Grafana
  • Driver matching algorithms
  • Geospatial indexing
  • Real-time location streaming
  • Race condition handling
  • Scaling high-frequency location updates
  • Event-driven architecture
  • Separate responsibilities into dedicated microservices.
  • Use Redis GEO or geospatial databases for efficient driver lookup.
  • Stream location updates through Kafka before indexing.
  • Prevent multiple driver assignments with atomic database operations.
  • Keep payment processing strongly consistent while allowing eventual consistency elsewhere.