← All articles
13 min read

Uber System Design Interview: Design a Ride-Sharing App 2026

Designing Uber is a test of geospatial indexing, real-time writes, and correct state transitions. This guide compares geohash, quadtree, and H3 and walks a trip from request to payment.

The Uber system design interview asks you to design a ride-sharing app that ingests driver GPS updates at scale, finds nearby available drivers in milliseconds, matches one driver to one rider without double-booking, and tracks each trip through payment. A passing answer centers on three choices: an in-memory geospatial index (geohash, quadtree, or H3), a dispatch service that locks drivers atomically, and an explicit trip state machine. Everything else, including ETA, surge pricing, and payments, builds on those three.

This guide walks the full design the way a strong candidate would present it in 45 to 60 minutes.

Key Takeaways

  • The core bottleneck is writes, not reads. Hundreds of thousands to millions of location updates per second rule out a single relational table as the live location store.
  • Geohash is the fastest index to explain, quadtree adapts to density, and H3 gives uniform hexagonal neighbors. Pick one and defend it with the comparison table below.
  • Matching needs an atomic driver lock with a timeout, otherwise two riders can win the same driver.
  • Model the trip as a state machine with idempotent transitions. Most follow-up questions about failures are really questions about this state machine.
  • Payments run asynchronously after the trip ends, with an idempotency key per trip so retries never double-charge.

What Does the Uber System Design Interview Test?

The Uber system design question tests whether you can combine real-time ingestion, geospatial search, and strongly consistent state changes in one system. It is popular because each part is a familiar building block, but the combination forces trade-offs. If you need a refresher on the general interview flow, read our guide to acing the system design interview first.

Interviewers usually grade four things:

  1. Requirements and estimates that drive real decisions, not numbers for show.
  2. A data model for live driver locations that survives the write load.
  3. A matching flow that is correct under concurrency.
  4. Clear handling of failure cases, such as a driver going offline mid-trip.

Candidates interviewing at Uber itself should also read our breakdown of the Uber technical interview process, since the system design round there often digs into marketplace and geospatial details. The same prompt shows up at Lyft and in delivery-flavored versions at DoorDash.

Requirements and Scale

Start by pinning down scope in two or three minutes. A typical agreed scope looks like this.

Functional requirements

  • Riders request a ride from a pickup to a drop-off and see a fare estimate.
  • Drivers go online and stream their location.
  • The system matches a rider to a nearby available driver.
  • Both sides see live location and ETA during the trip.
  • The rider is charged when the trip ends, and the driver is paid out later.

Non-functional requirements

  • Matching latency of a few seconds at most.
  • High availability for location ingestion. Losing a few GPS points is fine.
  • Strong consistency for driver assignment and trip state. Double-booking is not fine.
  • Regional deployment, since a trip in Paris never needs data from Chicago.

Back-of-the-envelope estimates

Uber's public earnings reports put its volume in the tens of millions of trips per day across mobility and delivery. For an interview, assume round numbers you can reason about.

QuantityAssumptionResult
Drivers online at peak5 million
Location update intervalEvery 4 seconds~1.25 million writes/sec
Payload per update~100 bytes (driver ID, lat, lng, heading, speed, timestamp)~125 MB/sec ingress
Ride requests30 million/day~350/sec average, a few thousand/sec at peak
Location history retained1 day of raw points~10 TB/day before compression

The table makes the key point for you: requests are tiny compared with location writes. That single observation justifies most of the architecture.

How Do Drivers Send Location Updates at Scale?

Drivers send location updates over a persistent connection to a stateless gateway, which writes the latest position into an in-memory geospatial store and appends the raw point to a log for everything else. The live store only needs the current position of each driver, so it overwrites rather than appends.

A practical pipeline:

  1. The driver app sends a GPS point every 4 seconds over WebSocket or a long-lived HTTP/2 stream.
  2. A location gateway authenticates the driver and validates the point (drops impossible jumps, stale timestamps).
  3. The gateway updates the driver's cell in the live index and stores driver_id -> {lat, lng, cell, status, ts} in a key-value store with a TTL of around 30 seconds. An expired key means the driver is offline.
  4. The gateway also publishes the point to Kafka. Consumers build trip routes, ETA training data, fraud signals, and analytics.

To cut load, send less often when the car is parked, batch points on poor connections, and skip updates that move less than a few meters.

