Skip to main content

Classic design · Senior

Ride-hailing app (Uber)

Classic interview design. Key points: geospatial indexing, WebSocket and presence, and distributed locks.

16 min read · Content reviewed on October 22, 2026

Short answer

Classic interview design. Key points: geospatial indexing, WebSocket and presence, and distributed locks. In a ride-hailing app, the rider sees the estimated fare and requests the ride; the system has to match that request with a nearby, available driver, and the driver accepts or declines and proceeds to pickup.

01 · CONTEXT AND REQUIREMENTS

What dispatch needs to guarantee

In a ride-hailing app, the rider sees the estimated fare and requests the ride; the system has to match that request with a nearby, available driver, and the driver accepts or declines and proceeds to pickup. The heart of the problem is the despacho (who gets which request) and the ride state along that path.

Three non-functional requirements drive the design: low-latency matching, with a response (match or failure) in under a minute; strong consistency so that no driver receives several rides at the same time; and high throughput at peaks and events, with a hundred thousand requests coming from the same place.

02 · BASIC FLOW

From request to acceptance

The ride service creates the ride with the initial state of requested and triggers matching. The ride entity follows the request through to completion and holds, among other fields, the state, and it is this state that changes with each decision.

  1. The rider confirms the fare and requests the ride; the gateway authenticates and forwards to the ride service.

    Architecture diagram: Dispatch: from request to acceptance
    CLRider app
    CLDriver app
    NWAPI Gateway
    CPRides service
    DBRides database
    CPMatching service
    CPLocation service
    CHLocation store
    CPNotification service
  2. The ride service writes the ride to the database with the initial state of requested.

    Architecture diagram: Dispatch: from request to acceptance
    CLRider app
    CLDriver app
    NWAPI Gateway
    CPRides service
    DBRides databaseride created
    CPMatching service
    CPLocation service
    CHLocation store
    CPNotification service
  3. Next it calls the matching service to find a driver.

    Architecture diagram: Dispatch: from request to acceptance
    CLRider app
    CLDriver app
    NWAPI Gateway
    CPRides service
    DBRides database
    CPMatching service
    CPLocation service
    CHLocation store
    CPNotification service
  4. In parallel, all the time, drivers send their position and the store keeps only the latest one for each.

    Architecture diagram: Dispatch: from request to acceptance
    CLRider app
    CLDriver app
    NWAPI Gateway
    CPRides service
    DBRides database
    CPMatching service
    CPLocation service
    CHLocation storelatest position
    CPNotification service
  5. Matching queries the nearby, available drivers, ranks them, and has the notification service alert the first on the list.

    Architecture diagram: Dispatch: from request to acceptance
    CLRider app
    CLDriver app
    NWAPI Gateway
    CPRides service
    DBRides database
    CPMatching serviceranks candidates
    CPLocation service
    CHLocation store
    CPNotification service
  6. The driver accepts in the app; the request returns through the gateway and the ride service updates the state and assigns the driver.

    Architecture diagram: Dispatch: from request to acceptance
    CLRider app
    CLDriver app
    NWAPI Gateway
    CPRides servicestate: accepted
    DBRides database
    CPMatching service
    CPLocation service
    CHLocation store
    CPNotification service

The matching service picks the driver weighing proximity, availability, rating, and other factors. The notification service alerts the driver when a request is matched to them, and the location service handles real-time positions.

Matching ranks the eligible drivers and notifies the first on the list via APNs or FCM. If that driver declines, the system notifies the next on the list; on acceptance, the ride service updates the state and assigns the driver.

03 · DRIVER POSITIONS

What feeds matching

With millions of drivers sending their position every few seconds, the write rate reaches millions of updates per second. Without optimization, finding drivers by latitude and longitude would require scanning the entire table.

One way out is an in-memory geospatial store, such as Redis: it serves writes and proximity searches with high throughput and low latency and even expires data on its own. For drivers who go offline, a periodic process removes anyone who has gone past a time limit without an update.

The main challenge is durability, but since positions refresh within a few seconds, the impact of losing data would be small. On the app side, adaptive intervals reduce the volume: the send frequency changes according to speed, direction, proximity to pending requests, and driver status.

04 · ONE DRIVER AT A TIME

Avoiding simultaneous requests to the same driver

Consistency in matching means asking one driver at a time for each ride and giving each driver one request at a time. The driver has a short window, ten seconds, to accept or decline before the system moves on to the next one.

