LLD Hub
lldstate-machinefactoryobserver

Ride Sharing Low Level Design (Uber/Ola) — LLD Interview Guide

Complete Uber / ride sharing LLD solution — driver matching, fare calculation, trip state machine, class diagram and Java code. Asked at Uber, Ola, Swiggy interviews.

2 April 2025·9 min read

Practice this problem

Ride Sharing System (Uber/Ola) — get AI-scored feedback on your solution

Solve it →

Ride Sharing Low Level Design — designing a system like Uber or Ola — is one of the most comprehensive LLD interview problems. It requires a trip state machine, driver-rider matching, surge pricing via the Strategy pattern, and real-time tracking via the Observer pattern. It is frequently asked at Uber, Ola, Swiggy, and Flipkart SDE interviews.

Why Interviewers Ask Ride Sharing LLD

  • Can you model a complex state machine — ride request through completion?
  • Do you separate driver and rider as distinct entities with different responsibilities?
  • Can you apply multiple design patterns in one solution?
  • Do you think about matching algorithms and location-based queries?
  • Can you design a rating system and handle cancellations cleanly?

Functional Requirements

  • Rider requests a ride with pickup and drop location
  • System finds nearest available driver within a configurable radius
  • Driver accepts or declines the ride request
  • Trip goes through states: REQUESTED, DRIVER_ASSIGNED, IN_PROGRESS, COMPLETED, CANCELLED
  • Fare calculated based on distance, time, and surge multiplier
  • Rider and driver can rate each other after trip completion
  • Rider can cancel before pickup; driver can cancel before pickup
  • Real-time trip status updates sent to rider during trip

Non-Functional Requirements

  • Driver location updates every few seconds — geo-spatial indexing needed
  • Matching must be fast — under 200ms even with thousands of available drivers
  • Cancellation policy — fee applies if rider cancels after driver arrival

Core Entities

  • Rider — id, name, phone, rating, paymentMethod
  • Driver — id, name, vehicle, currentLocation, status (AVAILABLE/ON_TRIP/OFFLINE)
  • Vehicle — licensePlate, type (BIKE/AUTO/CAR/PREMIUM)
  • Trip — id, rider, driver, pickup, drop, status, fare, startTime, endTime
  • Location — latitude, longitude
  • FareStrategy — interface; StandardFare, SurgeFare, FlatFare implement it
  • MatchingStrategy — interface; NearestDriver, HighestRating implement it
  • Rating — tripId, raterId, rateeId, score, comment

Text-Based Class Diagram

Rider
+-- id, name, phone, rating: double
+-- currentTrip: Trip
+-- requestRide(pickup, drop, vehicleType): TripRequest

Driver
+-- id, name, vehicle: Vehicle
+-- location: Location
+-- status: DriverStatus
+-- acceptRide(trip): void
+-- declineRide(trip): void

Trip
+-- id: String
+-- rider: Rider, driver: Driver
+-- pickup: Location, drop: Location
+-- status: TripStatus
+-- fare: double
+-- startTime, endTime: LocalDateTime

TripStatus (enum)
  REQUESTED, DRIVER_ASSIGNED, IN_PROGRESS, COMPLETED, CANCELLED

FareStrategy (interface)
+-- calculateFare(trip): double
StandardFare implements FareStrategy
SurgeFare implements FareStrategy

MatchingStrategy (interface)
+-- findDriver(request, availableDrivers): Driver
NearestDriverStrategy implements MatchingStrategy

Trip State Machine — Java

public enum TripStatus {
    REQUESTED, DRIVER_ASSIGNED, IN_PROGRESS, COMPLETED, CANCELLED
}

public class Trip {
    private TripStatus status = TripStatus.REQUESTED;

    public void assignDriver(Driver driver) {
        if (status != TripStatus.REQUESTED)
            throw new IllegalStateException("Cannot assign driver in status: " + status);
        this.driver = driver;
        this.status = TripStatus.DRIVER_ASSIGNED;
        notifyObservers(TripEvent.DRIVER_ASSIGNED);
    }

    public void startTrip() {
        if (status != TripStatus.DRIVER_ASSIGNED)
            throw new IllegalStateException("Cannot start trip in status: " + status);
        this.status = TripStatus.IN_PROGRESS;
        this.startTime = LocalDateTime.now();
        notifyObservers(TripEvent.TRIP_STARTED);
    }

