Yelp
Design nearby business search with text, category, and geographic filters.
Interview scope and guarantees
Find nearby businesses, filter by category, retrieve details, and submit reviews. Define radius, ranking and result freshness. A location candidate is not necessarily inside the radius; final geometry must be checked. Review editing and business ownership are separate authorization paths.
Capacity worksheet
Assume 10 million searches/day: 116/s average and 1,157/s at 10× peak. With 1,000 candidate businesses per query, that peak already evaluates about 1.16 million candidate distances/s. Density, not just total business count, determines query cost.
Concrete API contract
GET /businesses?lat=...&lon=...&radius=...&category=...&cursor=...
GET /businesses/{id}
POST /businesses/{id}/reviews {rating,text}
PUT /reviews/{id} {expectedVersion,rating,text}Data model and access paths
businesses(id PK,position,category,status,version)
reviews(id PK,business_id,user_id,rating,version)
geo_entries(cell_id,business_id,position)
rating_totals(business_id,sum,count,projection_version)
business_categories(business_id,category_id); PK(business_id,category_id)
opening_intervals(business_id,time_zone,weekday,start_local,end_local)
exceptional_hours(business_id,date,intervals)Evolve a solution and explain each change
Figure — Three architecture decisions for Yelp, including the pressure each introduces.
Step 1: Filter nearby records
Store businesses and query a coarse spatial region. A rectangular or cell candidate region includes false positives.
Step 2: Refine distance and filters
Check exact distance, category, hours, and query semantics before ranking. A cell boundary can hide a closer business unless neighbors are considered.
Step 3: Scale the search projection
Use an indexed read model and caches with an explicit freshness policy. Ranking caches can preserve closed or removed businesses.
Responsibility overview
Figure — Connected responsibilities for Yelp. Trace the authoritative and derived paths separately.
Worked end-to-end scenario
A user searches for open cafés within three kilometres near the edge of a spatial cell. Candidate discovery includes the intersecting neighboring cells, not just the containing cell. Exact distance then removes businesses outside the radius. Opening-hours filtering uses the business timezone and exceptions, not the user device timezone. Rank the survivors using a defined combination of distance and relevance. A newly closed café can remain in a stale index; decide whether the detail page rechecks authoritative availability or whether a bounded delay is acceptable. Separate the search result’s approximate ranking from facts users rely on when travelling.
Why these access paths matter
Businesses have stable IDs and coordinates; index spatial cells plus category/status filters. Reviews are stored separately with business and author access paths. Cache by normalized search region and filters, with care for time-dependent open-now results. Pagination needs a deterministic tie-breaker and preferably a snapshot for changing scores.
Build the baseline first
- Search → intersecting cells → candidate businesses.
- Exact distance and filters → ranking → page.
- Review write → durable record → rating projection.
Evolve the design under load
- Dense cells → finer resolution or spatial tree.
- Hot-area candidate cache → current status checks.
- Search replicas → bounded result candidates.
Defend the hardest decision
A geohash or grid is a candidate retrieval mechanism. Enumerate all cells intersecting the search circle, deduplicate businesses, calculate exact distance, then filter. Searching only the user’s cell misses a nearby business across a boundary. A nearest-K query may need progressive radius expansion with a stopping condition; stopping after the first nonempty cell is incorrect.
Failure and recovery analysis
A business closes but remains in the index. Versioned updates and tombstones prevent stale replays from restoring it; current status filtering protects responses during index lag. Rating edits must apply the difference between old and new contributions rather than increment count each time. Snapshot pagination prevents changing scores from causing excessive duplicates.
Security and privacy boundary
Protect reviewer identity where required, authorize business edits, and reject abusive review automation.
Interview follow-ups with reasoning
Question: How do you handle dense downtown areas?
Show answer and explanation
Answer: Refine cells and cap candidates while preserving the documented ranking contract.
Question: How do you update an address?
Show answer and explanation
Answer: Remove the old cell membership and add the new version idempotently.
Question: What if the geospatial index is lost?
Show answer and explanation
Answer: Rebuild it from authoritative coordinates.
Operate and verify the design
Candidate count, exact-distance cost, index lag and stale-business filtering.
Move a business across a cell boundary while queries run and verify no permanent duplicate or omission.
A second scenario to test transfer
A user stands ten meters west of a cell boundary. The nearest cafe is twenty meters east of that boundary. Searching only the user’s cell misses it even though another cafe in the same cell is a kilometer away. Query neighboring intersecting cells, then reject points outside the actual circular radius. The grid reduces work; exact filtering preserves the query’s meaning.
What evidence would show that the search index is causing poor nearby results?
Show answer and explanation
Answer: Measure candidate recall near cell boundaries, exact-filter rejection rate, index lag, empty-result rate, and query latency. Compare against a small authoritative distance calculation on representative edge cases, including the dateline when supported.
Compare alternatives
| Stage | Role | Caution |
|---|---|---|
| Spatial index | Reduce candidate set | Cell boundaries need neighbors |
| Exact distance | Honor radius | Coordinate system matters |
| Ranking | Order useful results | Stable pagination policy |
| Source record | Current truth | Search may lag |
A design-changing exercise
The nearest business lies across a cell edge. How do you avoid missing it?
Show answer and explanation
Answer: Query all cells intersecting the search region, then refine by exact distance. One containing-cell lookup is not a complete radius search.
Design workshop: exact radius and nearest-K are different queries
Core scope is nearby business search, category/hours filters, details and reviews. Personalized advertising is excluded. Assume p95 under 200 ms for a bounded radius, business updates searchable within one minute and current owner authorization for edits. Density determines candidate cost, so cap radius and page size explicitly.
Choose PostGIS geography with a spatial GiST index for the initial design rather than hand-building geohash traversal. Store a point using longitude/latitude in that order and meters for geography distance. Add business_categories(business_id,category_id), opening_intervals(business_id,time_zone,weekday,start_local,end_local) and exceptional_hours(date,intervals). A weekly local schedule must apply the business timezone and exceptions; a UTC-only daily interval mishandles clock changes.
SELECT id, ST_Distance(position, :query_geography) AS meters
FROM businesses
WHERE status='active'
AND ST_DWithin(position, :query_geography, :radius_meters)
ORDER BY meters, id
LIMIT :page_limit;This radius query is exact under its chosen geometry, not a proof of arbitrary globally nearest results. If scaling to a cell projection, enumerate cells intersecting the search circle, collect candidates and filter exact distance. For nearest K, visit cells by a valid minimum-distance bound. Stop only after K eligible candidates exist and every unvisited cell's lower bound is farther than the current Kth result. A popularity-ranked query cannot use a distance-only stopping proof if distant candidates can win by popularity.
Figure — Nearest-2 expansion in a flat teaching coordinate system.
For small distances a flat local projection can explain the example; production uses declared geodesic semantics, dateline handling and a tested library. A dense downtown cell may contain thousands of businesses: use adaptive precision or the indexed radius query and enforce bounded work. A signed query snapshot/cursor pins filters and ordering; current active status is rechecked before hydration.
Figure — Review edits update aggregates once.
Each review has one owner and optionally unique(business,user) if the product permits one active review per user. Projection applies ordered review versions transactionally, or recomputes from current source on a version gap. New review increments sum and count; edit changes only sum; deletion subtracts prior rating and count. Stale events cannot undo a newer edit. Monitor boundary recall, candidates scanned, exact-filter rejection, opening-hour errors and aggregate reconciliation.
Exercise: Two reviews at 2 and 4 give average 3. The first becomes 5 and its event repeats. What is the result?
Show answer and explanation
Answer: Sum becomes 9, count remains 2, average is 4.5. Applying +3 twice would incorrectly produce 6. A per-review version/identity or recomputation is needed.
PostGIS ST_DWithin documents indexed distance filtering and geometry/geography units.