</> MAANG.io
system design · 301

Advanced

Master system design interviews with scalable architecture patterns, distributed systems, and real-world design challenges.

0/480 solved 0% complete

Section 4: Evidence, Not a Number - API Design (with Pushback)

Executive Summary: This section designs the whole public contract of the leaderboard: one write endpoint and five reads. Every shape is defended under pressure. The write accepts a run, not a score, and answers with both ranks so the player reads their own write without a second call. The reads are bounded on purpose: the top 100 comes from a cache, around-me and friends are personal views keyed by the token, and paging stops at rank 10,000. We then work through the interviewer's pushback: why there is no PUT /score, why the POST returns ranks, why paging is capped, why me sits in the path, and what 409, 422 and 429 each mean. The contract is small, but each line of it is doing a job.

Section 3 ended with a reassuring result: one sorted set per board fits, and exact ranks cost O(log N). The interviewer nods, caps the marker and says, "Fine. Show me the API."

Most candidates now write POST /score with a body of {playerId, score}. That one line lets any client name any player, lets it set any score, and gives the server no way to tell a retry from a new run. The Staff answer starts from what the client is allowed to claim:

POST /v1/runs                                          submit a finished run, get both ranks back
GET  /v1/boards/{boardId}/top?limit=100                the top 100 (cached)
GET  /v1/boards/{boardId}/me/around?radius=5           the players just above and below me
GET  /v1/boards/{boardId}/me/friends                   me and my friends, ranked
GET  /v1/boards/{boardId}/players/{playerId}           one player's public rank
GET  /v1/boards/{boardId}/page?fromRank=&limit=50      paging, capped at rank 10,000

boardId:  weekly-2026-W44  |  all-time

The surface has six endpoints and no write that names a score to store.

The Write: Submitting Evidence

Kai has just finished run r-7f3a91 in Manila. Their client sends this:

POST /v1/runs
Authorization: Bearer <token>

{
  "runId": "r-7f3a91",
  "endedAt": "2026-10-28T21:14:00Z",
  "durationMs": 1140000,
  "score": 1284500,
  "ticket": "<server-signed run ticket>",
  "summary": "<compact run summary>",
  "signature": "<install-key signature over the summary>"
}

Every field has a job:

  • runId is generated on the device before the first attempt to send. It is the idempotency key.
  • endedAt is event time, so it decides which week the run belongs to. A run that ends before Monday 00:00 UTC still counts for the old week if it arrives within the 5-minute grace. A clock on the device is a claim, not a fact, so the server bounds it: ticketIssuedAt + durationMs ≤ endedAt ≤ its own receive time.
  • ticket is a server-signed run ticket issued when the run starts. Its ticketIssuedAt is the lower bound above, so a backdated endedAt can't win ties. Section 10 derives the tie time from it.
  • durationMs is what makes the score checkable. The plausibility bound is ≤ 2,000 points per second of run time.
  • summary and signature are the evidence: a compact record of the run, signed with a key held by that install. Section 10 explains how they are verified and replayed.

Notice what the body does not contain: a playerId. Identity comes from the token. Kai's playerId, 48213377, never appears in a request Kai can edit.

The response gives the player everything the post-run screen needs about themselves:

200 OK
{
  "weekly":  { "rank": 88416,   "best": 1284500, "improved": true,  "status": "accepted" },
  "allTime": { "rank": 1042311, "best": 1350200, "improved": false, "status": "accepted" }
}

That is a new weekly best, so Kai jumps from 412,907 to 88,416. The all-time best stays 1,350,200, so improved: false and the rank is unchanged. status is per board, and both say accepted because Kai is nowhere near the top 1,000. A run that enters the top 1,000 on a board comes back provisional on that board alone. The player sees their new rank at once, but nobody else does until replay validation finishes, which takes ≤ 15 minutes. If the board step fails after the run is durable, the submit still succeeds with "ranksPending": true and null ranks.

Sequence of a run submission: Kai's client posts run r-7f3a91 to the regional edge, the Run Validator verifies and dedupes it, the Score Service appends it to Kafka with acks all, applies ZADD GT CH and ZREVRANK on the board primaries, and returns weekly rank 88416 and all-time rank 1042311

The budget for this whole exchange is p99 ≤ 300 ms worldwide, of which ≤ 50 ms is server time in the home region. The rest is the trip from Manila.

Quick check before moving on:


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.