    public void completeTrip() {
        if (status != TripStatus.IN_PROGRESS)
            throw new IllegalStateException("Cannot complete trip in status: " + status);
        this.status = TripStatus.COMPLETED;
        this.endTime = LocalDateTime.now();
        this.fare = fareStrategy.calculateFare(this);
        notifyObservers(TripEvent.TRIP_COMPLETED);
    }

    public void cancel(String reason) {
        if (status == TripStatus.IN_PROGRESS || status == TripStatus.COMPLETED)
            throw new IllegalStateException("Cannot cancel trip in status: " + status);
        this.status = TripStatus.CANCELLED;
        notifyObservers(TripEvent.TRIP_CANCELLED);
    }
}

Surge Pricing — Strategy Pattern

public interface FareStrategy {
    double calculateFare(Trip trip);
}

public class StandardFare implements FareStrategy {
    private static final double BASE_FARE = 30.0;
    private static final double PER_KM = 12.0;
    private static final double PER_MINUTE = 2.0;

    @Override
    public double calculateFare(Trip trip) {
        double distanceKm = trip.getDistanceKm();
        long minutes = ChronoUnit.MINUTES.between(trip.getStartTime(), trip.getEndTime());
        return BASE_FARE + (distanceKm * PER_KM) + (minutes * PER_MINUTE);
    }
}

public class SurgeFare implements FareStrategy {
    private final double surgeMultiplier;
    private final StandardFare baseFare = new StandardFare();

    public SurgeFare(double multiplier) { this.surgeMultiplier = multiplier; }

    @Override
    public double calculateFare(Trip trip) {
        return baseFare.calculateFare(trip) * surgeMultiplier;
    }
}

Driver Matching

public class NearestDriverStrategy implements MatchingStrategy {
    @Override
    public Optional<Driver> findDriver(TripRequest request, List<Driver> available) {
        return available.stream()
            .filter(d -> d.getStatus() == DriverStatus.AVAILABLE)
            .filter(d -> d.getVehicle().getType() == request.getVehicleType())
            .min(Comparator.comparingDouble(d ->
                calculateDistance(d.getLocation(), request.getPickup())));
    }

    private double calculateDistance(Location a, Location b) {
        // Haversine formula for geo distance
        double lat = Math.toRadians(b.getLat() - a.getLat());
        double lon = Math.toRadians(b.getLon() - a.getLon());
        double h = Math.sin(lat/2) * Math.sin(lat/2) +
                   Math.cos(Math.toRadians(a.getLat())) *
                   Math.cos(Math.toRadians(b.getLat())) *
                   Math.sin(lon/2) * Math.sin(lon/2);
        return 6371 * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1-h));
    }
}

Key Design Decisions

  • State machine for Trip: Explicit state transitions with validation prevent illegal moves like completing a trip that was never started.
  • Strategy for fare and matching: Both are algorithms that need to change independently — surge pricing during peak hours, premium matching for high-rated riders. Injecting strategies at runtime enables this.
  • Observer for status updates: The rider's app, the driver's app, and the logging service all need trip status updates. Observer decouples the Trip from its consumers.

Common Follow-Up Questions

  • "How do you handle driver location updates at scale?" — Use a geospatial index like a QuadTree or Redis GEO commands. Drivers push location updates every 3-5 seconds via WebSocket.
  • "How do you handle driver declining a ride?" — MatchingService retries with the next nearest driver. After N declines or timeout, the request fails with a "no driver available" message.
  • "How do you calculate surge pricing?" — Divide the city into zones (H3 hexagons). If demand/supply ratio in a zone exceeds a threshold, apply a surge multiplier. Recalculate every minute.

FAQ — Ride Sharing Low Level Design

What design patterns are used in Uber/ride sharing LLD?

State pattern (trip lifecycle), Strategy pattern (fare calculation, driver matching), Observer pattern (real-time status updates), and Factory pattern (creating trips and vehicles).

What is the class diagram for ride sharing LLD?

Core classes: Rider, Driver (with Vehicle and Location), Trip (with TripStatus enum, FareStrategy, list of Observers), FareStrategy interface (StandardFare/SurgeFare), MatchingStrategy interface (NearestDriver), Rating.

How does driver matching work in ride sharing LLD?

The MatchingStrategy interface abstracts the algorithm. NearestDriverStrategy filters available drivers by vehicle type and sorts by geo-distance using the Haversine formula. In production, a geospatial index (QuadTree or Redis GEO) handles this efficiently at scale.

Ready to practice?

Submit your solution and get AI-scored feedback on OOP, SOLID principles, design patterns, and code quality.

Solve Ride Sharing System (Uber/Ola)