LLD Hub
lldstate-machinefactoryobserver

How to Design Uber / Ride Sharing | LLD Interview Guide

Design a ride sharing system like Uber or Ola. Driver matching, fare calculation, trip state machine, and real interview patterns used at Uber, Ola, Swiggy.

2 April 2025·9 min read

Practice this problem

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

Solve it →

The Ride Sharing LLD problem (designing Uber or Ola) is one of the most commonly asked advanced LLD questions in product-based company interviews. It tests your ability to model real-time systems, state machines, and multiple interacting entities. This guide breaks down everything you need to ace it.

What Interviewers Are Testing

  • Can you model a multi-state real-time system?
  • Do you understand the State pattern for driver lifecycle?
  • Can you design an extensible fare calculation system?
  • How do you handle asynchronous events like driver acceptance?

Core Requirements

  • Rider requests a ride with pickup and drop location
  • System finds and assigns the nearest available driver
  • Driver can accept or reject — timeout after 30 seconds
  • Real-time trip status: Requested → Accepted → Ongoing → Completed
  • Fare calculation based on distance, time, and ride type
  • Post-trip ratings for both rider and driver
  • Surge pricing during peak hours

Core Entities

  • Rider — has profile, location, payment methods
  • Driver — has vehicle, current location, state
  • Trip — the central entity connecting rider, driver, route, fare
  • Location — latitude/longitude value object
  • RideRequest — captures pickup, drop, ride type
  • FareCalculator — strategy for pricing
  • MatchingService — finds nearest available driver

Driver State Machine

The State pattern is essential here. A driver is always in one of these states:

enum DriverState { OFFLINE, AVAILABLE, ON_TRIP, UNAVAILABLE }

class Driver {
  state: DriverState;
  location: Location;

  goOnline()  { this.state = DriverState.AVAILABLE; }
  goOffline() { this.state = DriverState.OFFLINE; }
  startTrip() { this.state = DriverState.ON_TRIP; }
  endTrip()   { this.state = DriverState.AVAILABLE; }
}

Trip Lifecycle

enum TripStatus {
  REQUESTED, DRIVER_ASSIGNED, DRIVER_ARRIVED,
  ONGOING, COMPLETED, CANCELLED
}

class Trip {
  id: string;
  rider: Rider;
  driver: Driver;
  pickup: Location;
  drop: Location;
  status: TripStatus;
  startTime: Date;
  endTime: Date;
  fare: number;
  rideType: RideType;  // ECONOMY | PREMIUM | XL
}

Fare Calculation with Strategy Pattern

Different ride types have different rates. Surge pricing multiplies the base fare. Using Strategy keeps the Trip class clean:

interface FareStrategy {
  calculate(distanceKm: number, durationMin: number): number;
}

class EconomyFare implements FareStrategy {
  calculate(distanceKm: number, durationMin: number): number {
    return 50 + (distanceKm * 12) + (durationMin * 1.5);
  }
}

class SurgeFare implements FareStrategy {
  constructor(private base: FareStrategy, private multiplier: number) {}
  calculate(distanceKm: number, durationMin: number): number {
    return this.base.calculate(distanceKm, durationMin) * this.multiplier;
  }
}

Driver Matching

In an interview, keep matching simple but extensible. Use a MatchingService that can swap algorithms:

interface MatchingStrategy {
  findDriver(request: RideRequest, drivers: Driver[]): Driver | null;
}

class NearestDriverStrategy implements MatchingStrategy {
  findDriver(request: RideRequest, drivers: Driver[]): Driver | null {
    return drivers
      .filter(d => d.state === DriverState.AVAILABLE)
      .filter(d => d.vehicle.supportsRideType(request.rideType))
      .sort((a, b) =>
        distance(a.location, request.pickup) -
        distance(b.location, request.pickup)
      )[0] ?? null;
  }
}

Observer Pattern for Status Updates

Both rider and driver need real-time trip status updates. Use Observer:

interface TripObserver {
  onStatusChange(trip: Trip, newStatus: TripStatus): void;
}

class Trip {
  private observers: TripObserver[] = [];
  addObserver(o: TripObserver) { this.observers.push(o); }
  updateStatus(status: TripStatus) {
    this.status = status;
    this.observers.forEach(o => o.onStatusChange(this, status));
  }
}

Common Interview Mistakes

  • Embedding fare logic in Trip — violates SRP. Use FareStrategy.
  • Not modeling driver state transitions — interviewers specifically check this.
  • Ignoring driver rejection flow — what happens when all nearby drivers reject? Re-search with wider radius.
  • Missing RideType as an entity — Economy, Premium, XL have different vehicle requirements and pricing.

Follow-up Questions

  • "How do you implement surge pricing?" → Decorator on FareStrategy
  • "How do you handle driver timeout?" → Timer + fallback to next driver
  • "How do you track driver location in real time?" → Separate LocationService, event-driven updates
  • "How would you support ride pooling?" → Trip becomes 1-to-many riders

Companies That Ask This

Uber, Ola, Swiggy (for delivery matching), Rapido, Porter, and increasingly Amazon (for logistics) ask this problem. It's a standard LLD problem for SDE-2 and above roles at product companies.

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)