Think Beyond the Happy Path
- Latency: How do we consistently achieve sub-200ms move propagation while still validating rules, updating clocks, and persisting moves safely under peak load?
- Correctness under concurrency: If both players submit actions at nearly the same time, how do we guarantee strict move ordering, prevent double-accepts, and avoid state forks?
- Fault tolerance: If a game server crashes mid-match, what must already be persisted, and how do we recover the authoritative state without replaying or skipping moves?
- Scalability: With 500,000 concurrent games, how do we distribute sessions, avoid hotspots, and prevent leaderboard updates from impacting live gameplay?
- Cross-device resume: When a player reconnects from another device, how do we prevent duplicate actions and deterministically reconcile session ownership?
Problem Statement
Design a distributed chess platform that allows millions of users to play real-time games against opponents matched by skill level. The system must support simultaneous games, enforce chess rules server-side, maintain accurate game clocks, and provide instant move updates with sub-200ms latency. Players should be able to start games through automated matchmaking or by sending direct challenges to friends.
The platform needs to handle 500,000 concurrent games during peak hours, store complete game histories indefinitely for later review and analysis, and maintain global leaderboards that update within seconds of game completion. The system must prevent cheating through client-side manipulation while ensuring players can seamlessly resume interrupted games from any device. Consider how you'll handle network failures, concurrent move attempts, and the complexities of features like offering draws or requesting move takebacks.
Functional Requirements
FR1. Game Set: Matchmaking and Game Creation
The use case is that a player clicks "Play" and gets paired with a similarly skilled opponent, or challenges a friend and starts a new match once accepted. Let's truncate this use case:
- First, match with a component:
- Automatically match players by skill level and create a new game session.
- Allow players like friends to send and accept direct challenges to start a private game.
- Once the match is setup, Initialize a new game with:
- assigned sides (white/black),
- starting board state,
- initial clock configuration.
FR2. In Game: Real-Time Gameplay (Server-Authoritative)
The use case is that once the game starts, both players interact in real time and see consistent board updates and accurate clocks.
- During active gameplay:
- Accept move submissions from the current player.
- Validate each move server-side against official chess rules.
- Reject illegal or out-of-turn moves.
- After a move is accepted:
- Update the authoritative board state.
- Update both players' remaining clocks.
- Persist the move with timestamp and metadata for history replay.
- Broadcast the updated state to both players instantly.
Knowledge Tip - Clock in Chess
In real-time chess, each player has a fixed amount of time to complete all their moves, and only the active player's clock counts down during their turn. When a player makes a move, their clock stops and the opponent's clock starts. If a player's remaining time reaches zero before the game ends, they automatically lose on time (unless the position is a draw by rule). Because clock accuracy directly affects fairness, the server must be the single authority for time tracking and turn switching — client-side clocks are only visual representations and cannot be trusted.
FR3. Game Completion: Terminate the game by case
The use case is that players can end games naturally (checkmate, timeout) or through interactions like resignation or draw offers.
- During gameplay:
- Detect end conditions such as:
- Checkmate
- Stalemate
- Draw conditions
- Timeout
- Allow player-triggered actions:
- Resign
- Offer draw
- Accept/reject draw
- Request move takeback
- Detect end conditions such as:
- Once a terminal state is reached:
- Finalize the game result.
- Lock further move submissions.
- Transition the game session to completed state.
- Update the leaderboard with results for both players.
Non-Functional Requirements
NFR1. Correctness and Deterministic Ordering
Chess is a rule-based competitive game. The system must guarantee that every game progresses in a single, deterministic order without inconsistencies.
- For each
game_id, moves must be applied in a strict, authoritative sequence. - The server is the single source of truth for board state and clock timing.
- Once a move is accepted, it must be persisted in the exact applied order.
NFR2. Low Latency (Real-Time Gameplay)
Chess is interactive and clock-sensitive. Move delivery must feel instant to both players.
- End-to-end move propagation should meet sub-200ms latency (p99 target).
- Clock and turn switching must remain visually consistent with the server's authoritative time.
NFR3. Scalability (Peak Concurrency)
The system must support large peak traffic without gameplay degradation.
- Support 500,000 concurrent games during peak hours.
- Scale horizontally so load growth does not require redesigning core game flow.
NFR4. Fault Tolerance (Network and Server Failures)
The system should remain safe and usable under real-world failures.
- Handle disconnects and retries without corrupting game state.
- Concurrent or duplicated client actions must not cause double moves, double resigns, or inconsistent outcomes.
NFR5. Durability (Indefinite History Storage)
Game history is a core product promise and must not be lost.
- Store complete game histories indefinitely for replay and analysis.
- Once a move is acknowledged, it must not disappear due to crashes or restarts.
Requirement Summary
| Functional Requirements (FRs) | |
|---|---|
| Name | Description |
FR1. Game Set: Matchmaking & Creation | Match players by skill level or direct challenge, and initialize a new authoritative game session. |
FR2. In-Game: Real-Time Gameplay | Accept, validate, apply, persist, and broadcast moves with server-authoritative board and clock control. |
FR3. Game Completion | Detect terminal conditions (checkmate, timeout, resignation, draw), finalize result, and update leaderboard. |
| Non-Functional Requirements (NFRs) | |
|---|---|
| Name | Description |
NFR1. Correctness & Deterministic Ordering | Per game, moves must form a single strict order with server-authoritative board and clock. |
NFR2. Low Latency | End-to-end move propagation must meet sub-200ms p99 target. |
NFR3. Scalability | Support 500,000 concurrent games with horizontal scalability. |
NFR4. Fault Tolerance | Network failures, retries, and concurrent actions must not corrupt state. |
NFR5. Durability | Persist complete game histories indefinitely with no loss of acknowledged moves. |
APIs
Knowledge Tip — Why WebSocket for Real-Time Chess and how HTTP "upgrades" to WS
For real-time gameplay, we use WebSocket because it provides a long-lived, bidirectional connection so the server can push moves and clock updates instantly without repeated HTTP polling. Practically, the client first makes a normal HTTP request to obtain game_id and a short-lived join_token, then opens wss://... to the game endpoint. Under the hood, the WebSocket handshake starts as an HTTP/1.1 request containing Connection: Upgrade and Upgrade: websocket. If the server accepts, it returns 101 Switching Protocols. From that point forward, the same underlying TCP connection is kept, but the application-layer framing switches from HTTP messages to WebSocket frames (still running over TCP/IP, and over TLS for wss). This lets us treat REST as the "setup/control plane" and WebSocket as the "in-game data plane."
Now we will follow the user journey during the chess game to illustrate our high level APIs and data communications.
1) Game Setup (REST)
1. Create a match request (level matching or friends challenge)
POST /v1/matches
- Purpose: User A expresses intent to start a game.
- Request (two modes):
Mode A — Level Matching
{
"mode": "LEVEL_MATCHING",
"user_id": "userA",
"skill": { "rating": 1520 },
"clock_config": { "initial_ms": 300000, "increment_ms": 0 }
}
Mode B — Friends Challenge
{
"mode": "FRIENDS_CHALLENGE",
"from_user_id": "userA",
"to_user_id": "userB",
"clock_config": { "initial_ms": 300000, "increment_ms": 0 }
}
- Response:
{"match_id":"m_123","status":"PENDING"}
2. Check match status (poll until matched)
GET /v1/matches/{match_id}
- Purpose: Client checks whether the match has produced a playable game.
If matched
{
"match_id": "m_123",
"status": "MATCHED",
"game_id": "g_456",
"ws_url": "wss://.../v1/ws/games/g_456",
"join_token": "jwt_or_session_token"
}
3. Accept a friends challenge (only for FRIENDS_CHALLENGE)
POST /v1/matches/{match_id}/accept
- Purpose: User B accepts the friend challenge, transitioning it into a game session.
- Request:
{"user_id":"userB"}
- Response (same as matched status):
{
"status": "MATCHED",
"game_id": "g_456",
"ws_url": "wss://.../v1/ws/games/g_456",
"join_token": "jwt_or_session_token"
}