Yelp / Nearby Places#
Problem statement (interviewer prompt)
Design Yelp / Nearby Places: users search businesses by category + location, view details (hours, photos, reviews, ratings), and write reviews. Geo-sharded indexing must return ranked nearby results in <300ms p99 across 200M+ places globally.
flowchart LR
U([User])
SRCH[Search]
PSVC[Places]
REV[Reviews]
GEO[Geo Index]
IDX[(Search index)]
U --> SRCH --> IDX
SRCH --> PSVC
SRCH --> GEO
U --> REV
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class U client;
class SRCH,PSVC,REV,GEO service;
class IDX datastore;
flowchart TB
subgraph Clients
APP[App]
WEB
end
subgraph Edge
CDN
GW
end
subgraph Places
PSVC[Place Service]
PDB[(Places SQL)]
IMG[Photos / S3]
HOURS[Hours / Status]
end
subgraph Geo
GIDX[(Geo index<br/>quadtree / geohash / S2)]
NEAR[Nearby query]
BBOX[Bounding box]
KNN[k-NN search]
end
subgraph Search
SRCH[Search Service]
INV[(Inverted index<br/>name + category + amenities)]
RANK([Relevance ranker<br/>distance + ratings + popularity])
AUTOC[Autocomplete]
end
subgraph Reviews
RSVC[Review Service]
RDB[(Reviews)]
PHOTO[Photo Service]
SPAM([Spam classifier])
MOD[Moderation]
AGG([Rating aggregator<br/>Bayesian average])
end
subgraph Engage
CHECK[Check-in]
BOOK[Reservation integration]
NOTIF[Notifications]
end
Clients --> CDN --> GW
GW --> Places
GW --> Search --> Geo
Search --> INV
Search --> RANK
GW --> Reviews
Reviews --> SPAM --> MOD --> RDB
Reviews --> AGG --> PDB
GW --> Engage
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class APP,PSVC,HOURS,NEAR,BBOX,KNN,SRCH,AUTOC,RSVC,PHOTO,MOD,CHECK,BOOK,NOTIF service;
class PDB,GIDX,INV,RDB datastore;
class RANK,SPAM,AGG compute;
class IMG storage;
Geo search#
- Quadtree / geohash / S2 index for spatial queries.
- Nearby: walk parent cells outward until k results found.
- Bounding-box: directly query cells overlapping the box.
Ranking#
- Combines distance, star rating (with min-review threshold), photos, recency, paid promotion.
- Personalization on top: user history, similar users.
Anti-spam reviews#
- ML classifier on text + behavioral signals.
- Hide vs filter: filtered reviews exist but not surfaced; transparent UX.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
CDN | edge caching for static assets | cdn |
HLD |
Geo indexing | Geohash, Quadtree, S2, H3, R-tree | geo-indexing |
HLD |
Search internals | inverted index, BM25, embeddings, ANN | search-internals |
Quick reference#
Functional#
- Search places by category + name + location.
- Place detail (hours, photos, reviews, menus).
- Write review, upload photo.
- Check-in, reservations integration.
- Owner tools.
Non-functional#
- p99 nearby search < 300 ms.
- 99.95% availability.
- Read-heavy (50:1).
Capacity#
- 200M+ places globally; 200M+ reviews.
- Photos in TB-PB range.
Schema#
places(id, name, geo, category, hours, owner_id, rating_avg, review_count)reviews(id, place_id, user_id, text, rating, photos[], ts, is_filtered)geo_index(cell_id, [place_id])
Trade-offs#
- Quadtree vs geohash vs S2: S2 is sphere-aware, popular now.
- Filtered reviews: trust vs author goodwill.
- Personalization vs objective ranking: hybrid scoring.
Refs#
- Yelp engineering blog (Mussel, search, Spam Filter).
- Google S2 docs; Uber H3 hexes.
- ByteByteGo "Design Yelp".
FAQ#
How does Yelp search businesses in under 300 ms?#
Listings are indexed in a geo capable search engine, sharded by region. Queries combine geo radius, category, price, and ratings with a learning to rank model on top.
How are review ratings aggregated?#
Each new review updates a per business running average and a histogram of star counts. A separate quality score weights reviews by recency, reviewer reputation, and verified visit signals.
How does Yelp store and serve photos?#
Photos go straight from clients to object storage via presigned URLs. A pipeline generates thumbnails at fixed sizes and a CDN fronts both originals and thumbs for low latency reads.
How does Yelp fight fake reviews?#
A trust and safety pipeline scores reviews on writer history, IP reputation, language patterns, and review velocity. Suspected fakes are filtered out of the public ranking and routed for manual review.
How does Yelp recommend businesses to users?#
A ranker combines query relevance, distance, ratings, and personalization based on prior visits and reviews. Cold start users see popular venues filtered by their location and broad category preferences.