Tinder System Design Interview: Geosharded Recommendations and the Billion-Swipe Match Problem
Tinder has reported on the order of 1.6 to 2 billion swipes per day and tens of millions of matches created daily (company and press figures, treat exact daily counts as estimates). Every swipe is a durable write, and a right swipe has to be checked against the other person's history in milliseconds to decide if a match just happened. The hard part is not the UI. It is finding the right few hundred nearby candidates out of a global user base and recording a firehose of swipes without losing any.
Tinder is a location-based dating app where each user is shown a deck of nearby candidate profiles, swipes right (like) or left (pass) on each, and a mutual right swipe creates a match that opens a chat. The design has three genuinely hard subsystems. First, recommendation retrieval: given a user's location and distance filter, return candidates ranked by relevance, which Tinder solves with geosharding using Google's S2 library so a query touches only a handful of shards instead of a global index. Second, the swipe pipeline: billions of swipes per day are ingested as an ordered stream, left swipes are archived cheaply while right swipes are checked against a likes store to detect the reciprocal like. Third, match detection has to be idempotent and race-free so a simultaneous double right swipe produces exactly one match. Around these sit profile and photo storage on object storage plus a CDN, a match and chat service backed by a durable message store with real-time delivery over persistent connections, and push notifications. The recurring themes are geo-partitioning, write-heavy stream processing, and cache-backed reciprocal lookups.
Asked at: Asked at Match Group, Bumble, Hinge, and broadly at Meta, Amazon, and Uber as a proximity-search and write-heavy-stream variant. It is a favorite because dating apps map cleanly to geospatial indexing plus an idempotent write path.
Why this question is asked
Interviewers like Tinder because it forces you off the well-worn news-feed template and into geospatial retrieval, which most candidates have never partitioned before. It tests whether you can reason about sharding by a continuous dimension (location) rather than by user id, and whether you understand why a naive uniform grid creates hot shards over dense cities. It also probes the write path: billions of swipes is a throughput problem, mutual-like detection is a concurrency problem, and the double-swipe match is an idempotency problem. A strong candidate separates the read path (deck generation) from the write path (swipe ingestion and match detection) and shows how each scales independently.
Requirements
Always clarify these in the first 5 minutes of the interview. Do not start drawing boxes until both lists are agreed.
Functional requirements
- Generate a deck of candidate profiles for a user filtered by distance, age range, and gender preference
- Record every swipe (right for like, left for pass) durably and in order per user
- Detect a mutual right swipe and create exactly one match, then notify both users
- Never show the same profile twice to the same user within a session or recycle window
- Rank candidates by a relevance score rather than showing them in random order
- Let matched users exchange chat messages with delivery and read state
- Store profile data and multiple photos per user, served fast worldwide
- Push notifications for new matches and new messages
- Support super-likes, unmatch, block, and report with content moderation on photos and profiles
Non-functional requirements
- Deck and swipe responses should feel instant, targeting well under 100 ms at the API
- No lost swipes: the swipe write path must be durable and at-least-once with idempotent effects
- High availability across regions; a regional outage should not take the app down globally
- Recommendations scale to billions of swipes per day without global index scans
- Match creation is race-free under concurrent reciprocal swipes
- Photos load quickly on mobile networks through a CDN
- User location and preference changes propagate to retrieval within seconds
Back-of-envelope scale estimates
Show your math. Pulling numbers from thin air signals you have not thought about the load.
Daily swipes
~1.6 to 2 billion per day (estimate)
Company and press figures put swipe volume in the low billions per day. Taking 1.6 billion over 86,400 seconds gives about 18,500 swipes per second on average, with peak evening traffic several times higher. Derived average.
Swipe write throughput at peak
~50k to 100k writes/sec (estimate)
Average is ~18.5k/sec; dating traffic is heavily diurnal and clusters in evenings, so a 3x to 5x peak factor gives roughly 55k to 90k swipes/sec. Derived estimate, drives the choice of a streaming ingest layer over synchronous DB writes.
Matches created daily
tens of millions/day (estimate)
Only reciprocal right swipes match, and right-swipe rates are a minority of swipes. If a few percent of swipes are right swipes and a fraction of those are reciprocated, the daily match count lands in the tens of millions. Derived, consistent with reported figures.
Likes storage growth
billions of rows/day, hundreds of billions retained (estimate)
Each swipe is one like/pass row of roughly 50 to 100 bytes (swiper id, target id, direction, timestamp). At ~1.6 billion/day that is ~80 to 160 GB/day of raw swipe records before compression. Left swipes can be archived cheaply; right swipes are kept hot for reciprocal checks. Derived.
Geoshards worldwide
~40 to 100 shards (published range)
Tinder's engineering blog reports that 40 to 100 geoshards balanced P50/P90/P99 latency in their testing. Cells are grouped along the S2 Hilbert curve so each shard holds a contiguous, load-balanced region.
Shards touched per deck query
~3 of ~55 shards (published example)
Tinder's example shows a 100-mile search circle mapping to S2 cells that resolve to only about 3 geoshards out of 55, so a deck query fans out to a small fraction of the fleet instead of scanning a global index.
High-level architecture
A swipe or a deck request enters through an API gateway that authenticates the session and routes to the right backend service. For the read path, a recommendations service takes the user's current location and distance filter, uses the S2 library to convert the search circle into a set of covering cells, looks up which geoshards those cells belong to, and queries only those shards of a geosharded Elasticsearch cluster for candidate profiles. Candidates are filtered by age, gender, and distance, scored by a relevance model, cross-checked against the set of people this user has already swiped, and returned as an ordered deck that the client caches locally. For the write path, each swipe is published to an ordered stream (Kinesis or Kafka style) partitioned so that a given user's swipes are processed in order. A swipe processor archives left swipes to cheap object storage for analytics and writes right swipes to a likes store plus a likes cache. A match worker consuming the right-swipe stream checks whether the target has already liked the swiper; if so it creates a single match record and pushes a real-time match event to both clients over their persistent connections, which also triggers push notifications. Once matched, chat messages flow through a messaging service with a durable per-conversation message store and real-time fanout. Profiles live in a key-value store, photos live in object storage fronted by a CDN, and an indexer keeps each user's searchable document in the correct geoshard, migrating it when the user's location crosses shard boundaries.
In a real interview, sketch this on the whiteboard before diving into any single box.
Core components
Walk through each service. The interviewer wants to hear what each one owns, not just the names.
API gateway and edge
Single entry point that terminates TLS, authenticates the session token, applies rate limits, and routes to backend services. It shields internal services from the public internet and centralizes authorization for the roughly hundreds of microservices behind it.
Recommendation and retrieval service
Turns a location plus distance filter into a set of S2 cells, maps those to geoshards, and queries a geosharded Elasticsearch cluster for nearby candidates. It applies hard filters (age, gender, distance), a relevance ranking, and an already-seen exclusion before returning a deck.
Geosharded index and abstraction layer
An Elasticsearch cluster partitioned by geography, with an abstraction microservice that hides the shard math from callers. Cells are grouped along the S2 Hilbert curve into 40 to 100 load-balanced shards, and replicas are spread randomly across hosts to smooth out timezone-driven hot spots.
Swipe ingestion stream
A partitioned, ordered stream that absorbs the swipe firehose so the app never blocks on a synchronous database write. Left swipes are archived to object storage; right swipes are routed to the likes store and the match worker for reciprocal checks.
Likes store and likes cache
The durable record of who liked whom, plus a fast cache used to answer the has-target-already-liked-me question during match detection. The cache absorbs the read-heavy reciprocal lookups so the match path stays within milliseconds.
Match and chat service
Creates the single match record on a mutual like, then backs the conversation with a durable per-match message store and real-time delivery over persistent WebSocket connections. It handles unmatch, message read state, and ordering within a conversation.
Profile, photo, and moderation service
Stores structured profile fields in a key-value database and photos in object storage fronted by a CDN for fast global delivery. New photos and profile text pass through automated and human moderation before they can appear in anyone's deck.
Data model
Pick the right store per table. Justify each choice with the access pattern, not by reflex.
usersuser_idnamebirth_dategenderpreferenceslast_lat_lngProfile store, typically a key-value database like DynamoDB for fast single-key reads. last_lat_lng and preferences drive which geoshard the user is indexed in and what deck they see.
user_geo_indexuser_idgeoshard_ids2_cell_idlat_lngactive_atThe searchable document that lives in the geosharded Elasticsearch cluster. When last_lat_lng crosses a shard boundary, the indexer moves the document to the new geoshard.
swipesswiper_idtarget_iddirectionswiped_atswipe_idAppend-only record of every swipe. Partitioned by swiper_id for ordering. Left swipes are archived to object storage for analytics; right swipes are also written to the likes path. swipe_id makes replays idempotent.
likestarget_idliker_idcreated_atis_superRight swipes keyed so that has-X-liked-me is a single lookup on target_id. Mirrored in a likes cache. This is the hot read for match detection and for the who-likes-you feature.
matchesmatch_iduser_auser_bcreated_atstatusOne row per mutual like, with the user pair stored in a canonical order (smaller id first) so a unique constraint prevents a double-swipe race from creating two matches. status covers active, unmatched, and blocked.
messagesmatch_idmessage_idsender_idsent_atbodyread_atPer-conversation messages partitioned by match_id and clustered by time for fast history reads. Real-time delivery is layered on top; this table is the durable source of truth.
Deep dives
These are the conversations the interviewer is steering you toward. Practice each one until you can talk through it without notes.
Geosharding candidate retrieval with S2 cells
The core read problem is proximity search: return people within, say, 100 miles, ranked by relevance. A single global index does not scale, and sharding by user id would force every deck query to fan out to every shard. Tinder shards by geography using Google's S2 library, which projects the Earth onto a cube and lays a Hilbert space-filling curve across each face so that points close in the real world get nearby cell ids. They picked S2 levels 7 and 8 (roughly 45-mile and 22.5-mile cells) as the working granularity. A naive equal-area grid would overload dense cities, so instead they compute a load score per cell (unique active users) and walk the Hilbert curve grouping adjacent cells into 40 to 100 shards of balanced load. At query time the search circle is converted to its covering S2 cells, those cells map to a small set of geoshards (about 3 of 55 in their published example), and only those shards are queried. Because the Hilbert curve preserves locality, a shard holds a contiguous region, so most queries stay within one or two shards.
The write-heavy swipe pipeline
At a couple of billion swipes a day, you cannot do a synchronous database write per swipe on the request path. Swipes are published to a partitioned, ordered stream (Kinesis or Kafka style), keyed by swiper id so one user's swipes stay in order. A consumer then splits the stream: left swipes are the overwhelming majority and carry little value beyond analytics and dedup, so they are batched and archived to cheap object storage like S3. Right swipes are the valuable minority, so they are written to the likes store and pushed toward match detection. Decoupling the durable write from the user request means the app returns instantly, the stream absorbs evening traffic spikes with backpressure, and the storage tiers are sized for their real access patterns rather than treating every swipe as equally hot.
Mutual-like detection and the double-swipe race
A match happens only when both people swipe right. When user A right-swipes B, the match worker checks the likes store (via the likes cache) for an existing B-likes-A row. If it is there, a match is created; if not, A's like is simply recorded and the check will fire later when B swipes. The subtle bug is the simultaneous double swipe: A and B both swipe right at nearly the same instant, and two workers each see no prior like and both try to create a match. The fix is to store the pair in a canonical order (smaller user id first) and put a unique constraint on the pair, so the second insert loses cleanly and you get exactly one match. Making the swipe itself idempotent with a swipe id protects against stream replays and client retries so a resent swipe never creates a phantom second like.
Storing billions of likes and answering did-they-like-me
The likes data is the heaviest table and the hottest lookup. Two access patterns matter: during match detection, given A likes B, has B already liked A; and for the likes-you feature, list everyone who liked me. Both are served best by keying likes on the target (the person being liked) so that has-X-liked-me and who-liked-me are single-partition reads. A likes cache in front of the store absorbs the reciprocal checks so the match path avoids a database round trip most of the time. Left swipes do not need this treatment: they are recorded mainly to avoid re-showing a profile and for analytics, so they can live in a cheaper store or a bloom-filter-style seen set. Time-based partitioning and archiving of cold likes keeps the hot working set small even as the historical table grows into the hundreds of billions of rows.
Deck generation and not repeating profiles
A good deck is more than a proximity query. Candidates are filtered by the hard constraints (distance, age, gender preference on both sides), then ranked by a relevance score. Tinder historically used an Elo-style desirability score and has since moved to more sophisticated relevance models, but the interview point is that ranking is a separate stage layered on top of geo retrieval. The recommendation service also has to exclude everyone the user already swiped, which is where the archived left swipes and the likes data feed back in as an already-seen set, often a compact structure like a per-user bloom filter or a recent-swipes set so the exclusion check is cheap. Decks are generated in batches and cached on the client so the user can keep swiping without a round trip per card, and the server refills the deck ahead of time.
Chat after match and real-time delivery
A match unlocks a one-to-one conversation, so the messaging subsystem is a scoped chat rather than a general social graph. Messages are partitioned by match id and clustered by time, which makes loading a conversation and appending to it both single-partition operations. Durability lives in that message store; real-time delivery is a separate concern handled by persistent connections (WebSockets) to an online user, with a fallback to push notification when the recipient is offline. Because a conversation only has two participants and starts empty at match time, you avoid the fanout complexity of group chat, and ordering only has to be consistent within a single match id.
Photos, CDN, and moderation
Profile photos are large binary objects with a read-heavy, write-rare pattern, so they belong in object storage fronted by a CDN, never in the primary database. The app uploads to object storage and stores only the URLs on the profile record, and the CDN serves resized variants close to the user for fast loading on mobile networks. Because photos and bios are user-generated and shown to strangers, every new photo and profile edit passes through moderation, a mix of automated classifiers for nudity, spam, and known-bad content plus a human review queue for edge cases, before the content becomes eligible to appear in anyone's deck.
Trade-offs to discuss
Every senior interviewer expects you to surface at least 3 of these. Pick the decisions, state the alternatives, and justify your choice.
Geosharding by location versus sharding by user id
Sharding by user id spreads writes evenly but forces every proximity query to scan all shards. Geosharding keeps a deck query on a handful of contiguous shards, at the cost of hot-shard risk over dense cities, which is why load is balanced by user count along the Hilbert curve rather than by equal area.
Streaming swipe ingestion versus synchronous database writes
A synchronous write per swipe cannot absorb billions of daily writes with evening spikes and would couple app latency to database health. A partitioned stream gives ordering, backpressure, and cheap tiered archival, at the cost of eventual consistency in when a match is detected.
Archiving left swipes cheaply versus keeping all swipes hot
Left swipes are the majority but are rarely read after the fact, so pushing them to object storage saves enormous cost. The tradeoff is that any analytics or already-seen check on old passes is slower, which is acceptable because the hot need is only to avoid re-showing recent profiles.
Unique constraint on the ordered pair versus a distributed lock for match creation
A canonical-order unique key on the match row makes the double-swipe race resolve itself at the database with no coordination. A distributed lock would also work but adds latency and a failure mode on every match, so the constraint is simpler and faster.
Key-value profile store versus a relational database
Profiles are read by primary key, need low latency at high volume, and rarely require joins, which fits a key-value store like DynamoDB. The tradeoff is losing ad hoc relational queries, which are pushed to a separate analytics pipeline rather than served from the hot path.
Eventually consistent recommendations versus strict freshness
Letting a location or preference change take a few seconds to reach the geo index keeps the write path cheap and the read path fast. The cost is that a just-moved user might briefly see slightly stale candidates, which is invisible in practice and well worth the scalability.
How Tinder actually does it
Tinder's engineering team published a two-part Geosharded Recommendations series describing exactly this design: using Google's S2 library and its Hilbert-curve cell ids, choosing S2 levels 7 and 8, balancing cells into 40 to 100 geoshards by load rather than by area, and running the whole thing on a single multi-index Elasticsearch cluster behind an abstraction microservice, with replicas spread randomly across hosts to smooth timezone-driven hot spots. They reported the geosharded system handling roughly 20 times more computations than the prior single-index setup and a typical 100-mile query resolving to about 3 of 55 geoshards. Independent architecture write-ups describe the swipe path as a Kinesis or Kafka style ordered stream feeding match workers that consult a likes cache, with left swipes archived to S3, profiles in DynamoDB, and match notifications delivered over WebSockets. Match Group's public filings put Tinder in the tens of millions of monthly active users, which is the scale these subsystems are built for. Exact daily swipe and match counts vary by source and should be treated as estimates.
Sources
- Tinder Tech Blog: Geosharded Recommendations Part 1 (Sharding Approach)
- Tinder Tech Blog: Geosharded Recommendations Part 2 (Architecture)
- How Tinder Recommends To 75 Million Users with Geosharding (ByteByteGo)
- Tinder Architecture: How Tinder Scaled to 1.6 Billion Swipes per Day (systemdesign.one)
- Designing Tinder (High Scalability)
Lessons to study before this interview
If any of these topics are fuzzy, the interviewer will catch it. Each lesson is 15 to 60 minutes with diagrams, code, and a quiz.
Related system design interview questions
Practice these next. They lean on the same core building blocks as Tinder.