Shard the live store by geographic cell rather than driver ID. A dispatch query for one pickup point then touches one or a few shards instead of all of them. Map cells to nodes with consistent hashing so adding a node moves only a slice of the cells. Our distributed cache design guide covers consistent hashing in more detail if the interviewer asks.

A hot cell, such as an airport or a stadium after an event, is the classic follow-up. Answer it by splitting into a finer resolution for that area or by replicating the read path for that shard.

Geohash vs Quadtree vs H3: Which Geospatial Index Should You Use?

A geospatial index is a data structure that maps 2D coordinates to keys so that "find everything near this point" becomes a fast lookup instead of a full scan. In a ride-sharing design, you use it to answer one query thousands of times per second: which available drivers are within a few kilometers of this rider?

Geohash encodes latitude and longitude into a base-32 string. Nearby points usually share a prefix, so a prefix match finds candidates. A 6-character geohash covers roughly 1.2 km by 0.6 km, and 7 characters about 150 m by 150 m. Redis supports this out of the box with GEOADD and GEOSEARCH.

Quadtree recursively splits a square region into four children until each leaf holds fewer than some threshold of points. Dense downtown areas get small leaves and empty suburbs get large ones.

H3 is Uber's open-source hexagonal grid. According to Uber's engineering blog post introducing H3, it was built to optimize ride pricing and dispatch and to analyze spatial data. Each cell is a 64-bit integer, and the H3 documentation lists 16 resolutions, from continent-sized cells at resolution 0 to roughly square-meter cells at resolution 15.

CriterionGeohashQuadtreeS2 / H3 (hierarchical cells)
Cell shapeRectanglesSquares of varying sizeS2: quadrilaterals; H3: hexagons
Adapts to densityNo, fixed grid per precisionYes, splits where denseNo, but you choose resolution per use case
Neighbor searchCheck the cell plus 8 neighborsWalk the tree to adjacent leavesH3: gridDisk returns rings of neighbors at equal distance
Edge-case behaviorNearby points can have different prefixes at cell bordersCorrect but needs tree traversalUniform neighbors (H3), fewer border surprises
Update cost per moving driverCheap: recompute string, move between setsHigher: may trigger splits and mergesCheap: recompute cell ID
ShardingEasy, by prefixHard, the tree is a single structureEasy, by parent cell ID
Off-the-shelf supportRedis, Elasticsearch, most databasesUsually custom in-memoryLibraries in many languages, PostGIS extension
Best interview useFast, safe defaultWhen density varies wildly and data is mostly staticDispatch plus surge zones and analytics

What to say in the interview: start with geohash in Redis because it is simple and well supported. Then mention that moving drivers make quadtree updates expensive, and that hexagonal cells like H3 give equal-distance neighbors, which is why Uber built H3 for pricing and dispatch. That answer shows you know the default and the reason to move past it.

A nearby-driver lookup with H3 looks like this:

import h3

def nearby_available_drivers(lat, lng, live_index, max_rings=3, want=10):
    origin = h3.latlng_to_cell(lat, lng, 9)
    found = []
    for k in range(max_rings + 1):
        ring = h3.grid_ring(origin, k)
        for cell in ring:
            found.extend(live_index.available_in(cell))
        if len(found) >= want:
            break
    return found

Expanding ring by ring keeps the common case cheap.

Stuck explaining why hexagons beat rectangles while the interviewer waits? TechScreen gives you real-time, invisible AI help during system design rounds on Zoom, Google Meet, and Teams. Start with 3 free tokens, no credit card.

Get started free →

How Does Driver Matching and Dispatch Work?

Matching takes a ride request, pulls candidate drivers from the geospatial index, ranks them by estimated time to pickup, and offers the trip to the best one under an exclusive lock. The ranking uses road-network ETA, not straight-line distance, because a driver 500 meters away across a river can be ten minutes out.

The dispatch flow:

  1. Candidate search. Query the live index for available drivers within a few rings of the pickup cell.
  2. Filter. Remove drivers with the wrong vehicle type, low acceptance history for this product, or stale locations.
  3. Rank. Call the ETA service for the top 10 to 20 candidates and sort by pickup ETA. Some systems also weigh fairness, such as time since the driver's last trip.
  4. Lock. Atomically change the chosen driver's status from AVAILABLE to OFFERED with a short expiry.
  5. Offer. Push the offer to the driver app and wait for accept or decline.
  6. Fallback. On decline or timeout, release the lock and move to the next candidate.

The lock is the part interviewers push on. A Redis version:

