Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

System Design (High-Level Design)  Module 23 – Case studies: social, feeds and search

Design dating matches like Tinder

Design the matches of a dating app: decks of nearby people that suit both sides, swipes at peak load, one match per pair, private distances, blocks.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free core, Pro depth: 8 of 25 minutes free
Free: Both people's settings must allow the pair · Swipes are the biggest number; deck builds are the heaviest work · Six records, from a person's square to the pair of two likes · 3 more sections. With a pass: 3 in-depth parts and 3 interview answers, about 17 minutes.

What you will learn

  • Estimate the swipes, matches, deck builds and storage of a dating app from its daily users
  • Design deck building from a cell index, with every filter applied in both directions
  • Create exactly one match when two likes arrive at the same moment
  • Protect people's locations and make blocks take effect at once

Before you start

At 20 million people a day, a dating app records 23,148 swipes a second and builds 463 decks of nearby people a second. Its matching system does three jobs: it shows each person a deck of nearby profiles that suit both people’s settings, it records every swipe, and when two people have liked each other it creates a match that opens a chat. Decks are built ahead of time from a location index, with every setting checked in both directions, so each swipe is a small write.

The hard parts are correctness and safety: two likes that arrive at the same moment must create exactly one match, the distance a card shows must never give away where someone is, and a block must work at once. This case study sizes the app and follows each of its paths.

Not affiliated

Based on public information about Tinder. MySmartCoPilot is not affiliated with Tinder, and Tinder’s real architecture may differ from what this lesson describes.

Both people’s settings must allow the pair

  • Who may see whom? Each person sets the genders they want to see, an age range and a maximum distance of 2 to 100 km. A profile appears only when both people’s settings allow the pair.
  • What makes a match? Two likes, in either order. A pass removes the profile from that person’s decks.
  • After a match: both people hear about it within seconds and can chat; an unmatch ends the chat for both.
  • Safety: blocks and reports, and distances shown only roughly.
  • Out of scope: the chat itself (see chat delivery), payments and photo moderation.

Functionally, people report a position, fetch cards, like or pass, see their matches, and unmatch, block or report. The next card must be on the phone before the current one is swiped, so cards are fetched ahead and a page of them loads within 200 ms at the 99th percentile. An acknowledged swipe is never lost. A pair has at most one match, created within a second of the second like. An exact position never leaves the server, and a block applies on the next request.

Swipes are the biggest number; deck builds are the heaviest work

Quantity Assumption Result
Swipes 20 million people a day × 100 swipes 2 billion a day: 23,148 a second, about 70,000 at an evening peak of 3 ×
Likes 30 % of swipes 600 million a day, 6,944 a second
Matches 3 % of likes find the other person’s like 18 million a day, 208 a second, two notifications each
Swipe history 32 bytes a swipe: two ids, the direction, the time 64 GB a day, about 23 TB a year
Deck builds 2 decks of 100 cards a person a day 40 million a day: 463 a second, about 1,400 at peak
Deck cache 20 million decks × 100 ids × 8 bytes 16 GB
Photos one 100 KB photo for each card shown 200 TB a day from a CDN, about 18.5 Gbit/s

Each swipe is a few dozen bytes. The heavy work hides in the deck builds: the example below reads 18,499 people to fill one deck, so 463 builds a second check about 8.6 million candidates a second.

Checkpoint

Check the scope and the estimate

  1. Question 1 of 3 Meera, 29, wants men aged 27 to 35. Arjun is 31 and wants women aged 22 to 27. Does Arjun appear in Meera's deck?

    Choose one answer.

    Show the answer: Question 1 of 3

    Answer: No, because Meera's age is outside the range Arjun asked for

    A profile appears only when both people's settings allow the pair: genders, ages and distance, each checked in both directions.

  2. Question 2 of 3 20 million people a day swipe 100 times each. How many swipes a second is that on average?

    Type a number.

    A hint

    A day has 86,400 seconds.

    Show the answer: Question 2 of 3

    Answer: 23148 swipes a second (anything from 20833 to 25463 counts)

    20,000,000 × 100 = 2 billion swipes a day, and 2,000,000,000 ÷ 86,400 ≈ 23,148 a second. Swipes bunch up in the evening, so plan the write path for about three times that.

  3. Question 3 of 3 Each of the 20 million people gets 2 decks built a day. How many deck builds a second is that on average?

    Type a number.

    Show the answer: Question 3 of 3

    Answer: 463 builds a second (anything from 417 to 509 counts)

    20,000,000 × 2 = 40 million builds a day, and 40,000,000 ÷ 86,400 ≈ 463 a second, about 1,400 at a peak of three times the average. Each build reads thousands of people, so these few hundred builds are the heavy work.

