Question Breakdowns
Dating App
A worked system design: show nearby, eligible profiles, record swipes, and detect mutual likes to create matches. Geo filtering, the match-detection write path, and recommendation feeds.
A dating app shows a user a stack of candidate profiles, records a like/pass on each, and — when two people like each other — creates a match and opens a chat. The interesting parts are generating a good candidate feed with geo + preference filtering, and detecting the mutual like efficiently without scanning. It reuses ideas from Proximity Search and the News Feed.
1. Requirements
Functional:
- Show a feed of candidate profiles filtered by location, age, and preferences.
- Record a swipe (like / pass) on each profile.
- When two users like each other, create a match and notify both.
Non-functional:
- Low-latency feed — the stack must feel instant.
- Scale — swipes are extremely high-volume (many per session per user).
- No missed matches — a mutual like must reliably become a match.
- Eventual consistency is fine for the feed; match detection must be correct.
2. Data model
users ( id, location, age, gender, preferences, ... )
swipes ( swiper_id, target_id, direction, created_at ) -- high volume
matches ( user_a, user_b, created_at )Swipes are the firehose — one row per swipe, append-only, and you rarely read them back individually; you read them to check for reciprocity.
3. The candidate feed
Generating "who to show next" is a filtered recommendation problem:
- Geo + attribute filter: candidates within distance X matching age/gender/preferences — a geospatial query plus attribute filters. Elasticsearch handles "near me AND age 25–35 AND these preferences" well.
- Exclude already-swiped: don't show profiles the user already acted on. Keep a per-user set of seen ids (a Redis set, or a bloom filter at huge scale) and filter against it.
- Precompute vs on-demand: like a feed, you can precompute a queue of candidates per active user so the swipe stack is a cheap read, refilling it in the background.
4. The key write: detecting a mutual like
When user A likes user B, you must answer: did B already like A? Don't scan — make it a direct lookup:
A likes B:
1. record swipe (A → B, like)
2. check: does a swipe (B → A, like) exist?
yes → create a match, notify both
no → nothing more; wait for B to maybe like A laterStep 2 is a point lookup keyed by (swiper=B, target=A) — index swipes on (swiper_id, target_id) so it's O(1)-ish, not a scan. The whole match engine is "on each like, check the reverse like." Do this atomically or idempotently so two simultaneous mutual likes create exactly one match, not two.
5. Handling the swipe firehose
Swipes vastly outnumber matches. Options:
- Write swipes to a fast, write-optimised store (Cassandra/DynamoDB) — they're append-heavy and keyed by swiper.
- The reciprocity check and match creation can go through a queue if you want to absorb bursts, at the cost of a slight delay before "It's a Match!" appears (usually acceptable).
6. Deep dives
- Recommendation quality: beyond filters, rank candidates (activity, popularity balancing, ML scoring) — keep mechanism (how profiles reach the stack) separate from policy (ranking), as in the News Feed.
- Don't re-show passes: the seen-set also prevents showing someone they passed; decide whether passes are permanent or expire.
- Fairness / starvation: avoid always surfacing the same popular profiles and starving others — cap exposure.
- Match → chat: a match opens a conversation, handing off to a Chat App design.
What to emphasise
Center the mutual-like detection and the geo-filtered feed:
"The feed is a geo-plus-attribute query excluding anyone the user already swiped, which I'd precompute into a per-user candidate queue so swiping is a cheap read. The core write is the mutual-like check: when A likes B I record the swipe and do a direct indexed lookup for an existing B→A like — no scanning — and if it exists I create exactly one match idempotently and notify both. Swipes are a firehose, so they go to a write-optimised store, and feed freshness can be eventually consistent while match detection stays correct."
Next up: Coding Judge — running untrusted code safely at scale.
