Guide · System design
Design a Ride-Hailing App Like Uber: Matching and Surge
25 min readUpdated By Saket Bhatnagar
Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.
In short
- Uber system design comes down to three hard parts: tracking each driver's live location, matching each rider to a good nearby driver, and pricing rides when demand spikes.
- Driver phones send a GPS ping, a small message with position and time, every few seconds. Keep each driver's latest spot in an in-memory geo index, a store that quickly answers who is near a point.
- Matching takes nearby free drivers, ranks them by road time rather than straight-line distance, and pairs a few seconds of requests at once. Uber says it moved from closest-first matching to batched matching.
- Surge pricing compares ride requests with free drivers in small map cells and raises a multiplier, the number the normal fare is multiplied by. The higher price trims demand and pulls drivers in, and this design caps it at 2.0x.
- Beginners can stop after the surge section. Engineers then get tested Redis and solver code, a claim key that stops double booking and the two races it still has, and a failure plan.
What goes wrong when a ride-hailing app just sends the nearest driver?
Riders wait too long, and some get no car at all. The nearest driver in a straight line can be far away by road, the map can show drivers who left, and two riders can get the same car. Uber system design is mostly about fixing these problems at city scale.
Picture a common setup. A regional app runs cars and autos (three-wheeled auto-rickshaws) in Guwahati, with about 30,000 rides on a normal weekday and around 2,400 drivers online at the evening peak. The setup is made up, but the problems are real.
Its first dispatch code is the naive version: when a rider books, send the closest free driver. It works on quiet days. Then a wet Monday in June brings four kinds of trouble.
- A rider at Ganeshguri gets a car 600 metres away in a straight line. It is on the far side of a flyover, a 14-minute drive.
- Another rider sees an auto parked near her on the map. Its driver switched off his phone and went home 10 minutes earlier.
- Two riders in Paltan Bazaar book within a second of each other. Both are sent the same auto.
- Near the Kamakhya temple, crowds for the yearly Ambubachi Mela (a temple fair) push waits past 25 minutes. Drivers drive 6 km empty to reach riders, earning nothing for those kilometres.
That evening, 38% of ride requests end with no driver at all (made up). Each problem has its own fix, and this guide builds them in order: fresh locations, road-time matching in short batches, surge pricing, and a claim that stops double booking.
What does an Uber system design need to handle?
An Uber system design must handle four things: live driver locations, a fast match for each request, a price that reacts to demand, and trips that never double book. Payments and ratings matter too, but these four are the core of the problem.
Start with what users can do. These are the functional requirements:
- Riders see nearby cars and autos, get a fare quote and book one.
- Drivers go online, receive ride offers, and accept or decline them.
- Both sides see each other move on the map during pickup and the trip.
- The fare reflects distance and time, plus a surge multiplier at busy moments.
Non-functional requirements are the quality targets. In this setup, the team wants a match within about 10 seconds and cars on the map no more than a few seconds old. One driver must never get two riders at once.
Now some rough maths. Each online driver's phone sends a GPS ping every 4 seconds. With 2,400 drivers online, that is 2,400 divided by 4, or 600 location writes a second.
Bookings are tiny next to that. Even at the evening peak, the app sees only a few bookings a second. So the system is write-heavy for locations and light for bookings, and that one fact shapes most of the design.
Here are the main parts. Follow the driver's pings into the location service, and notice that matching reads from the geo index, not from the trip database.
The app's main parts
Drag to exploreLocation data flows in constantly. Bookings are rare by comparison and end up in the trip database.
Diagram connections:
- Driver app to Connection gateway (GPS pings)
- Rider app to Connection gateway (Book a ride)
- Connection gateway to Location service
- Location service to Redis geo index (Latest spot)
- Location service to Kafka location stream (Every ping)
- Connection gateway to Matching service (Ride requests)
- Matching service to Redis geo index (Find nearby)
- Kafka location stream to Pricing service (Count free drivers)
- Pricing service to Connection gateway (Fare quotes)
- Connection gateway to Pricing service (Requests and price checks)
- Matching service to Trip database (Save the trip)
- Driver app: Sends a GPS ping every 4 seconds
- Rider app: Gets quotes, books, watches the car
- Connection gateway: Holds open links to every phone
- Location service: Checks, cleans and saves each ping
- Redis geo index: Latest spot of every free driver
- Kafka location stream: Every ping, kept for fares and surge
- Matching service: Pairs riders and drivers every 5 seconds
- Pricing service: Sets a surge multiplier per map cell
- Trip database: Trips, fares and driver records
The connection gateway keeps a long-lived connection open to each phone, so the server can push offers and car positions without the phone asking first. WebSockets, a standard for two-way messages over one open connection, are a common choice. A separate guide compares them with other live-update options.
How do drivers send their live location to the server?
The driver app sends a GPS ping every few seconds over an open connection, and the server keeps only the latest position for matching. Every ping also goes to a log for fares, safety and analytics.
The location service does three things with each ping. First, it drops any ping older than the last one it saved for that driver. Phones on weak networks often send pings late and out of order.
Second, it snaps the point to the nearest road, because raw GPS can drift into a building or across a river. Third, it writes the result in two places.
The latest spot goes into the geo index used by matching. Every ping also goes onto a Kafka topic, a log that keeps events for a set time, even after services have read them. The fare service and the pricing service each read that stream on their own.
The rider's app gets positions the other way. The gateway pushes the assigned driver's latest point every few seconds. The app then slides the car icon smoothly between points, so it does not jump.
How does a ride-hailing app find nearby drivers?
It keeps free drivers in a spatial index, a data structure built for questions about places, and asks it for drivers within a few kilometres of the rider. Checking every driver in the city on every request would waste work, because almost all of them are too far away.
Drivers sit in separate pools, one per city and vehicle type, such as guwahati:auto. An auto request then searches only free autos.
A common spatial index is the geohash. It turns a latitude and longitude into a short code, and nearby places usually share the same starting characters. Geohashes and quadtrees have their own guide, so here we simply use one.
The team uses Redis, an in-memory data store with geo commands built in. GEOADD stores each driver's point in a sorted set (a list kept in order by a number), using a 52-bit geohash as that number. GEOSEARCH returns the members inside a circle or box around a point, nearest first if you ask.
One detail catches people out. Redis can expire a whole key, and since version 7.4 single fields of a hash, but not one member of a sorted set. So a driver who goes home stays in the set until something removes them.
The fix has two parts. Each ping also refreshes a small alive key that expires after 20 seconds, and searches skip drivers without one. A sweeper job removes long-silent drivers from the set every few seconds. Together they fix the phantom auto from the opening.
How does Uber match a rider with a driver?
Uber's marketplace page says it first matched each rider with the closest driver, then moved to batched matching, because closest does not always mean quickest. In the seconds after a request, it weighs nearby riders and drivers in one batch to cut the average wait for everyone.
That is Uber's own public description as of 19 October 2026. The page does not say how long a batch lasts or which algorithm pairs them, so the numbers below belong to the example setup.
Rank by road time, not straight-line distance
GEOSEARCH measures distance as the crow flies. Guwahati has the Brahmaputra, hills, flyovers and one-way lanes, so 600 metres can mean 14 minutes. The team takes the nearest drivers and asks a routing service for each one's ETA (estimated time of arrival) by road.
That alone fixes the flyover complaint. Uber's engineering blog describes a similar split for its own ETAs in 2022. A routing engine adds up travel times along road segments using live traffic, and a learned model then corrects the gap to real trip times.
Pair a few seconds of requests at once
Matching each rider alone, the moment they book, is greedy: it gives the first rider the best driver, even when a later rider needed that driver more. Here is the smallest case where that goes wrong.
Two requests arrive
Rider A books at Ganeshguri. Two seconds later, rider B books near Six Mile. Two autos are free nearby: auto 1 and auto 2.
Road ETAs come back
Auto 1 is 2 minutes from A and 3 minutes from B. Auto 2 is 4 minutes from A and 11 minutes from B.
Pause and predict
Before reading on, guess which pairing gives the lower total wait, and which rider pays for it.
Closest-first matching
A booked first and gets auto 1, just 2 minutes away. B is left with auto 2, 11 minutes away. Total wait: 13 minutes.
Batched matching
The batch gives A auto 2 (4 minutes) and B auto 1 (3 minutes). Total wait: 7 minutes, and nobody waits more than 4.
What just happened? The batch gave rider A a slightly worse auto so that rider B avoided an 11-minute wait. Total wait fell from 13 to 7 minutes, and the longest single wait fell from 11 to 4.
Choosing pairs so the total cost is lowest is a classic problem called the assignment problem. You do not need to write a solver, because libraries such as SciPy include one. The engineer section runs it on this exact example.
How long should a batch be? Longer batches find better pairs, but every rider waits before matching even starts. This setup uses 5 seconds at peak, and matches at once when only one rider is waiting nearby.
| Closest-first | Batched (5 seconds) | |
|---|---|---|
| When it decides | The moment each rider books | Once per batch window |
| What it tries to lower | This one rider's pickup time | Total pickup time for the batch |
| Delay before a match | None | Up to 5 seconds |
| Average pickup wait | 6.5 minutes | 4.8 minutes |
How does surge pricing work in a ride-hailing app?
Surge pricing raises fares in a small area when ride requests outnumber free drivers there. The higher price makes some riders wait or walk, and it draws free drivers toward that area.
Uber's driver help page for Australia says surge happens when demand is very high and there are not enough cars. Its examples are bad weather, rush hour and special events (as of 19 October 2026).
Surge is specific to areas of a city, and it may show as a multiplier, an extra amount or an upfront fare, depending on the city. Drivers see surge areas shaded from light orange to dark red, where darker means a bigger earning opportunity.
Think of a pony stand at the foot of a hill shrine on the first day of a pilgrimage season. More pilgrims arrive than there are ponies, so the rate goes up. Some pilgrims walk or wait for the afternoon, and pony owners from villages lower down hear about the rate and come up.
- Pilgrims are riders, and those who walk or wait are demand that drops away.
- Ponies are free drivers, and owners climbing up from the villages are supply moving in.
- The stand is one map cell, and the rate board is the surge multiplier.
Where the picture breaks: at the stand everyone can see the queue. The app has to measure demand and supply in each cell from data, and a rule caps how high its rate can go.
Why prices are set per map cell
The pricing team needs small areas in which to compare demand and supply. Uber built a grid called H3 for jobs like this. Its 2018 engineering post says Uber calculates surge pricing by measuring supply and demand in hexagons in each city it serves.
H3 covers the earth with hexagons at 16 sizes, called resolutions. A sphere cannot be tiled with hexagons alone, so each resolution also has 12 pentagons. Uber's post says they are positioned so that all of them sit in water.
Hexagons help because, in a flat hexagon grid, all six neighbours sit the same distance from a cell's centre. With squares, corner neighbours are about 1.41 times farther away than side neighbours. Real H3 cells are slightly uneven: around Guwahati at resolution 8, the six distances run from about 930 to 970 metres.
The setup uses H3 resolution 8, where an average cell covers about 0.74 square kilometres. One cell is too small to judge alone, because a driver one street over may sit in the next cell. So the pricing job counts free drivers in the cell plus its six neighbours, about 5.5 square kilometres in all, because cells near Guwahati are a little larger than average.
Demand is ride requests, plus app opens that checked a price and did not book, counted once per rider. Leaving out those quiet give-ups would hide real demand. Demand is counted in the cell itself, while supply adds the six neighbours, because drivers there are about a kilometre away.
Here is the loop the pricing job runs every minute. Follow the two arrows that come back to the start: they show how a higher price changes demand and supply at the same time.
The surge loop
Drag to exploreRecomputed every minute for each map cell. The price acts on both riders and drivers.
Diagram connections:
- Ride requests and checks to Requests per free driver
- Free drivers nearby to Requests per free driver
- Requests per free driver to Ratio above 1.2?
- Ratio above 1.2? to Normal fare (No)
- Ratio above 1.2? to Set a capped multiplier (Yes)
- Set a capped multiplier to Riders see a higher quote
- Set a capped multiplier to Drivers see a heat map
- Riders see a higher quote to Ride requests and checks (Less demand)
- Drivers see a heat map to Free drivers nearby (More supply)
- Ride requests and checks: Demand, counted in the one H3 cell
- Free drivers nearby: Supply in the cell plus 6 neighbours
- Requests per free driver: Recomputed every 60 seconds
- Ratio above 1.2?: Below this, supply is fine
- Normal fare: Multiplier stays at 1.0x
- Set a capped multiplier: Moves at most 0.25x a minute, max 2.0x
- Riders see a higher quote: Some wait, walk or share
- Drivers see a heat map: Free drivers head toward the cell
The pricing team turns the ratio into a multiplier with simple bands.
| Requests per free driver | Multiplier | A Rs 120 auto ride costs |
|---|---|---|
| Below 1.2 | 1.0x (normal) | Rs 120 |
| 1.2 to 1.8 | 1.25x | Rs 150 |
| 1.8 to 2.5 | 1.5x | Rs 180 |
| 2.5 to 3.5 | 1.75x | Rs 210 |
| Above 3.5 | 2.0x (the cap) | Rs 240 |
At 5 pm on a Mela day, the cell at the foot of Kamakhya hill gets 90 requests in five minutes. The cell and its six neighbours hold 24 free drivers. That is 3.75 requests per free driver, so the multiplier climbs toward the 2.0x cap.
Two rules stop the price from jumping around. The multiplier moves at most 0.25x per minute, up or down. A quote stays locked for 3 minutes, so a rider who books in that window pays what they saw.
Without surge, the app had hit what Castillo, Knoepfle and Weyl (2017) call a wild goose chase. When free drivers are too few and spread thin, the platform must send them to distant riders.
Those long empty drives keep drivers busy, so fewer rides are served and drivers earn less. Some drivers then stop driving, which makes the shortage worse. Surge breaks the loop by raising prices only at peak times.
In India, the cap is not only a product choice. The Motor Vehicles Aggregator Guidelines 2025 from the road transport ministry allow dynamic fares up to twice the base fare, which the state government sets. In July 2025, states were advised to adopt them within three months, so check your state's current rules (as of 19 October 2026).
How do you build the geo index and the batch matcher in code?
Use one Redis sorted set per pool for the geo index, and an assignment solver for each batch. Both samples below ran on Python 3.13 with redis-py 8.1.0, Redis server 7.0.15 and SciPy 1.18.1, tested in October 2026.
Store and search free drivers in Redis
In the Python below, r is a Redis client. save_ping runs for every ping from a free driver, and nearby_free runs when a rider books. GEOSEARCH needs Redis 6.2 or later.
import time
import redis
r = redis.Redis(decode_responses=True)
def save_ping(pool, driver_id, lon, lat):
r.geoadd(f"free:{pool}", (lon, lat, driver_id)) # longitude first
r.set(f"alive:{driver_id}", int(time.time()), ex=20) # gone after 20 s
def nearby_free(pool, lon, lat, radius_km=3, limit=10):
hits = r.geosearch(f"free:{pool}", longitude=lon, latitude=lat,
radius=radius_km, unit="km", sort="ASC",
count=limit * 3, withdist=True)
live = [(driver, km) for driver, km in hits
if r.exists(f"alive:{driver}")]
return live[:limit]Three lines matter. In geoadd, longitude comes before latitude, the opposite of how most people say coordinates. The alive key expires after 20 seconds, so a driver who misses about five pings drops out of the results.
The search asks for three times as many drivers as it needs. COUNT applies before the alive filter, so asking for exactly 10 could return none if the 10 nearest had all gone silent. In our test, three silent drivers emptied a search for 3 until we made this change. Three times is a margin, not a guarantee: nine silent drivers still emptied that search. The sweeper keeps silent drivers rare, and a short list should trigger a wider search.
A swapped longitude and latitude fails loudly in Guwahati, because 91.8 is not a valid latitude. In Delhi it does not. Redis 7.0.15 accepted Delhi's latitude 28.6 as a longitude and stored the point in the Arctic, with no error.
You can check the index by hand with redis-cli, the Redis command-line client. Add two autos, then search 3 km around a rider standing near the first one.
redis-cli GEOADD free:guwahati:auto 91.7890 26.1500 auto:1 91.8000 26.1350 auto:2
redis-cli GEOSEARCH free:guwahati:auto FROMLONLAT 91.7900 26.1510 BYRADIUS 3 km ASC WITHDISTThe first command replies (integer) 2, the number of drivers added. The search prints auto:1 at 0.1496 and then auto:2 at 2.0407, nearest first. Distances come back in the unit you searched with, here kilometres.
Solve the batch with an assignment solver
SciPy's linear_sum_assignment takes a grid of costs, one row per rider and one column per driver, and returns the pairs with the lowest total. In the code, eta_minutes stands in for a call to the routing service.
from scipy.optimize import linear_sum_assignment
TOO_FAR = 999 # minutes: marks a pair we never want
def match_batch(riders, drivers, eta_minutes, max_eta=12):
cost = []
for rider in riders:
row = [eta_minutes(driver, rider) for driver in drivers]
cost.append([m if m <= max_eta else TOO_FAR for m in row])
rows, cols = linear_sum_assignment(cost)
return [(riders[i], drivers[j], cost[i][j])
for i, j in zip(rows, cols) if cost[i][j] < TOO_FAR]
# The two-rider example from the steps above, ETAs in minutes
etas = {("auto 1", "A"): 2, ("auto 1", "B"): 3,
("auto 2", "A"): 4, ("auto 2", "B"): 11}
print(match_batch(["A", "B"], ["auto 1", "auto 2"],
lambda d, r: etas[(d, r)]))It prints A with auto 2 at 4 minutes and B with auto 1 at 3 minutes, the same 7-minute total as the worked example. Closest-first on the same numbers gives 13 minutes.
The TOO_FAR swap marks any pair over 12 minutes as one the solver should avoid. The solver still returns as many pairs as it can, even very bad ones. So the last line drops those, and that rider waits for the next batch.
How do you stop two riders from getting the same driver?
Give each driver a state, and change it from free to offered in one atomic step that fails if someone else got there first. Atomic means the check and the change happen as one action that nothing can split.
The Paltan Bazaar bug happened because two matching workers read the same auto as free, then both sent offers. Reading in one step and writing in another leaves a gap between them.
The fix is a claim key in Redis, written with SET and its NX and EX options. NX writes the key only if it does not exist yet. EX makes it expire on its own, here after 15 seconds.
RELEASE = r.register_script(
"if redis.call('GET', KEYS[1]) == ARGV[1] then "
"return redis.call('DEL', KEYS[1]) else return 0 end")
def claim(pool, driver_id, rider_id, seconds=15):
# SET with NX and EX: one caller wins, and the key expires
won = bool(r.set(f"claim:{driver_id}", rider_id, nx=True, ex=seconds))
if won:
r.zrem(f"free:{pool}", driver_id) # out of searches for now
return won
def release(driver_id, rider_id):
# delete the claim only if it still belongs to this rider
return RELEASE(keys=[f"claim:{driver_id}"], args=[rider_id]) == 1In our test, 50 threads claimed the same auto at once and exactly one won. If the driver ignores the offer, the key expires and the driver can be offered again.
Release needs the same care. Suppose worker 1's claim expired and worker 2 now holds the driver. A plain delete from worker 1 would wipe worker 2's claim, and our test showed exactly that. The script deletes only when the value is still your rider id, the safe release pattern shown on the Redis SET page.
Here are the states a driver moves through. A winning claim also removes the driver from the free set with ZREM, so only drivers in Free get offers. Their next ping as a free driver adds them back.
A driver's states
Drag to exploreEach state change is one atomic step. A missed offer sends the driver back to Free.
Diagram connections:
- Offline to Free (Goes online)
- Free to Offered (Picked by matching)
- Offered to Driver accepted?
- Driver accepted? to Going to pickup (Yes)
- Driver accepted? to Free (No or timed out)
- Going to pickup to On trip (Rider gets in)
- On trip to Free (Trip ends)
- Offline: App closed, in no index
- Free: In the geo index, can get offers
- Offered: Claimed for one rider for 15 seconds
- Driver accepted?: Tapped Accept before the claim expired
- Going to pickup: Rider watches the car move
- On trip: Every ping kept for the fare
The trip itself is written inside a database transaction, a group of writes that all succeed or all fail. That transaction checks that the driver is still offered to this rider. The Redis claim is a fast first gate, and the database has the final word.
This split matters because the claim is not a strong lock. The Redis SET page discourages this simple lock pattern in favour of Redlock, an algorithm that takes the lock on most of several independent Redis servers. Redis also copies writes to its replicas asynchronously, a moment later. So a claim written just before a failover, when a replica takes over from a failed main node, can be lost.
Riders cause duplicates too. On a slow network, a rider may tap Book twice. The app sends an idempotency key, a unique id for each booking attempt, so the server creates one trip, not two. Idempotency keys have their own guide.
Here is the whole booking path, with matching and claims together. Find the loop at the end: a declined offer goes back to the next best driver, not back to the start.
What happens when a rider taps Book
Drag to exploreOne booking, step by step. Each box says which part talks to which.
Diagram connections:
- Rider taps Book to Lock the fare quote
- Lock the fare quote to Find free drivers nearby
- Find free drivers nearby to Rank by road ETA
- Rank by road ETA to Pair the 5-second batch
- Pair the 5-second batch to Claim driver, send offer
- Claim driver, send offer to Accepted within 15 s?
- Accepted within 15 s? to Create the trip (Yes)
- Accepted within 15 s? to Release claim, try next (No)
- Release claim, try next to Claim driver, send offer (Next best driver)
- Rider taps Book: Rider app → Gateway
- Lock the fare quote: Gateway → Pricing service
- Find free drivers nearby: Matching → Redis geo index
- Rank by road ETA: Matching → Routing service
- Pair the 5-second batch: Matching → Assignment solver
- Claim driver, send offer: Matching → Driver app
- Accepted within 15 s?: Driver app → Matching
- Create the trip: Matching → Trip database
- Release claim, try next: Matching → Redis geo index
How does a ride-hailing app scale across many cities?
It splits the work by geography, because a rider in Guwahati never needs a driver in Shillong. Each city gets its own geo pools, matching workers and pricing jobs.
This is sharding: splitting data across machines by a key, here the city. A festival rush in one city then cannot slow down another. Inside a very large city, the split can follow map cells instead.
Borders need care. A rider near the edge of one shard may need drivers from the next one, so the search also asks the neighbouring shard.
The team also writes down what happens when each part fails:
- A geo index node dies: a replica takes over, and fresh pings fill any gap within seconds. Claims lost in the switch are caught by the database check.
- A matching worker dies mid-batch: its claims expire after 15 seconds, so no driver stays stuck in Offered.
- A driver's phone drops network during a trip: the app saves pings on the phone and sends them later, and the fare uses that trail.
- The Kafka stream falls behind: matching keeps working, because it reads the geo index. Surge holds its last good multiplier.
The trip database is different. A trip means money and safety, so it lives in a database with transactions and replicas, and every state change is logged.
How do you explain Uber system design in a system design interview?
Tell it as a story: requirements, rough numbers, the main parts, and then deep dives on location, matching, surge and double booking.
Ask about scope
Confirm riders, drivers, vehicle types and cities. Ask whether payments and ratings are in scope, or only dispatch.
Do the maths out loud
Drivers online divided by seconds between pings gives location writes per second. Compare it with bookings to show the system is write-heavy for locations.
Draw the main parts
Rider and driver apps, a connection gateway, a location service, a geo index, matching, pricing, a trip database and a location log.
Deep dive on location
Latest position in memory, every ping in a log. Expire silent drivers with a key and a sweeper. Explain the spatial index in one line.
Deep dive on matching
Straight-line candidates, ranking by road ETA, a short batch and an assignment solver. Show the two-rider case.
Deep dive on surge
Demand and supply per map cell, a capped and smoothed multiplier, and a locked quote.
Close with double booking and failures
An atomic claim with a timeout, checked again in the trip transaction. Then say what happens when a node, a worker or a phone fails.
Questions people ask
- How often does a ride-hailing driver app send its location?
- Usually every few seconds while the driver is online. The setup in this guide uses 4 seconds, which means 600 writes a second for 2,400 drivers. A shorter interval gives a fresher map, but costs more writes on the server and more battery on the phone, so teams tune it.
- Why not store driver locations in a normal SQL database?
- You can at small scale, but it is a poor fit. Matching needs one constantly overwritten position per driver and fast radius searches. An in-memory geo index does both cheaply. Keep the SQL database for trips, payments and driver profiles, which must last.
- Is geohash or H3 better for a ride-hailing app?
- They answer different questions, and one system can use both. Geohash is simple and built into Redis, so it suits nearby searches. H3's hexagons have nearly equal neighbour distances, which makes them good for comparing areas, such as surge zones. Uber's 2018 post says it measures surge in hexagons.
- How does a ride-hailing app calculate ETA?
- It finds the fastest route on a graph of the road network and adds up the travel time of each road segment, using live traffic. Uber's 2022 DeepETA post says a learned model then predicts the gap between that routing estimate and real trip times, trained on past trips and live signals.
- Is surge pricing allowed in India?
- Yes, within limits. The Motor Vehicles Aggregator Guidelines 2025 from the road transport ministry allow dynamic fares up to twice the base fare, which each state sets. States were advised in July 2025 to adopt them within three months, so check the current rules where you operate (as of 19 October 2026).
- What happens if a driver's phone loses network during a trip?
- The trip continues, and the app keeps recording GPS points on the phone. When the network returns, it sends the saved points with their original times. The server adds them to the fare trail but ignores them for matching, because they are already old.
Sources and further reading
- How matching works, Uber Marketplace (closest-first then batched matching)
- H3: Uber's Hexagonal Hierarchical Spatial Index, Uber Engineering Blog (27 June 2018)
- How surge pricing works, Uber driver help (Australia)
- DeepETA: How Uber Predicts Arrival Times Using Deep Learning, Uber Engineering Blog (10 February 2022)
- Surge Pricing Solves the Wild Goose Chase, Castillo, Knoepfle and Weyl, NBER conference paper (July 2017)
- GEOADD command, Redis documentation (argument order, 52-bit geohash scores)
- GEOSEARCH command, Redis documentation
- SET command, Redis documentation (NX, EX and the lock pattern)
- HEXPIRE command, Redis documentation (hash field expiry, since 7.4)
- Redis replication, Redis documentation (asynchronous by default)
- Introduction, Apache Kafka documentation (retention, events kept after reading)
- Govt allows cab aggregators to charge up to 2 times of base fare during peak hours, Outlook Business (2 July 2025)