-- KEYS[1] = driver:{id}:status, ARGV[1] = trip_id, ARGV[2] = ttl_seconds
local current = redis.call('GET', KEYS[1])
if current == 'AVAILABLE' then
  redis.call('SET', KEYS[1], 'OFFERED:' .. ARGV[1], 'EX', ARGV[2])
  return 1
end
return 0

Because the script runs atomically, two dispatch workers racing for the same driver cannot both succeed. If you prefer a database, a conditional update (UPDATE drivers SET status = 'OFFERED' WHERE id = ? AND status = 'AVAILABLE') gives the same guarantee.

Greedy vs batched matching

Greedy matching assigns each request the moment it arrives. It is simple and fast, but it can make poor global choices when many requests arrive at once. Batched matching collects requests for a second or two and solves an assignment problem across the batch, which reduces total pickup time across all riders. Mentioning batching as an optimization is a strong signal, especially for staff-level candidates.

What Does the Trip State Machine Look Like?

A trip state machine is the set of allowed states for a trip and the events that move it between them. Making it explicit is how you prevent impossible situations, such as charging for a trip that was never started.

From stateEventTo stateSide effects
REQUESTEDDriver locked and offeredDRIVER_OFFEREDPush offer to driver
DRIVER_OFFEREDDriver declines or timeoutREQUESTEDRelease lock, try next driver
DRIVER_OFFEREDDriver acceptsDRIVER_ASSIGNEDNotify rider, start pickup ETA
DRIVER_ASSIGNEDDriver arrives at pickupDRIVER_ARRIVEDStart wait timer
DRIVER_ARRIVEDRider picked upIN_PROGRESSStart route recording
IN_PROGRESSDriver ends tripCOMPLETEDCompute final fare, emit trip.completed
COMPLETEDPayment capturedPAIDSchedule driver payout
Any pre-trip stateRider or driver cancelsCANCELLEDApply cancellation fee rules

Store trips in a strongly consistent database such as PostgreSQL or a distributed SQL store, partitioned by city or region. Each transition is a conditional write on the current state plus a version number, so a duplicate "end trip" tap from a flaky phone does nothing the second time.

The trip lifecycle, step by step

Here is one ride from request to payment, which is the walkthrough most interviewers want to hear:

  1. The rider enters a destination. The pricing service returns a fare quote, including any surge multiplier, and stores it with a quote ID that expires after a few minutes.
  2. The rider confirms. The trip service creates a REQUESTED trip tied to that quote ID, so the price is locked.
  3. Dispatch finds candidates, locks the best driver, and moves the trip to DRIVER_OFFERED.
  4. The driver accepts. The trip becomes DRIVER_ASSIGNED, and the rider app subscribes to the driver's location stream.
  5. The driver arrives, picks up the rider, and the trip moves to IN_PROGRESS. Location points now also feed the route used for the final fare.
  6. The driver ends the trip. The trip becomes COMPLETED, and the service publishes a trip.completed event.
  7. The payment service consumes the event, charges the rider with the trip ID as the idempotency key, and marks the trip PAID.
  8. Receipts and driver earnings updates go out through the notification system.

If the driver's phone goes offline in step 5, the trip stays IN_PROGRESS. The server keeps the last known route and reconciles when the phone reconnects with its buffered points.

How Do ETA and Surge Pricing Work?

ETA is the predicted travel time between two points on the road network, and surge pricing is a multiplier that raises fares in areas where demand exceeds supply. Both are usually extensions rather than the core of the interview, so cover the basics and go deeper only when asked.

ETA basics

  • Model the road network as a weighted graph where edge weights are travel times.
  • Run a shortest-path algorithm. Plain Dijkstra is too slow at city scale, so production systems use precomputation techniques such as contraction hierarchies.
  • Adjust edge weights with live traffic derived from the same driver location stream you are already ingesting.
  • Layer a machine learning model on top to correct systematic errors, for example at specific intersections or times of day.

Surge pricing basics

  1. Every 30 to 60 seconds, count open requests and available drivers per cell. A coarse H3 resolution works well here because hexagons tile evenly.
  2. Compute a demand-to-supply ratio, smooth it over time, and map it to a multiplier with a cap.
  3. Publish multipliers to a cache read by the pricing service.
  4. Lock the quoted price into the trip at confirmation, so a surge change mid-request never changes what the rider agreed to pay.

Smoothing matters. Without it, prices flicker every refresh and drivers chase cells that cool down before they arrive.

How Should Payments Work in a Ride-Sharing Design?