CriterionApplication lockStatus in the databaseLock with expiration in Redis
Coordination across instancesThere is no central coordination and race conditions appearThe database coordinates who holds the lockAll instances compete for the same lock
If the lock owner crashesIt can get stuckThe in-memory timer is lost and the lock gets stuckThe lock expires on its own
Main costLack of coordinationCron or an extra routine to release itDepends on the in-memory store
  1. The ride request is published to the queue instead of being handled directly by the matching service.

    Architecture diagram: Matching with a queue and a per-driver lock
    CPRides service
    QRequest queue
    CPMatching worker A
    CPMatching worker B
    CHPer-driver lock
    DBRides database
    CPNotification service
    CLDriver app
  2. A worker picks up the request; it will only be marked as done after the matching succeeds.

    Architecture diagram: Matching with a queue and a per-driver lock
    CPRides service
    QRequest queue
    CPMatching worker Arequest in progress
    CPMatching worker B
    CHPer-driver lock
    DBRides database
    CPNotification service
    CLDriver app
  3. Before contacting the driver, the worker tries to acquire that driver's lock, with an expiration time equal to the acceptance window.

    Architecture diagram: Matching with a queue and a per-driver lock
    CPRides service
    QRequest queue
    CPMatching worker A
    CPMatching worker B
    CHPer-driver lockdriver lock
    DBRides database
    CPNotification service
    CLDriver app
  4. If another worker tries the same driver now, the lock already exists and its request does not go on to that driver.

    Architecture diagram: Matching with a queue and a per-driver lock
    CPRides service
    QRequest queue
    CPMatching worker A
    CPMatching worker Block denied
    CHPer-driver lock
    DBRides database
    CPNotification service
    CLDriver app
  5. With the lock in hand, the worker sends the request to the driver by push.

    Architecture diagram: Matching with a queue and a per-driver lock
    CPRides service
    QRequest queue
    CPMatching worker A
    CPMatching worker B
    CHPer-driver lock
    DBRides database
    CPNotification service
    CLDriver app
  6. If the driver accepts, the state goes to the database and the lock is released; if the driver ignores it, the lock expires on its own and the driver becomes a candidate again.

    Architecture diagram: Matching with a queue and a per-driver lock
    CPRides service
    QRequest queue
    CPMatching worker A
    CPMatching worker B
    CHPer-driver lockreleases or expires
    DBRides database
    CPNotification service
    CLDriver app

The lock is short-lived and ephemeral, so losing it is easier to recover from than losing durable data; even so, the store needs monitoring and failover because matching depends on it.

05 · PEAKS AND FAILURES

Requests that cannot be lost

Without a queue, if a matching instance crashes, the requests it was processing are lost. With a fila, each request enters the queue and matching consumes them in order of arrival, scaling horizontally if the queue grows.

By committing the offset only after finding the match, the request stays in the queue if the instance crashes and another instance picks it up.

What if the driver doesn't respond? A delay queue schedules a message for when the window expires and, if the ride is still unassigned, moves on to the next driver in the list. The cost is managing many delayed messages and canceling them correctly when the driver accepts.

A durable execution solves this in another way: it offers timeouts, retries, and state that survive crashes and service restarts. The workflow stores its state persistently and, if the service crashes, resumes where it left off. Uber is the original author of the Cadence project, which gave rise to Temporal.

  1. The rides service starts a matching flow for the new ride.

    Architecture diagram: Matching as a durable workflow
    CPRides service
    CPMatching workflow
    DBWorkflow state
    CPNotification service
    CLDriver app
    DBRides database
  2. The flow sends the request to the first driver in the list and waits for the response with a time limit.

    Architecture diagram: Matching as a durable workflow
    CPRides service
    CPMatching workflow
    DBWorkflow state
    CPNotification service
    CLDriver app
    DBRides database
  3. The driver does not respond within the limit; the flow notices the expiration and moves on to the next one in the list.

    Architecture diagram: Matching as a durable workflow
    CPRides service
    CPMatching workflowadvances
    DBWorkflow state
    CPNotification service
    CLDriver appno response
    DBRides database
  4. At each step the progress is recorded in the flow's state store.

    Architecture diagram: Matching as a durable workflow
    CPRides service
    CPMatching workflow
    DBWorkflow stateprogress saved
    CPNotification service
    CLDriver app
    DBRides database
  5. If the flow instance crashes in the middle of the wait, the saved state is still available.

    Architecture diagram: Matching as a durable workflow
    CPRides service
    CPMatching workflowinstance crashed
    DBWorkflow statestate preserved
    CPNotification service
    CLDriver app
    DBRides database
  6. Another instance resumes the flow from where it stopped; when a driver accepts, the database receives the final state.

    Architecture diagram: Matching as a durable workflow
    CPRides service
    CPMatching workflowretomado
    DBWorkflow state
    CPNotification service
    CLDriver app
    DBRides databasedriver assigned

06 · TAKEAWAYS

Each failure, one mechanism

ProblemaMecanismoObservation
Two matchers compete for the same driverLock with expiration in the in-memory storeThe lock disappears on its own if nobody accepts
Matching instance crashesQueue with offset committed after the matchThe request goes back to another instance
Driver does not respondDelay queue or workflow timerMoves on to the next in the list

Frequently asked questions

What is Ride-hailing app (Uber)?

Classic interview design. Key points: geospatial indexing, WebSocket and presence, and distributed locks.

What does the Ride-hailing app (Uber) design cover?

It is organized in these sections: What dispatch needs to guarantee, From request to acceptance, What feeds matching, Avoiding simultaneous requests to the same driver, Requests that cannot be lost, and Each failure, one mechanism.

How long is Ride-hailing app (Uber) and what level is it?

It takes about 16 minutes to read and is rated Senior level.

Now design it yourself

Run this case as an interview in the System Design school: you make the decisions, the tutor challenges them and you see how your design scores.