A rider requests a car and forty drivers are within range. Which one do you pick, and how long do you have to decide?
Ride matching system design should rank drivers by estimated pickup time and market impact, not straight-line distance. Offer the ride under a short soft lock, use a small batching window when it improves assignment quality, and keep the rider wait within a few seconds. Use this dispatch answer to show the decision, trade-off, and evidence rather than a memorised definition.
What the interviewer is scoring
- Does the candidate distinguish finding candidates from ranking them, and give each a different cost budget
- Whether pickup time rather than straight-line distance is proposed as the ranking signal
- That a driver is soft-locked while an offer is outstanding, so two riders cannot be offered the same car
- Whether declines and silence are treated as the normal case rather than as errors
- Can they argue both sides of batching requests versus dispatching each one immediately
Answer
Short answer
Estimate pickup time, rank candidates with supply impact, reserve one driver briefly, and fall back quickly when they decline or time out so the rider does not wait on stale choices.
Finding forty is cheap, choosing one is the product
The spatial index already did its job. It turned tens of thousands of drivers into forty candidates in a few milliseconds, and it did so using straight-line proximity because that is the only thing a geospatial key can express. Now the expensive question starts, and it is a different kind of question.
The reflex answer is nearest. It is wrong often enough to matter. A driver eight hundred metres away on the far side of a railway line, facing the wrong direction on a dual carriageway, may be six minutes from the rider. A driver two kilometres away on the same road heading towards them may be three. What the rider cares about is time to pickup, and time to pickup is a routing computation, not a distance.
So the pipeline has two stages with different budgets. Candidate generation is cheap and approximate, and it can afford to be run on every request. Ranking is expensive and it runs over dozens, not thousands. Send forty candidates to a routing service, get forty estimated pickup times back, and you have paid for accuracy exactly where it changes the decision. A strong candidate says the split out loud: "The index finds, the router ranks, and I only ever ask the router about a few dozen cars."
The clock the rider is watching
The budget is set by a human staring at a screen. A few seconds of spinner is normal, ten feels broken, and thirty means they open a competitor. Everything below has to fit inside that.
Break it down. Candidate lookup is milliseconds. Batch routing for forty candidates is a few hundred milliseconds if you call one service once rather than forty times. Ranking is arithmetic. That leaves the real consumer of the budget: waiting for a driver to accept.
An offer is not a decision, it is a proposal to a person who may be parking, talking, or ignoring their phone. So the offer carries a timeout of somewhere around ten to twenty seconds, and after it lapses you move to the next candidate. Two declines in sequence and you are already past the rider's patience, which is why the rider-facing experience has to be decoupled from the offer loop: the rider sees "finding your driver" while the system works through candidates, and only learns the outcome once.
An outstanding offer is a lock, and this is where correctness lives
While a driver is considering an offer they must not be offered to anyone else. Two riders both offered the same car is the same class of bug as selling one seat twice, and it produces the same visible failure: one rider watches a driver they were promised drive somewhere else.
So an offer writes a soft lock on the driver, with an owner and an expiry, claimed by a conditional operation that either succeeds or tells you the driver is taken. Not a read followed by a write. The expiry is what makes it safe against a dispatcher crashing mid-offer, because the lock lapses on its own rather than stranding a driver who is available and invisible.
The states a driver moves through are worth naming: available, offered, assigned, on trip, and then available again. Only available drivers enter candidate generation. That means the index has to reflect the lock quickly, or your candidate list keeps proposing cars that are already committed, and every dispatch wastes an offer discovering it.
Greedy per request, or a short batching window
Here is the trade-off an interviewer is usually fishing for. Dispatching each request the moment it arrives is greedy: give this rider their best driver. It is simple, it is fast, and it is measurably worse for the market as a whole.
Consider the smallest case that shows it. Two riders, A and B, and two drivers. Driver one is two minutes from A and three minutes from B. Driver two is nine minutes from A and twenty from B, because B is out past a bridge. Handle A first and greedily give them driver one: A waits two minutes, B is left with driver two and waits twenty, so the total is twenty-two. Pair them the other way and A waits nine while B waits three, for a total of twelve. The greedy choice was optimal for the rider who happened to ask first and cost the pair ten minutes.
Batching makes that visible. Hold requests for a few seconds, collect the drivers and riders in the window, and solve it as an assignment problem over the resulting cost matrix rather than one row at a time. With dozens of riders and dozens of drivers this is small enough to solve exactly, and it consistently beats greedy on average wait.
What batching costs is honesty about latency. Every rider now waits for the window before anything happens, and a rider at three in the morning with one driver nearby waits for no benefit at all. The usual resolution is to make the window adaptive: near zero when supply is plentiful relative to demand, a few seconds when the market is dense enough that pairing choices exist. Whether riders prefer it is a question for an experiment, not for an architecture diagram.
What else is in the cost function, and what should not be
Pickup time is the base, and a few other terms are defensible. A driver approaching the end of their shift should not be sent on a long airport run. A vehicle class or accessibility requirement is a hard filter, not a weight. Trip length balance across drivers keeps the supply side willing to work. Direction of travel matters, because a driver already pointed the right way avoids a turn that adds minutes.
Two things are worth being careful about. Ranking by acceptance rate seems reasonable and quietly builds a system where declining once costs a driver income, which is a policy decision disguised as a scoring tweak. And optimising for revenue per assignment rather than for wait time produces choices that are hard to explain to either side of the marketplace. Whatever the terms, record them with the assignment. When a driver asks why they did not get a trip they were nearest to, the answer needs to exist.
The failure that looks like nothing is wrong
The one to walk through is the cascade of silent declines. Supply is thin, the top candidate ignores the offer, the second is driving and does not look, the third declines. Ninety seconds have passed, the rider is still watching a spinner, and no component has logged an error. Dispatch is working exactly as designed and the outcome is a rider who leaves.
Three things address it. Widen the radius as candidates are exhausted, so the search relaxes over time instead of failing at the original bound. Cap total dispatch time and tell the rider the truth when it runs out, because "no cars available right now" is a better product than an endless spinner. And send offers to two or three drivers at once when supply is thin, accepting that a race between them wastes a driver's tap, because a wasted tap is cheaper than a lost trip. That last one is a deliberate exchange of driver experience for rider conversion, and saying so is better than pretending it is free.
Candidate generation is a spatial problem and assignment is an economics problem, and the second one is bounded not by compute but by how long a person will look at a spinner before deciding your product does not work.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- Four drivers all decline in sequence and ninety seconds have passed. What do you change about the fifth offer?
- A batching window of five seconds improves assignment quality. How would you measure whether riders prefer it to being matched instantly?
- Two riders in the same street request at the same instant and the same driver is best for both. Where is that conflict resolved, and what does the loser get?
- How do you stop a driver gaming the system by declining everything except long airport runs?
Related questions
- An asyncio call times out and you handle the TimeoutError, but the background task keeps running and mutates shared state a few seconds later. What happened, and how do you make the timeout actually stop the work?hardAlso on timeouts and concurrency4 min
- A dependency that normally answers in 80ms starts taking eight seconds. What in your service reacts, and in what order?hardAlso on timeouts7 min
- How would you design a thread-safe component, and why is adding synchronized to every method not a design?hardAlso on concurrency7 min
- A family plan shares 100 GB across five SIMs with each SIM capped at 30 GB. How does charging enforce both limits at once?hardAlso on concurrency6 min