Six records, from a person’s square to the pair of two likes

  • PUT /v1/me/location takes a latitude, a longitude and their accuracy; the server keeps only the person’s 1 km square and never returns it.
  • GET /v1/deck?limit=25 returns cards: profile id, first name, age, photos and a label such as “4 km away”.
  • POST /v1/swipes takes the profile id, like or pass, and a swipe id made by the app, so a retry is stored once; it answers whether the swipe made a match.
  • GET /v1/matches, DELETE /v1/matches/{id}, POST /v1/blocks and POST /v1/reports complete the API.
Record Key Holds
Profile person id age, gender, settings, photos, last active
Cell index cell id the people active in the cell in the last 3 days
Pair both ids, the smaller first which of the two has liked the other, and the match once both have
Swipe history swiper id, profile id like or pass, and when
Deck person id the next 100 profile ids and when they were built
Blocks person id whom they blocked and who blocked them

Decks are built ahead, so a swipe is one small write

Dating matches: app, API, location, deck and swipe services, cell index, pair store, swipe history, deck builder and deck cache.Mobile appcards fetched aheadAPI gatewayLocation servicekeeps a 1 km squareDeck serviceblocks, distance labelsSwipe serviceCell indexpeople per 4 km cellPair storeboth likes of a pairin one recordSwipe historyfed by the swipe logDeck builderrings of cells,filters both ways, rankingNotificationsand chatDeck cachenext 100 ids a person1. position2. next 25 cards3. like or pass4. a like:one transactionevery swipe5. the match, oncerings of cellspast swipesa deck ahead of needreads

The first complete design: positions, swipes, matches and decks

Text description of the diagram

The diagram shows the parts of the design from top to bottom.

  1. The mobile app calls the API gateway, which sends positions to the location service, requests for the next 25 cards to the deck service, and likes and passes to the swipe service.
  2. The location service keeps only the centre of the person's 1 km square and files the person under a 4 km cell of the cell index.
  3. The swipe service writes every swipe to the swipe history, through the swipe log. A like is also written, in one transaction, to the pair store, which holds both likes of a pair in one record. When that transaction finds both likes, it creates the match once, and the match goes to the notification and chat services.
  4. Ahead of need, the deck builder reads rings of cells from the cell index and past swipes from the swipe history, applies every filter in both directions, ranks the candidates and writes the next 100 profile ids to the deck cache.
  5. The deck service reads the next cards from the deck cache, checks blocks again and turns distances into labels.

Position. The location service keeps only the centre of the person’s 1 km square and files them under a 4 km cell of the cell index. Deck build. Ahead of need, a deck builder reads the person’s cell and enough rings of cells around it to cover their maximum distance, applies every filter in both directions, drops past swipes and blocks, ranks the rest and writes the next 100 ids to the deck cache. Serve. The app fetches 25 cards at a time; the deck service fills them in from profiles, checks blocks again and turns distances into labels. Swipe. Every swipe goes to the swipe log, which feeds the swipe history, kept by the person who swiped. A like also updates the pair record of the two people, on the shard: One of several databases that together hold a group's data, each with its own share of the rows, such as the accounts of one set of customers and everything that belongs to them. More in “First-hand case: how MySmartCoPilot is built” the pair’s key picks, in one transaction. Match. If that transaction finds the other like already there, it creates the match, the reply says “It’s a match”, and the other person gets a notification. Follow one like through those steps:

