Nearby places
Design a service that answers "what is near me?" (restaurants, shops, stations), ranked by distance and quality, filtered by category and whether they are open now.
Where’s the slack?
Query: ~200 ms. Place data freshness: hours. Opening hours: minutes.Two hundred milliseconds to answer, and the underlying data barely changes. That makes this one of the most cacheable problems on the site, and one of the easiest to over-engineer.
The query budget is a normal mobile-app budget: about 200 ms, because the user has tapped a button and is looking at a loading state. Comfortable compared to typeahead, tight compared to a batch job.
The freshness slack is where the design comes from. Restaurants do not move. A place's location, name and category change perhaps once a year. Ratings shift slowly. So almost all the data behind an answer is effectively static, and users could not tell an index rebuilt daily from one rebuilt continuously.
There is one fast-moving field mixed into an otherwise static dataset: whether somewhere is open right now. That changes on a schedule, plus irregular closures. It is tempting to let that one field force the whole system to be dynamic. Resisting that is the main design skill this problem teaches. Compute openness at read time from static opening hours, and keep the index static.
The other dynamic case is live availability: a table free tonight, a bike at a dock. If that is in scope it is a separate fast path, not a reason to make the place index live.
So: Static data plus a hard query budget means aggressively precomputed and cached geographic tiles. The one dynamic field is computed at read time from static data rather than being allowed to invalidate everything.
Work it through
Five stages, gated. You write your answer for each stage before its model answer unlocks. That is the core of the method.
Reference material for this problem. The diagrams below are here to check your own against once you have drawn it, not to read first.
The lifecycle
The numbers
So the hard part is density, not volume. The same query costs a thousand times more downtown than in a village. That is why cell precision has to adapt, and why fixed-radius designs fail at one end or the other.
The architecture
The mechanism
The decision this turns on
- —Simple: a string prefix, so an ordinary B-tree index answers proximity.
- —Nearby things usually share a prefix, so each cell lookup is a range scan.
- —Boundaries are arbitrary, so you must always query the eight neighbours.
- —One fixed cell size is wrong somewhere: too big downtown, too small in the countryside.
- —Cells subdivide where density is high, so candidate counts stay even.
- —Handles the city-versus-countryside problem natively.
- —A tree to build and maintain, rather than a string you compute.
- —More work when points move, which places rarely do.
Follow-ups you should expect
Many interviews are decided in the follow-ups, not the first diagram.
Same shape, other products
Two dimensions pretending to be one, plus a very high write rate from things that move.
Writes hugely outnumber reads, positions are stale the moment they land, and the index gives you candidates rather than an answer.
Others with this shape: Nearby places · Delivery zones · Live location ingestion
How it is really built
Write your own answer first. Then read how teams who run this at scale describe it. Their constraints were real, and the write-ups often say what their choices cost.
- H3 — indexing the globe ↗Turning two coordinates into one indexable cell id.