Designing Tinder
A location-based matching app — recommending nearby profiles via geospatial indexing, storing billions of swipes, and detecting mutual-like matches efficiently.
The problem
Design Tinder: users see a stack of nearby candidate profiles, swipe left/right, and match when two people swipe right on each other. The interesting parts are geospatial recommendation (who to show), swipe storage at huge volume, and efficient mutual-match detection.
Step 1 — Requirements
Functional: build a profile (photos, bio, preferences); get a feed of nearby candidates matching preferences (age, distance, gender); swipe left/right; create a match on mutual right-swipe; chat after matching.
Non-functional: low-latency profile feed, huge swipe volume (billions/day — write-heavy), scalable geospatial queries, available; eventual consistency mostly fine.
Step 2 — Recommendation: nearby candidates
The core query: “active users near me, within distance D, matching my preferences, whom I haven’t swiped.” Plain lat/long filtering doesn’t scale — use a geospatial index:
- Geohash — encode lat/long into a string prefix; nearby points share prefixes, so a prefix range query finds candidates in a cell (and neighbors). Index users by geohash.
- Quadtree / S2 cells / R-tree — alternative spatial partitions.
- Filter the spatial candidates by preferences and exclude already-swiped users.
Precompute/cache each user’s candidate deck so swiping is instant; refill in the background. (Geospatial indexing recurs in Yelp/Uber.)
Step 3 — Storing swipes (write-heavy)
Billions of swipes/day → a write-heavy problem:
- Store swipes in a horizontally-scalable store sharded by the swiper (
swiper_id → {target_id: direction}), so “did I swipe X?” and dedup are fast and writes spread. - Most swipes are lefts (passes) — you can store rights authoritatively and treat lefts more cheaply, but you still need to not re-show swiped users.
Step 4 — Match detection (the clever bit)
A match = both users swiped right. Detect it at swipe time with a single lookup, not a scan:
- When A swipes right on B, check whether B already right-swiped A (a point lookup in the swipe store). If yes → create a match and notify both.
- This is O(1) per swipe — no expensive “find mutual likes” job. Optionally keep a
likes_receivedindex per user to make the reverse lookup direct.
Step 5 — Architecture
location update → geo index (geohash) of active users
feed request → geo query (nearby) → filter prefs + exclude swiped → candidate deck (cached)
swipe → record (sharded by swiper) → if right, check reverse → match? → notify + open chat
Matches open a chat (reuse the messaging system).
Step 6 — Scale
- Geo index sharded by region/cell; hot dense cities are the load hotspots.
- Swipes sharded by swiper; matches stored per user pair.
- Feed precomputed/cached per user; recommendations can add ML ranking (ELO-like desirability, interests) on top of geo.
Trade-offs to raise
- Geohash (simple prefix queries, edge effects at cell borders) vs quadtree/S2 (balanced, more complex). Handle border cases by querying neighboring cells.
- Precomputed deck (instant swipes, may go stale/locations move) vs query-per-feed (fresh, costlier).
- Match detection at write (O(1), needs reverse index) vs batch (simpler, delayed).
The interview cue
“Index active users by geohash for nearby queries; build each user’s deck by geo query → preference filter → exclude already-swiped, cached for instant swiping; store swipes sharded by swiper; detect a match at swipe time with an O(1) reverse lookup (‘did they already like me?’), then open a chat.” Geospatial recall + O(1) match detection + write-heavy swipe storage is the answer; implementation next.