Design Chess.com
Watch senior+ engineer Uma Abu (Netflix) design Chess.com with his interviewer, another senior+ engineer Dani Saqib (ex-Amazon, ex-Microsoft).
You're asked to design an online chess platform. Players are matched against opponents of similar skill, play a real-time game with a clock that can run as short as one minute per side, see each other's moves within a fraction of a second, and have their rating updated when the game ends. Spectators can watch a game in progress, players who disconnect can reconnect and resume, and cheating with an engine has to be detectable.
Messaging systems move data between people. This system holds authoritative state that both players are trying to change, under a clock where a few hundred milliseconds of unfairness is a lost game. That combination of small state, strict correctness, and hard timing is what makes it a distinct problem.
Clarifying the requirements
- What time controls? Bullet at one minute per side is the constraint that matters. If the shortest game were ten minutes, most of the timing difficulty would disappear.
- Who validates moves? The server, always. Confirming this early rules out an entire class of wrong answer.
- Is matchmaking in scope? Usually yes, and rating-based matching with a wait-time tradeoff is a good deep dive.
- Do we support spectators? A popular game can have tens of thousands of viewers, which is a fan-out problem with a very different profile from the game itself.
- Is anti-cheat in scope? Engine detection is the defining problem of modern online chess, and it's worth asking whether the interviewer wants it.
Assume: bullet through classical time controls, server-authoritative moves, rating-based matchmaking, spectators, and anti-cheat in scope.
Back-of-envelope numbers
- Concurrent games: at peak, so
- Moves: a bullet game averages over , so
- Game state: a position plus move history is roughly , so all live games are , small enough to hold in memory
- Completed games: , or about
- Spectators on a top game: , which is a larger fan-out than the game itself
The state figure is the important one. A hundred megabytes of live game state means every active game can live in memory, and that fact is what makes sub-100ms move handling achievable.
High-level architecture
- Connection gateway. Holds each player's WebSocket.
- Game server. Owns a set of games; validates moves, runs clocks, and decides outcomes.
- Live game state. Positions and clocks in memory, with a durable write-ahead record for crash recovery.
- Game archive. Completed games, stored for history and analysis.
- Matchmaker. Pairs waiting players and assigns the game to a server.
- Spectator fan-out. A separate broadcast path so viewers never affect game latency.
- Spectators. Read-only, and tolerant of a short delay.
- Anti-cheat. Analyzes completed games offline.
Deep dive 1: one owner per game
The single most important structural decision is that each game has exactly one authoritative owner, a specific process holding that game's state in memory.
The alternative, where any server can handle any move with state in a shared database, creates a race the moment both players act near-simultaneously, which in bullet chess is constant. Two servers read the same position, both validate a move against it, and both write. Now the game has forked. Avoiding this with distributed locking adds a round trip to every move, which is exactly the latency the product can't spend.
Routing both players' connections to the game's owner makes move handling a single-threaded operation on in-memory state: validate against the current position, apply, update the clock, broadcast. No locks, no coordination, and the ordering question answers itself because one process sees the moves in a definite order.
Durability comes from an append-only move log rather than from writing the position. Chess state is fully reconstructible by replaying moves from the start, so each accepted move is appended to a durable log and the position is derived. If the game server dies, another loads the log, replays it, and resumes.
Server crash needs a clock policy, and it's a good detail to raise unprompted: replaying the log restores the position exactly, but the elapsed time during the outage is genuinely ambiguous. Crediting both players the lost time is the fair resolution, and having an answer here shows you've thought about what recovery means for a timed game rather than only for state.
Deep dive 2: the clock
In a one-minute game, the clock is the product. Getting it wrong is more visible than any other bug in this system, because players lose games to it.
The server owns the time, absolutely. The client displays a countdown for responsiveness, but the authoritative remaining time is computed server-side from when each move arrived. A client-authoritative clock is trivially cheated.
Charge time from move receipt, not from move broadcast. The measured interval is between the server receiving the opponent's move and receiving this player's move. That interval includes the player's network latency in both directions, which is the fairness problem: a player on a slow connection loses real time to the wire.
Chess servers handle this with lag compensation, crediting back a measured estimate of the player's round-trip time, capped so it can't be gamed by an artificially slow client. Naming the mechanism and the cap is what makes this a senior answer, because the cap is the part that stops the compensation from becoming an exploit.
Two more clock details that come up:
- Increment and delay are per-move time additions that the server applies on accepting a move. Mechanically simple, but they belong in the state so a reconnecting client renders the right value.
- Flagging is a server-side decision. When a clock hits zero, the server ends the game rather than waiting for a client to notice. A timer per game handles this, and it must fire even when neither player is doing anything.
Deep dive 3: matchmaking
Matchmaking is a queue with a quality-versus-wait tradeoff, and the design is mostly about how that tradeoff is expressed.
Players enter a queue keyed by time control, and the matcher looks for an opponent within a rating band. The mechanism that makes it work is widening the band with waiting time: start at ±50 rating points, widen every few seconds, and accept the best available match once the band is wide enough. A fixed band either matches instantly at low quality or makes strong players wait forever, because there are very few of them.
- Partition the queue by time control and pool. Bullet, blitz, and rapid are separate populations, and rated and casual shouldn't mix.
- Thin populations need explicit handling. A 2800-rated player at 4am has no peers online, and the band has to widen far enough to find someone or the player waits indefinitely. Capping the wait and accepting a worse match is the right call.
- Assign the game to a server with capacity at match time, and route both players there.
Rating updates go through Elo or Glicko after the game ends, and it's worth noting that the update should be transactional with recording the result. A crash between "game ended" and "ratings updated" that loses the rating change is a support ticket, and the standard fix is to write the result and the rating change together, or to derive ratings from the result log.
Deep dive 4: spectators and anti-cheat
Spectators are a separate broadcast path, and keeping them off the game path is the design's key move. A top game with fifty thousand viewers generates far more outbound traffic than the game itself, and none of it can be allowed to add latency between the two players.
The separation is straightforward once stated: the game server publishes each accepted move to a broadcast channel, and a fan-out tier delivers to viewers. Viewers tolerate a delay of a second or more, which means their path can batch, buffer, and even serve through a CDN for very popular games. A deliberate broadcast delay is also the standard defense against a spectator relaying moves to an engine and feeding them to a player.
Anti-cheat runs offline, on completed games. Real-time detection isn't feasible and isn't necessary, since the response is account action rather than in-game intervention. The signals stack up:
- Move-quality correlation. Compare each move against an engine's preferred moves and score how closely the player tracks it. A player consistently matching a top engine well above their rating is the primary signal.
- Timing patterns. Human move times vary with position complexity, since hard positions take longer. Uniform timing regardless of difficulty is a strong tell.
- Rating trajectory. Sudden step-changes in performance rather than gradual improvement.
- Behavioral and device signals. Tab-switching during games, and account or device linkage to previously banned accounts.
Two points that make this a stronger answer than the list alone: no single signal is sufficient, and the design should combine them into a score with a human review step, because a false positive bans a legitimate player. And the analysis is a batch pipeline over the archived game log, which means it can be re-run with better detection later across historical games, which is a genuine advantage of storing complete move logs rather than just results.
Common pitfalls
- Client-authoritative move validation. Trivially cheated; the server must validate every move against the current position.
- Any-server-handles-any-move with shared state. Simultaneous moves race, and locking adds latency the product can't afford.
- Charging the player for network latency with no compensation. Players on slower connections lose games to the wire.
- Spectators on the game's delivery path. Fifty thousand viewers add latency to the two people playing.
- Banning on a single anti-cheat signal. False positives ban legitimate players.
Leveling the answer
Related lessons
Let multiple users edit the same document at the same time, converging on one state.
Deliver messages across multiple devices with read receipts and server-stored history.