Payments in a ride-sharing system should run asynchronously after the trip completes, keyed by the trip ID so that retries never create a second charge. The trip service should never block on a payment provider being available.

A common pattern:

  • At request time, place an authorization hold for the estimated fare on the rider's card.
  • On trip.completed, compute the final fare from the locked quote plus actual distance and time where the product calls for it.
  • Capture the payment through the provider using the trip ID as the idempotency key.
  • Write a double-entry ledger record: rider debit, platform revenue, driver payable.
  • Batch driver payouts on a schedule, separate from rider charges.

Failed captures go to a retry queue with backoff, and anything still failing goes to a dead-letter queue for manual review. Our dedicated payment system design guide covers ledgers, idempotency, and reconciliation in full.

Common Follow-Up Questions and Strong Answers

Expect at least two of these in the last 15 minutes:

  • What happens if a dispatch node dies? The live index is soft state. Drivers resend their location within seconds, so a replacement node rebuilds its cells quickly. Trip state lives in the durable database, not the index.
  • How do you handle a hot cell at an airport? Use queue-based dispatch for airports (first-in, first-out driver queue in a geofenced lot), and split dense cells into finer resolutions.
  • How do you scale across cities? Deploy per region, route by the rider's city, and keep cross-region data limited to accounts and payments.
  • How do you protect the APIs from abuse? Add per-user and per-device limits at the gateway, which our rate limiter design guide explains.

How to Practice This Question

Practice the Uber design out loud with a 45-minute timer, and draw the trip state machine before anything else. Most weak answers skip it and then struggle when asked about cancellations or duplicate events.

Sketch five boxes (location pipeline, live index, dispatch, trip service, payments), defend one index against the other two, then walk the lifecycle aloud.

For a structured plan, see our guide on how to practice system design interviews, and pair this question with the chat app design, which reuses the same persistent connection patterns.

Want a second brain in the room for your Uber or Lyft system design round? TechScreen runs invisibly during screen shares and suggests trade-offs, estimates, and follow-up answers in real time. Try it on a mock interview with 3 free tokens.

Get started free →

Frequently Asked Questions

What is the hardest part of the Uber system design interview?

The hardest part is handling the location write load while keeping nearby-driver queries fast. Millions of drivers send GPS updates every few seconds, so a single relational table with a spatial index will not keep up. Strong candidates keep live locations in an in-memory store sharded by geographic cell, write history asynchronously through a log like Kafka, and explain how matching avoids assigning one driver to two riders at the same time.

Should I use geohash, quadtree, or H3 in a ride-sharing design?

Any of the three can work if you explain the trade-offs. Geohash is the simplest because it turns a coordinate into a string prefix that Redis and most databases already handle. A quadtree adapts to density, which helps in cities with uneven driver counts, but it is harder to shard and update. H3 uses hexagons with uniform neighbor distances, which makes it a strong fit for radius searches and surge zones.

How often should drivers send location updates in a ride-sharing system?

A common interview assumption is every 4 to 5 seconds while a driver is online. You can reduce load by sending less often when the driver is stationary, batching points on the client, and dropping updates that move less than a few meters. Mention that the interval is a tunable trade-off between freshness and cost.

How do you prevent two riders from being matched to the same driver?

Treat the offer as a lease on the driver. When dispatch selects a driver, it performs an atomic compare-and-set that moves the driver from available to offered with a short expiry, often around 10 to 15 seconds. Only one request can win that write. If the driver declines or the lease expires, the driver returns to the available pool and dispatch moves on to the next candidate.

Does the Uber system design interview require surge pricing?

Usually not in depth. Most interviewers expect surge pricing as an extension after the core flow works. A solid answer computes a supply and demand ratio per geographic cell over a short window, maps it to a multiplier with caps and smoothing, and locks the quoted price when the rider confirms. Spend more time there only if the interviewer steers you toward pricing.

Is designing Uber asked only at Uber?

No. Ride-sharing is one of the most common system design prompts across the industry because it tests real-time data, geospatial queries, and consistency in one problem. Variants include designing Lyft, a food delivery dispatcher like DoorDash, a proximity service like Yelp, or a find-nearby-friends feature. The same building blocks of location ingestion, spatial indexing, and matching apply to all of them.

Ready to use AI assistance in your next interview?

TechScreen is the invisible AI assistant trusted by engineers interviewing at Google, Meta, Amazon, and hundreds of other companies. Start with 3 free tokens — no credit card required.

Ace your next interview →