Interactive · message by message One like that completes a match Step through Meera's like of Arjun's card, from her phone to the match he hears about.
One like that completes a match, step 6 of 6. Messages so far: Meera's app to Swipe service: like Arjun, swipe id 7f3; Swipe service to Pair store: Meera likes Arjun; Swipe service to Meera's app: It's a match.Meera's appnext cards already loadedSwipe servicePair storeboth liked: matchlike Arjun, swipe id 7f3Meera likes ArjunIt's a matchthe match goes on to Arjun

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. A swipe right. Meera likes Arjun's card. The next cards are already on her phone, so only this one small write leaves it, with a swipe id the app made, so a retry is stored once.
  2. Every swipe is logged. The swipe service writes every swipe to the swipe log, which fills the swipe history, kept by the person who swiped. A pass would end here.
  3. One transaction for the pair. A like also updates the record of the pair, Meera and Arjun, on the shard the pair's key picks, in one transaction.
  4. The other like is there. Arjun liked Meera's card an hour ago, so the same transaction finds both likes in the record and creates the match.
  5. It's a match. The reply tells the app that the swipe made a match, so Meera sees "It's a match" at once.
  6. Arjun hears about it. The match goes on to the notification and chat services, and Arjun gets a notification within seconds of Meera's like.

The example builds one deck for a woman of 29 with a maximum distance of 8 km, among 40,000 synthetic people:

One deck: cells, filters in both directions, ranking and labels JavaScript · deck/deck_builder.mjs
// One person's deck, built the way a deck builder does it: read the index cells around her, keep the people whose
// settings and hers both allow the pair, drop anyone she swiped or blocked, rank, and label distances roughly.
// 40,000 synthetic people live on a flat 60 km square measured in kilometres; a seeded generator makes them, so every
// run prints the same.
const INDEX_KM = 4; // the cell index groups people into 4 km squares
const SNAP_KM = 1; // positions are kept only as the centre of a 1 km square
const DECK_SIZE = 100;

