System-design interview · Extended interviews
Design a leaderboard with exact snapshot ranks
Maintain trusted cumulative scores and a single ordered index per board, handling ties, duplicate results and downward corrections before introducing distributed rank queries.
You will learn to
- Define competition rank separately from display order using tied scores.
- Trace one trusted match result into a versioned ordered projection without duplicate points.
- Scale independent boards and shared reads while stating the limits of one very large global board.
Practice in this chapter
8 interview questions with model answers and follow-ups.
Go to interview practiceUseful foundations: Database indexes: B-trees, composite keys and query access · Data partitioning and sharding · Message queues, event logs, delivery guarantees, and backpressure · Caching: cache hits, misses, write policies and invalidation
Workload and timing examples are interview assumptions.
Dotted concept links open the relevant explanation in a new tab.
01Define score, rank and the board being ranked
Build seasonal game leaderboards offering top 100, a player's rank and nearby competitors. Scores are cumulative contributions from trusted match-result services, with authorized corrections that may decrease a total. Do not accept arbitrary browser-submitted scores. Anti-cheat model training and matchmaking are separate systems, although producer authorization and correction audit belong here.
Competition rank is one plus the number of players with a strictly higher score. Use the following scores and shared ranks:
| Player | Score | Competition rank |
|---|---|---|
| P1 | 920 | 1 |
| P2 | 900 | 2 |
| P3 | 900 | 2 |
| P4 | 880 | 4 |
Display order needs a deterministic tie-breaker, but it must not turn P3's shared rank into 3. Choose descending player ID for equal-score display order in this exercise, matching the selected ordered-index query direction. Thus P3 displays before P2, while both retain rank 2.
The initial design gives each season/region board one authoritative ordered index, supports a bounded largest board and scales many boards independently. Top lists may be cached for a second; personalized rank is exact for the single-index state read, not a claim of globally instantaneous agreement with newly accepted scoring events.
A successful score update may precede its displayed ranking while the projection catches up. Show that delay honestly. A hundred-million-player globally sharded exact rank is a stronger extension with additional coordination, not the default architecture hidden behind one endpoint.
Clarify whether the ranking is global or per season/region, how tied scores rank, and how quickly an accepted score must appear. Choose bounded boards with exact competition rank for each coherent index read and explicitly delayed score projection; a globally sharded instantaneous rank is a different prompt.
02Functional requirements
Accept trusted scoring events. Record match contributions and authorized revisions, including corrections that reduce a player’s total.
Read rankings. Provide top 100, a player’s competition rank and a bounded neighborhood with deterministic tie display order.
Manage seasons and awards. Separate season/rule namespaces, close under a stated cutoff and completeness policy, and publish an auditable frozen award result.
Recover score history. Deduplicate retries, expose ranking freshness and rebuild the ordered view from durable score evidence and versioned updates.
03Non-functional requirements
These are illustrative interview assumptions, not product facts or measured benchmarks. Confirm them before choosing components, then validate the completed design under the stated workload. Here p95 means the 95th-percentile latency: 95% of measured requests take no longer than that value. Report errors and rejected work alongside latency; a fast failure is not a successful outcome.
Fleet and board scale. Plan for 100 million player/board records, 10,000 score events/s and 50,000 top-list reads/s across the fleet. Keep the largest initial board at one million players; assume at most 1,000 score updates/s and 5,000 uncached rank/neighborhood reads/s on one board for its load test.
Read latency and visibility. Target top/rank/neighborhood reads below 200 ms p95 under admitted load. Target 99% of accepted score changes reaching the ordered index within one second. For updates that change top-100 membership, ordering or displayed scores, target 99% of those updates being incorporated into a served top-list snapshot within two seconds of acceptance, including the one-second cache interval. The snapshot may already include a newer accepted update. An update to a player outside the top 100 need not appear in that list. Stalled projection is a measured miss and must expose age.
Ranking correctness. Competition rank is one plus the number of strictly higher scores. Read the player’s score and comparison population coherently; tie display order does not change shared rank, and a retry cannot award the same match twice.
Durability and recovery. Committed score evidence and outbox state survive scoring-service restarts; the ordered index and cache may be rebuilt. Do not acknowledge scores based only on a disposable index. The database’s replica/region disaster policy remains a separate deployment requirement.
Authority and fairness of evidence. Accept scores only from authorized producers, audit corrections and rule changes, and enforce board/friend permissions. Freeze awards only after checking the agreed cutoff’s evidence is complete; a cache refresh alone cannot establish completeness.
04Complete scoring and ranking with one ordered board
Start with an authenticated scoring API, a transactional score database and an ordered index for each board. A hash lookup answers P2's current score, while an ordered structure supports top ranges and counts above a score. A Redis sorted set is one practical projection; the score database remains the recoverable source.
Trusted event E77 reports match M91 awarding P2 thirty points. One database transaction records its business identity and match contribution, changes P2 from 900 to 930, advances the player's version and stores an outbox update. A worker applies the complete new total to the ordered index only if its version is newer than the stored projection version.
The index update changes score and version atomically. A repeated version 12 update does not add thirty again, and a delayed version 11 cannot restore the old score. After application, P2 is above P1 and has rank 1. Top-list reads return the highest entries under the chosen display comparator.
For a rank read, obtain P2's score and the count of strictly higher scores in one bounded atomic index operation. Reading the score first and counting later could compare it with a different board state. The score and comparison count therefore describe the same index state, although scoring-to-index lag remains an explicit freshness limitation.
Accepted scores are durable before the display projection catches up.
Read each connection in order
- syncMatch, revision, points, event IDTrusted match service → Scoring API
- syncAtomic contribution and totalScoring API → Match contributions, totals, outbox
- asyncComplete player total + versionMatch contributions, totals, outbox → Versioned projection worker
- syncApply only a newer versionVersioned projection worker → One ordered index per board
- syncCoherent ordered queryTop / rank / nearby API → One ordered index per board
05Separate fleet size from one hot board
Assume 100 million active player/board records across many seasonal or regional boards, 10,000 score events/s across the fleet and 50,000 top-list reads/s. A bounded largest board initially has up to one million players; benchmark its assumed 1,000 score updates/s and 5,000 uncached rank/neighborhood reads/s on one index against the 200 ms p95 target. These are proposed capacity requirements, not measured capabilities.
At 64 bytes of raw player state, the fleet payload is about 6.4 GB, but ordered indexes, version lookups, allocators and replicas require more. At an illustrative 128 bytes per indexed player, a million-player board uses about 128 MB before additional overhead and copies. That explains why many useful boards can remain on one ordered owner, while still requiring measurement of real memory use.
Ten thousand 100-byte score events/s create 1 MB/s or 86.4 GB/day of history. Ninety days require 7.776 TB before indexes and replicas. Retaining auditable scoring evidence can cost far more than caching a top list.
A top 100 response at 48 bytes per entry is 4.8 KB; 50,000/s produces about 240 MB/s of response payload. Many readers can share a cached complete top list instead of recomputing it. Personalized rank and neighbor reads are less shareable, so size them separately rather than assuming one cache solves every query.
06Represent match contributions and projection versions
Interfaces
| Request or message | Contract |
|---|---|
POST /score-events |
Trusted season, player, match, source revision, points and event identity. |
GET /boards/S4/top?limit=100 |
Complete cached top list with display order and as-of information. |
GET /boards/S4/players/P2/rank |
Player score and competition rank from one coherent index read. |
Read P2’s competition rank
GET /boards/S4/players/P2/rank
Complete projection update after the worked correction
{
"season": "S4",
"player": "P2",
"total": 905,
"version": 13
}
Stored records
| Record | Fields or identity | Purpose |
|---|---|---|
| Match contribution | season, player, match |
Last accepted source revision and awarded points. |
| Player total | season, player, total, version |
Current authoritative cumulative score. |
| Accepted event / outbox | Accepted event and player-total version | Audit evidence and recoverable complete versioned total. |
| Ordered projection | Board/player and applied version | Score order plus player version used to reject delayed updates. |
An event ID suppresses transport retries, but a second event ID for the same match must not award its points again. The stored match contribution and its source revision prevent awarding the same match twice. Retrying identical accepted content returns the prior result; conflicting reuse of an identity is an error.
Scope every query and cache key by season, region and scoring-rule version. The same player can have different scores on different boards. A top 100 means one hundred display positions, not every player tied at the boundary. If the product wants all boundary ties, its result size and pagination contract must change explicitly.
07Apply corrections without repeating or losing points
For E77, the stored contribution for P2's match M91 changes from zero to thirty. The owner calculates the change:
Contribution change
delta = new contribution − previous contribution
It adds that delta to P2’s total within the same transaction that saves the contribution revision, accepted event and outgoing update. P2 moves from 900 to 930 at version 12.
Now an authorized correction changes that match contribution from thirty to five. The derived delta is −25, so the total becomes 905 at version 13. A delayed older revision cannot restore thirty or subtract twenty-five again. The source revision orders that match's evidence; the player version orders resulting total changes across all its matches.
Send complete versioned totals to the projection, not unprotected increments. A repeated message saying “total 905, version 13” can be recognized and ignored. Repeating “subtract 25” would corrupt the board unless separately deduplicated. A lower numeric score can still be the newer correct version, so comparing score magnitude is not a valid stale-update rule.
Changes to the scoring rule itself need an explicit board version or rebuild plan. Do not apply a new rule to only some old matches and present the mixed result as if every player had been evaluated consistently. Keep immutable evidence and audit correction authority.
The match source revision and resulting player version have distinct jobs.
Read each connection in order
- syncM91 revision 1 awards 30Match service → Scoring database
- asyncP2 total 930, version 12Scoring database → Ordered projection
- syncM91 revision 2 corrects award to 5Match service → Scoring database
- asyncP2 total 905, version 13Scoring database → Ordered projection
- asyncDelayed total 930, version 12Scoring database → Ordered projection
- syncIgnore older version; retain 905Ordered projection → Ordered projection
08Explain ties, atomic reads and nearby results
For P2 at 900, count players with scores strictly above 900 and add one. P1 is the only such player, so P2 and P3 both have rank 2. A sorted-set ordinal position answers a different question: it includes the display tie-breaker and therefore does not directly implement shared competition rank.
With a Redis-style index, a strict score-range count such as scores greater than 900 supplies the needed count. Read the player's score and that count together through the chosen atomic script or transactional operation so another update cannot change the comparison between the two reads. Keep the operation bounded and use the index's supported count query rather than scanning all members in an application loop.
For top 100, fetch the ordered entries in one index read. Compute displayed shared ranks using strictly higher scores, carrying the preceding score/rank through the list. The chosen descending-ID tie-breaker affects ordering only. A large tie group can occupy many display positions while all its members share one rank.
Nearby-player results use the same score order and tie comparator. This default API offers a bounded neighborhood, not unlimited stable historical pagination. If users need to page through a fixed old board while scores change, retain a snapshot explicitly; persistence files are not automatically a historical query API.
09Cache shared reads and distribute independent boards
Cache each complete top list for a short interval with its board identity and as-of metadata. Thousands of viewers can reuse the same hundred entries, reducing index work. Cache the whole coherent result rather than mixing rows from several refreshes. Personalized rank remains a separate current-index operation and should not combine a fresh authoritative score with an older cached population.
Assign different season/region boards to different index owners. This provides straightforward aggregate scale while keeping each board's top, rank and neighbors local. Replicas can improve read availability under their disclosed lag; failover and rebuild must preserve the projection's version state or reconstruct it from the score source before accepting new updates.
For a single board that exceeds one owner's memory or throughput, discuss the changed cost explicitly. Hashing complete players distributes updates, and global top k can be obtained by merging every shard's local top k under the same comparator. A globally winning player cannot have k better players on its own shard and still be globally top k.
Exact personalized rank is harder: every shard’s count and the player’s score must describe the same board snapshot. The baseline deliberately avoids promising that distributed snapshot protocol. Options include a coordinated snapshot, a separately labeled approximate percentile, or retaining a larger single ordered authority after benchmarking.
A trusted result commits the match contribution and complete player total before the projection worker updates the board index. Readers reuse complete cached top lists or perform coherent rank reads against one board owner. Independent season/region boards scale across owners; exact cross-shard personalized rank is outside this selected diagram.
Read each connection in order
- syncMatch result or correctionTrusted score producer → Scoring API
- syncCommit contribution and totalScoring API → Score / contribution / outbox DB
- syncRead versioned score workProjection workers → Score / contribution / outbox DB
- asyncApply newer complete totalsProjection workers → Season / region board indexes
- syncTop / rank / neighborsLeaderboard reader → Leaderboard query API
- syncRead or fill cached top listLeaderboard query API → Complete top-list cache
- syncAtomic rank / top-list fillLeaderboard query API → Season / region board indexes
10Close a season from complete scoring evidence
Create a new season namespace instead of synchronously zeroing every old player. Scores, match identities, rules and caches include the season so old retries cannot silently award points in the new competition. Closing a season has an announced cutoff and an allowed-lateness/adjudication policy.
For the bounded single-authority baseline, stop accepting ordinary season scores at the chosen cutoff, drain all previously accepted outgoing updates, and verify the ordered projection against the authoritative totals before freezing the final award result. Publish an immutable award version with the rule and cutoff used. A cache refresh time alone is not proof that every eligible match arrived.
If the contract uses match event time rather than acceptance time, trusted sources must establish completeness through that cutoff or the rules must explicitly define which late results are reviewed. Waiting an arbitrary second does not prove that an offline match source has finished sending results.
A later fraud finding or accepted correction produces a new audited award-decision version rather than silently rewriting the originally awarded snapshot. This separates live display freshness from the stronger reproducibility required for prizes. Keep enough scoring evidence to explain the final result; the tiny top-list cache is not the evidence archive.
11Rebuild the index without treating it as score truth
| Failure | Recovery |
|---|---|
| Scoring transaction commits, reply disappears | Retry the same event/business identity and return its saved total version. |
| Outbox update repeats | Atomically reject an already applied or older player version. |
| Projection worker stops | Keep scores durable; show display lag while recovering pending updates. |
| Ordered index is lost | Rebuild from a consistent player-total snapshot plus subsequent versioned changes. |
| Cache refresh fails | Serve a labeled older complete list within policy, or fail; do not fabricate a partial latest board. |
A rebuild must include player versions as well as scores, then catch up updates after the source snapshot before becoming current. Copying arbitrary rows while scores change without a replay boundary can miss or double-apply changes. Use the database's consistent snapshot and change-stream/outbox recovery capabilities.
During overload, protect authoritative score acceptance and recovery work before optional personalized queries. Bound top-list limits, neighborhood size and private-board populations. The service can deliberately display an older complete top list while recovering, but must expose its age rather than call a stalled index live.
A future distributed board cannot omit one failed shard and still return an exact global rank. That missing population may contain every player above P2. Use a previously complete snapshot or fail the exact query under the stronger design's declared contract.
12Test the ranking rules rather than only endpoint speed
Monitor scoring acceptance, score-to-index lag, duplicate-event suppression, conflicting revisions, index rebuild progress, memory use and per-board read/write hotspots. A healthy cached top endpoint can hide a scoring pipeline that has not updated for an hour. Track the oldest unapplied score event directly. Measure accepted-score-to-index delay against one second for 99% of updates. For updates that affect top-100 membership, order or displayed scores, measure acceptance-to-visible-effect against two seconds total, including cache time; outages still count as visible misses.
Test tied scores, ties crossing the hundredth display position, E77 arriving twice, the same match under different event IDs, downward corrections and old revisions arriving late. Compare sampled ranks with a slow sort of the same source snapshot. Verify that the selected ordered-index tie behavior matches the API comparator rather than assuming reverse-score queries keep ascending member order.
Authorize score producers and corrections, audit rule changes and protect private friend graphs. A friend-only board ranks within the authorized friend population; filtering a global top 100 afterward can omit all relevant friends outside that global list. For a small friend set, retrieve their complete scores and rank that bounded set directly.
The primary limits are one very hot board, required historical snapshots and exact global rank under sharding. Keep those visible. The first coherent design can meet a useful bounded product without compressing a distributed publication proof into an unexplained “generation” field on every response.
13Check the design against its requirements
Use the numbered requirements to check the final design. FR refers to the functional list; NFR refers to the non-functional list. Performance rows specify tests still required, not achieved benchmark results.
| Requirement | Design mechanism | Verification and remaining limit |
|---|---|---|
| FR1; NFR3,5: correct score updates | Canonical match revision, one transactional total change and complete versioned outbox updates. | Replay E77 under the same and different event IDs, then correct thirty points to five. P2 ends at 905 without repeating the award or reversing a newer version. |
| FR2; NFR1–3: fast coherent rankings | One bounded ordered authority per board, atomic rank reads and shared complete top-list caching. | Test 920/900/900/880 → 1/2/2/4; load-test the declared per-board mix against 200 ms p95. Fleet capacity cannot prove the hottest board’s capacity. |
| FR4; NFR2,4: visible, recoverable projection | Durable source/outbox, atomic score-version application and timestamped cache refresh. | Measure acceptance-to-index within one second and, for updates affecting the top 100, their effect in refreshed served lists within two seconds; stop the worker and rebuild the index. Expose misses and older results rather than claiming current rank. |
| FR3; NFR5: reproducible awards | Season namespaces, drained accepted updates, source comparison and immutable award version. | Close while events are pending and deliver an old-season retry. Verify completeness before awarding; late adjudication creates an audited new result instead of rewriting history silently. |
14Rapid revision
Remember: A newer correction can lower a score. Order updates by version, and count strictly higher players for shared rank.
| Concern | Complete mechanism |
|---|---|
| Score rule | Add trusted match contributions; allow authorized corrections with increasing match revisions. |
| Business uniqueness | Event IDs detect retries; match IDs and revisions prevent awarding the same result again under another event ID. |
| Projection | Save a player’s complete score and version together; accept only newer versions. |
| Shared rank | One plus the number strictly above the player's score; ties share rank. |
| Display | Use score and player ID for stable display order; tied players still share competition rank. |
| Coherent read | Read the player’s score and count of higher-scoring players in one atomic index operation. |
| Scale | Cache popular top lists and assign different boards to different servers before splitting one board. |
| Season | Set the cutoff, check all eligible results and freeze an award version; publish later corrections as new audited versions. |
| Recovery | Rebuild from saved player totals and later versioned updates; show how far the display lags. |
Close by tracing P2 from 900 to 930 after E77, then to 905 after correction. Explain why repeating E77 changes neither total, why P2 and P3 once shared rank 2, and why a globally sharded exact rank would require an additional coherent-view contract.
Practise the interview questions
Say your answer aloud before opening the model answer. Then answer the follow-up and compare the reasoning.
What are the ranks for 920, 900, 900 and 880?
Reveal a model answer
Competition ranks are 1, 2, 2 and 4 because each is one plus the number of strictly higher scores.
Interviewer follow-up
Does a player-ID tie-break change them?
Reveal the follow-up answer
No. It orders display positions, not shared competition ranks.
What the answer must demonstrate: Define strict-greater competition rank separately from tie-broken display position.
Why is event-ID deduplication insufficient for match awards?
Reveal a model answer
The same match may arrive under a different transport event ID. Store its canonical contribution and source revision so business meaning is applied once.
Interviewer follow-up
What commits together?
Reveal the follow-up answer
Accepted evidence, the contribution change, player total/version and outgoing projection work.
What the answer must demonstrate: Protect canonical match contribution/revision as well as transport-event identity.
A match contribution changes from 30 to 5. What updates?
Reveal a model answer
Derive delta −25 from the stored contribution and apply it atomically with the newer source revision and player version.
Interviewer follow-up
Can the index ignore the update because the score decreased?
Reveal the follow-up answer
No. Version, not score magnitude, determines which state is newer.
What the answer must demonstrate: Derive correction delta from stored contribution and order projection updates by version.
Why read score and count-above atomically?
Reveal a model answer
Another update between those reads can make the comparison describe different board states. One atomic index operation gives a coherent rank.
Interviewer follow-up
Does that mean every accepted source event is already included?
Reveal the follow-up answer
No. Projection freshness is separately measured and disclosed.
What the answer must demonstrate: Read score and population count coherently while disclosing source-to-index lag.
Why send complete totals to the projection?
Reveal a model answer
Repeating a versioned replacement is safely recognizable, whereas an unprotected repeated increment awards points again.
Interviewer follow-up
What if the index disappears?
Reveal the follow-up answer
Rebuild totals and versions from a consistent source snapshot plus later changes.
What the answer must demonstrate: Use complete versioned totals and a consistent rebuild/replay boundary.
Why can global top k merge local top k from each player shard?
Reveal a model answer
A player excluded locally has at least k better local players under the same comparator, so cannot be globally top k.
Interviewer follow-up
Does that alone solve exact distributed rank?
Reveal the follow-up answer
No. Player lookup and all count-above results still need a coherent shared view.
What the answer must demonstrate: Explain local-top-k completeness separately from cross-shard snapshot consistency.
Does waiting one second after cutoff prove an award board is complete?
Reveal a model answer
No. Use the declared late-result policy to establish which accepted inputs count or whether sources have sent all eligible results. Apply those inputs and verify the board before freezing awards.
Interviewer follow-up
What happens to a later fraud correction?
Reveal the follow-up answer
Publish a separate audited adjudication version rather than silently altering the historical award.
What the answer must demonstrate: Establish cutoff completeness and retain immutable award/adjudication evidence.
Can a friend board filter the global top 100?
Reveal a model answer
That can miss every eligible friend outside the global list. Rank within the authorized friend population before truncation.
Interviewer follow-up
What is a simple small-set solution?
Reveal the follow-up answer
Batch-fetch the complete friend scores, then sort and rank that bounded set.
What the answer must demonstrate: Filter the eligible population before ranking or truncating friend results.
Blank-page exercise · 45 minutes
Build the answer yourself
Design a seasonal board with tied scores, duplicate match delivery and a downward correction, then explain the limit of sharding one global rank.
- Agree numbered functional and non-functional requirements, including board population, shared-rank semantics and score-to-display freshness. Then define competition rank separately from display order using tied scores.
- Trace one trusted match result into a versioned ordered projection without duplicate points.
- Scale independent boards and shared reads while stating the limits of one very large global board.
- Trace a timeout and a concurrent request using the actual durable records.
- Review the final architecture against every numbered requirement, including measured bottlenecks, targets still needing validation and remaining failure limits.
Check that each component and design decision follows from your requirements and workload.
Recall the key ideas
Answer from memory before opening each card. Explain why the choice works and what it costs. Revisit missed cards tomorrow.
Design a leaderboard with exact snapshot ranksScores are 920, 900, 900 and 880. Why does the last player rank 4 rather than 3?Recall first, then reveal
Three players have strictly higher scores, so the last rank is 1 + 3 = 4. The two players at 900 both rank 2; tie display order does not change that.
Count higher-scoring players, including tied players above you.
Return to lessonDesign a leaderboard with exact snapshot ranksA correction lowers P2 from 930 to 905. What prevents an old message from restoring 930?Recall first, then reveal
The stored match revision determines the correction, and the newer player version makes the index keep total 905 even if an older total arrives later.
Newer can be lower.
Return to lessonDesign a leaderboard with exact snapshot ranksWhat should scale before splitting one leaderboard across servers?Recall first, then reveal
Cache shared top lists and place independent boards on different servers.
Scale boards before global order.
Return to lessonFinal revision
Summary and interview notes
Add trusted match contributions to each player’s score and maintain one ordered index per board. Handle tied scores, repeated results and corrections that lower scores before splitting one board across machines.
Remember these points
- Define how points accumulate and how tied scores rank first.
- Protect business contributions as well as transport events.
- Read the player’s score and higher-score count from the same index state.
- Expose projection lag and the extra cost of exact global sharding.
Interview tips
- Separate score revisions, display order and competition rank.
- Trace P2 from 900 to 930 to 905, then replay the old thirty-point award.
Important qualifications
- Traffic and latency figures are interview assumptions, not claims about a named company's deployment.
Continue after the core interview
Explore the advanced version
The advanced lesson keeps the full detailed design. Use these sections when you want to examine the stronger requirements and failure cases.
- Exact distributed snapshot rank
One enormous sharded board needs coherent counts and player lookup across all owners.
- Full score and top-k proofs
Study match revisions, atomic projection replacement and merge completeness in depth.
- Coordinated generation publication
Retained cross-shard snapshots add ownership, storage and slow-shard coordination.
- Score-range and histogram alternatives
Approximate percentiles and alternative layouts change exactness or update costs.
Technical references
- Redis sorted setsDocuments ordered members, score updates, and range operations as an implementation option.
- Redis ZCOUNTDefines inclusive/exclusive score boundaries used for competition-rank counts.
- Redis ZRANGEOfficial reverse-order and tie ordering behavior; generation snapshots remain an application/index requirement.
Practice marks stay in this browser.