Skip to content
System design course
Ch.4 · Designing real systems·concept ·8 min read

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_received index 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.