function generator(seed) {
  const imul = Math.imul;
  return () => {
    seed = (seed + 0x6d2b79f5) | 0;
    let t = imul(seed ^ (seed >>> 15), 1 | seed);
    t = (t + imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}
const rand = generator(4111);
const pick = (items) => items[Math.floor(rand() * items.length)];
const WANTS_OF_W = ['M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'W', 'MWN']; // mostly men, some women, a few anyone
const WANTS_OF_M = ['W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'M', 'MWN'];
const MAX_KMS = [5, 10, 10, 15, 15, 25, 40];
const bell = (spread) => (rand() + rand() + rand() + rand() - 2) * spread; // a bump around 0, no Math.log needed

const snap = (v) => (Math.floor(v / SNAP_KM) + 0.5) * SNAP_KM;
const cellOf = (x, y) => `${Math.floor(x / INDEX_KM)},${Math.floor(y / INDEX_KM)}`;
const km = (a, b) => Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
const n = (v) => String(v).replace(/\B(?=(\d{3})+(?!\d))/g, ','); // 18460 -> 18,460

// People: most live in the city around (0, 0), the rest anywhere in the square.
const people = [];
for (let id = 1; id <= 40000; id++) {
  const inCity = rand() < 0.75;
  const x = inCity ? bell(14) : (rand() - 0.5) * 60;
  const y = inCity ? bell(14) : (rand() - 0.5) * 60;
  const gender = rand() < 0.47 ? 'W' : rand() < 0.95 ? 'M' : 'N';
  const wants = gender === 'W' ? pick(WANTS_OF_W) : gender === 'M' ? pick(WANTS_OF_M) : 'MWN';
  const age = 18 + Math.floor(32 * Math.min(rand(), rand()));
  people.push({
    id, age, gender, wants,
    ageMin: Math.max(18, age - 2 - Math.floor(rand() * 6)), ageMax: age + 2 + Math.floor(rand() * 9),
    maxKm: pick(MAX_KMS), hoursAgo: Math.floor(rand() * 72),
    x: snap(x), y: snap(y), // what the location service keeps: never the exact position
  });
}
const index = new Map(); // cell -> ids of the people active there in the last 3 days
for (const p of people) {
  const c = cellOf(p.x, p.y);
  if (!index.has(c)) index.set(c, []);
  index.get(c).push(p.id);
}
const byId = (id) => people[id - 1];

const her = { id: 0, age: 29, gender: 'W', wants: 'M', ageMin: 27, ageMax: 35, maxKm: 8, x: snap(1.3), y: snap(-0.6) };
const swiped = new Set(Array.from({ length: 6000 }, () => 1 + Math.floor(rand() * 40000)));
const blocked = new Set([2286, 31407]);

// 1. Read the cells: hers and enough rings of neighbours to cover her maximum distance.
const rings = Math.ceil(her.maxKm / INDEX_KM);
const [cx, cy] = cellOf(her.x, her.y).split(',').map(Number);
let read = [];
for (let dx = -rings; dx <= rings; dx++) for (let dy = -rings; dy <= rings; dy++) read.push(...(index.get(`${cx + dx},${cy + dy}`) ?? []));
const cells = (2 * rings + 1) ** 2;
console.log(`cells read: ${cells} (her ${INDEX_KM} km cell and ${rings} rings around it), people in them: ${n(read.length)}`);

// 2. Filter: every rule holds in both directions, so neither person sees someone who could never see them.
const steps = [
  ['within her 8 km', (p) => km(her, p) <= her.maxKm],
  ['genders wanted both ways', (p) => her.wants.includes(p.gender) && p.wants.includes(her.gender)],
  ['ages wanted both ways', (p) => p.age >= her.ageMin && p.age <= her.ageMax && her.age >= p.ageMin && her.age <= p.ageMax],
  ['within their own distance', (p) => km(her, p) <= p.maxKm],
  ['not swiped or blocked', (p) => !swiped.has(p.id) && !blocked.has(p.id)],
];
let left = read.map(byId);
for (const [name, keep] of steps) {
  left = left.filter(keep);
  console.log(`  ${name.padEnd(27)} ${n(left.length).padStart(6)}`);
}

// 3. Rank: near and recently active first (6 hours away from the app count like 1 km more); keep a deck of 100.
const score = (p) => km(her, p) + p.hoursAgo / 6;
const deck = left.slice().sort((a, b) => score(a) - score(b) || a.id - b.id).slice(0, DECK_SIZE);
const label = (p) => `${Math.max(1, Math.round(km(her, p)))} km away`; // whole km between 1 km squares
console.log(`\ndeck: ${deck.length} of ${n(left.length)} eligible people; the first 6 cards:`);
for (const p of deck.slice(0, 6)) console.log(`  #${String(p.id).padStart(5)}  age ${p.age}  ${label(p).padEnd(10)}  active ${p.hoursAgo} h ago`);
const shown = new Map();
for (const p of deck) shown.set(label(p), (shown.get(label(p)) ?? 0) + 1);
console.log(`the deck's distance labels: ${[...shown].sort((a, b) => parseInt(a[0]) - parseInt(b[0])).map(([l, c]) => `${c} × ${l.replace(' away', '')}`).join(', ')}`);

// 4. Check: a scan of all 40,000 people finds exactly the same eligible set, so the cells missed no one.
const scan = people.filter((p) => steps.every(([, keep]) => keep(p)));
const same = scan.length === left.length && scan.every((p) => left.includes(p));
console.log(`\nfull scan of ${n(people.length)} people: ${n(scan.length)} eligible, ${same ? 'the same people' : 'DIFFERENT people'}; the cells held ${(100 * read.length / people.length).toFixed(1)} % of everyone`);

Output

cells read: 25 (her 4 km cell and 2 rings around it), people in them: 18,499
  within her 8 km             11,283
  genders wanted both ways     5,127
  ages wanted both ways        1,397
  within their own distance    1,310
  not swiped or blocked        1,138

deck: 100 of 1,138 eligible people; the first 6 cards:
  #25031  age 27  1 km away   active 0 h ago
  #32687  age 28  1 km away   active 1 h ago
  #19066  age 30  1 km away   active 0 h ago
  #21023  age 30  1 km away   active 0 h ago
  # 6503  age 27  1 km away   active 3 h ago
  #  439  age 29  1 km away   active 5 h ago
the deck's distance labels: 25 × 1 km, 22 × 2 km, 19 × 3 km, 21 × 4 km, 13 × 5 km

full scan of 40,000 people: 1,138 eligible, the same people; the cells held 46.2 % of everyone

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node deck_builder.mjs

The 25 cells held 18,499 people, and the filters left 1,138. The two-way rules did most of the work: 5,127 men within 8 km want women, but only 1,397 of them are in her age range and also want someone of 29. A scan of all 40,000 people finds the same 1,138, so reading cells missed no one. The labels are whole kilometres between square centres, from 1 to 5 km.

Checkpoint

Check: The six records and their keys · Decks ahead, swipes as small writes

  1. Question 1 of 2 What is the key of the pair record that holds both likes of two people?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Both ids, the smaller first

    Ordering the two ids makes the key the same from either side of the pair, which is what lets one record hold both likes.

  2. Question 2 of 2 A person likes a profile. Which of these happen before the app shows the answer?

    Choose every answer that is right.

    Show the answer: Question 2 of 2

    Answer:

    • The like is written to the pair record of the two people
    • The same transaction creates the match when the other person's like is already there

    The reply waits for one transaction on the pair's shard, which records the like and, when both likes are there, the match. The swipe history is filled from the swipe log afterwards, and notifications follow the match.

Practise: filter a deck in both directions

Write the filter a deck builder runs on every candidate:

Exercise · Easy · Python

Filter and rank a deck in both directions

Write the two functions in deck.py that a deck builder runs on every candidate it reads from the cell index. A person is a dictionary such as {"id": 7, "age": 31, "gender": "M", "wants": "W", "age_min": 26, "age_max": 34, "max_km": 10, "hours_ago": 5}; wants holds the letters of every gender the person wants to see.

eligible(viewer, candidate, km) returns True when the candidate may appear in the viewer's deck, where km is the distance between their squares. Every rule holds in both directions:

  • each person's gender is in the other's wants;
  • each person's age is from the other's age_min to age_max, both ends included;
  • km is at most the viewer's max_km and at most the candidate's max_km;
  • nobody appears in their own deck.

build_deck(viewer, candidates, swiped, blocked, size) takes (person, km) pairs and returns the ids of the deck: the eligible candidates whose ids are in neither swiped nor blocked, nearest first, then the most recently active (the smaller hours_ago), then the smaller id, and at most size of them.

Starter code · deck.py

def eligible(viewer, candidate, km):
    """True when the candidate may appear in the viewer's deck; km is the distance between their squares."""
    # Replace this line with your code.
    return True


def build_deck(viewer, candidates, swiped, blocked, size):
    """The ids of the viewer's deck from (person, km) pairs: eligible, not swiped or blocked, best first."""
    # Replace this line with your code.
    return [person["id"] for person, km in candidates][:size]
The sample tests · test_deck.py
from deck import build_deck, eligible

HER = {"id": 1, "age": 29, "gender": "W", "wants": "M", "age_min": 27, "age_max": 35, "max_km": 8, "hours_ago": 0}


def person(id, **changes):
    base = {"id": id, "age": 30, "gender": "M", "wants": "W", "age_min": 25, "age_max": 34, "max_km": 15, "hours_ago": 3}
    base.update(changes)
    return base


def test_a_good_pair_is_eligible():
    """both people's settings accept each other"""
    assert eligible(HER, person(2), 3) is True


def test_genders_both_ways():
    """she wants men, and the man must also want women"""
    assert eligible(HER, person(2, gender="W", wants="W"), 3) is False
    assert eligible(HER, person(2, wants="M"), 3) is False
    assert eligible(HER, person(2, wants="MWN"), 3) is True


def test_ages_both_ways_ends_included():
    """her range is 27 to 35, and her age 29 must be in his range"""
    assert eligible(HER, person(2, age=27), 3) is True
    assert eligible(HER, person(2, age=35), 3) is True
    assert eligible(HER, person(2, age=26), 3) is False
    assert eligible(HER, person(2, age=36), 3) is False
    assert eligible(HER, person(2, age_min=29), 3) is True
    assert eligible(HER, person(2, age_max=29), 3) is True
    assert eligible(HER, person(2, age_min=30), 3) is False
    assert eligible(HER, person(2, age_max=28), 3) is False


def test_distance_within_both_limits():
    """at most her 8 km and at most his own maximum"""
    assert eligible(HER, person(2), 8) is True
    assert eligible(HER, person(2), 9) is False
    assert eligible(HER, person(2, max_km=5), 5) is True
    assert eligible(HER, person(2, max_km=5), 6) is False


def test_never_yourself():
    """the viewer is not a candidate in her own deck"""
    assert eligible(HER, dict(HER, gender="M", wants="W"), 0) is False


def test_deck_filters_swiped_and_blocked():
    """swiped and blocked ids never come back"""
    candidates = [(person(2), 3), (person(3), 2), (person(4), 1), (person(5, age=40), 1)]
    assert build_deck(HER, candidates, swiped={3}, blocked={4}, size=10) == [2]


def test_deck_order_and_size():
    """nearest first, then the most recently active, then the smaller id; at most size cards"""
    candidates = [
        (person(10, hours_ago=1), 4),
        (person(11, hours_ago=9), 2),
        (person(12, hours_ago=2), 2),
        (person(13, hours_ago=2), 2),
        (person(14, hours_ago=0), 7),
    ]
    assert build_deck(HER, candidates, swiped=set(), blocked=set(), size=10) == [12, 13, 11, 10, 14]
    assert build_deck(HER, candidates, swiped=set(), blocked=set(), size=3) == [12, 13, 11]
A hint

Write one helper, allows(a, b), that answers whether person a's settings accept person b: the gender, the age range and nothing about the distance. Then eligible is allows(viewer, candidate) and allows(candidate, viewer) plus the two distance limits and the check on the ids. For the order, sorted accepts a key that returns a tuple, such as (km, hours_ago, id), and compares the tuples item by item.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Then practise, weakest idea first:

Practice that adapts to you

Practise this lesson

9 questions about 10 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 Which settings must both people's profiles allow before one appears in the other's deck?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: The genders each wants to see, an age range and a maximum distance

    A profile appears only when both people's settings allow the pair, so the builder checks genders, ages and distances both ways.

  2. Question 2 of 9 Meera's maximum distance is 8 km and Kabir's is 5 km. They are 6 km apart. Does either see the other?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: Neither, because 6 km is beyond Kabir's own maximum

    Each rule holds in both directions, so the pair is visible only within the smaller of the two maximum distances.

  3. Question 3 of 9 Each swipe in the history takes 32 bytes. How many gigabytes does a day of 2 billion swipes add?

    Type a number.

    Show the answer: Question 3 of 9

    Answer: 64 GB a day (anything from 57.6 to 70.4 counts)

    2,000,000,000 × 32 bytes = 64,000,000,000 bytes, which is 64 GB a day and about 23 TB a year.

  4. Question 4 of 9 The deck cache keeps the next 100 profile ids, of 8 bytes each, for each of 20 million people. How many gigabytes is that?

    Type a number.

    Show the answer: Question 4 of 9

    Answer: 16 GB (anything from 14.4 to 17.6 counts)

    20,000,000 × 100 × 8 bytes = 16,000,000,000 bytes, or 16 GB: small enough to keep in memory on a few cache servers, with replicas.

  5. Question 5 of 9 Which part of the estimate is the heavy work of the system?

    Choose one answer.

    Show the answer: Question 5 of 9

    Answer: The deck builds, because each reads thousands of people to keep a hundred

    The biggest number is not the heaviest load: a few hundred deck builds a second read millions of candidates.

  6. Question 6 of 9 A phone sends a like, the reply is lost, and the app sends the same like again. Why is it stored once?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: The app made a swipe id, and a retry with the same id is stored once

    A write that can be retried needs an identity the client chose; the swipe id lets the server recognise the second copy.

  7. Question 7 of 9 The location call takes a latitude, a longitude and their accuracy. What does the server keep?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: Only the person's 1 km square, which it never returns

    Keeping only the square serves both the cell index and the distance labels, and leaves nothing finer to leak.

  8. Question 8 of 9 Why does swiping feel instant?

    Choose one answer.

    Show the answer: Question 8 of 9

    Answer: Decks are built ahead of time, so each swipe is a small write and the next cards are already on the phone

    The deck builder does the heavy reading ahead of need, and the app fetches 25 cards at a time, so a swipe only writes a few dozen bytes.

  9. Question 9 of 9 The deck service fills in cards from profiles before serving them. What else does it do?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: It checks blocks again and turns distances into labels such as "4 km away"

    Serving is where the latest blocks apply and where distances become rough labels, so a deck built earlier is still safe to show.

The deep dives this design must answer

A round then probes these questions:

  • Two likes at once: the like and the check share one record per pair, so exactly one of two simultaneous likes creates the match.
  • Crowded cities: a city-centre cell holds far more people than a rural one, which changes how a build reads it.
  • Fresh decks: a deck is rebuilt when it runs low, when the person moves or changes settings, and when it ages.
  • Past swipes: a Bloom filter: A small bit array built from a set of keys that answers "definitely not here" or "maybe here", never a false no. LSM engines keep one per file to skip the files that lack a key. More in “Storage engines: B-trees vs LSM-trees” of each person’s swiped ids keeps profiles from coming back cheaply.
  • Location privacy: distances come from coarse squares and are rounded, so the most anyone learns is a square.
  • Blocks: they are checked when cards are served and messages sent, not only when decks are built.
  • Scale: the bottlenecks move at 10 × and 100 × the swipes.

Exactly one match for two likes, reproduced Pro: opens with a Pro, Premium or Ultimate pass

Two people who like each other within the same few milliseconds break most first designs. This part runs every order in which two such likes can interleave against four designs, shows which ones always create exactly one match, and proves why.

In this part:

  • Every interleaving of two likes, measured
  • Why the pair record is always right: the invariant and its proof
  • Where the pair records live, with numbers

1 diagram · 2 runnable examples (Python) · about 12 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Design a news feed

Deck builds in crowded cities, measured and sized Pro: opens with a Pro, Premium or Ultimate pass

A deck build costs little in the countryside and a great deal in a city centre at night, because the same area holds far more people. This part measures two ways of reading the cell index, then sizes the deck pipeline around the cheaper one.

In this part:

  • Every cell within reach against rings that stop early, measured
  • Hot cells at night, and how to split them
  • Sizing the deck pipeline, with numbers
  • When a deck is rebuilt

1 runnable example (JavaScript) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Location privacy, safety, failure drills and the level rubric Pro: opens with a Pro, Premium or Ultimate pass

The distance on a card is the most sensitive number in the system, because a few of them can place a person in one building. This part runs that attack against three ways of showing distance, then covers blocks, failure drills, scale and what each level is expected to cover.

In this part:

  • Finding someone from the distances shown, reproduced
  • What the server keeps, uses and shows
  • Blocks and reports that take effect at once
  • Failure drills
  • Bottlenecks at 10× and 100×, with numbers
  • What each level is expected to cover
  • A sample answer in ten minutes
  • A 45-minute self-mock

1 runnable example (Python) · about 7 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

Choose your opening move for each brief:

Checkpoint

Your first decision

  1. Question 1 of 2 Brief: two people like each other within the same millisecond, and likes are stored on 64 shards by the person who swipes. What is your first decision?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Bring both likes of the pair to one record, so one transaction can see both

    Write the two servers' steps side by side and the problem is clear: each needs to see the other's like at the moment it decides. Keying both likes to one record of the pair gives them one place to meet.

  2. Question 2 of 2 Brief: on a festival night, deck builds in the centre of a city of 10 million time out while the rest of the country is fine. What is your first decision?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Compare how many people one build reads in the centre with how many it reads elsewhere

    Start from the estimate: deck builds, not swipes, are the heavy work, and their cost follows how many people a build reads, which depends on how crowded the cells are.

  1. Mid to senior

    Two people like each other within the same millisecond. Likes are stored on 64 shards by the person who swipes, and each server checks for the other like after storing its own. Show how the match can be lost or duplicated, and design detection that always creates exactly one.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Senior to staff

    A security researcher reports that three fake accounts can find where any of your users lives, using only the distance your app shows on each card. Explain the attack, and redesign how distances are computed and shown.

    Model answer, Pro. First step and follow-ups

  3. Senior to staff

    On a festival night, deck builds in the centre of a city of 10 million people take seconds and time out, while the rest of the country is fine. Find the likely cause and fix it without making decks worse.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Build decks ahead of time from the cells around a person, with every filter applied in both directions.
  • A deck build reads thousands of people to keep a hundred: that read, not the swipe rate, is the heavy work.
  • A swipe is a small write to a log; a like also updates one record for the pair.
  • Keep only coarse positions: 1 km squares, and distances in whole kilometres.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.