Requirements
A gaming backend handles player authentication, matchmaking (pairing players of similar skill), real-time game state synchronization, leaderboards, and player profile storage. Each of these components has distinct technical requirements. Matchmaking is a search problem (find compatible players quickly). Real-time sync is a latency-sensitive messaging problem. Leaderboards are a ranking problem at scale. This system design is asked at gaming companies and any company with real-time competitive features. Scale: Fortnite peak concurrent players: 8.3 million. League of Legends: 150 million registered players. Even at startup scale, the real-time components are technically challenging.
Matchmaking Service
Goal: pair players into a match where all players have similar skill (ELO, MMR — Matchmaking Rating). Approach: players submit a matchmaking request (player_id, game_mode, mmr). The matchmaking service maintains a queue of waiting players per game mode. Matching algorithm: search the queue for a group of players whose MMRs are within an acceptable range of each other (e.g., ±50 MMR initially). Expanding search: if no match is found after 30 seconds, expand the acceptable range (±100 MMR after 30s, ±200 after 60s). This trades match quality for wait time. Implementation: Redis sorted set per game mode: ZADD mm:queue:5v5 {mmr} {player_id}. Find candidates: ZRANGEBYSCORE mm:queue:5v5 (target_mmr – delta) (target_mmr + delta). If 10 players found (5v5): create a match, notify all 10, remove from queue. Match confirmation: each player confirms within 15 seconds. If any player declines or times out: return the other 9 to the queue. Anti-abuse: track acceptance rate; players with < 70% acceptance rate incur longer queue delays. Regional isolation: match players in the same region (us-east, eu-west) to minimize latency. Cross-region matching only when queue is very sparse.
Real-Time Game State Synchronization
In a real-time multiplayer game, all players must see a consistent view of the game world. Two models: authoritative server (server holds true game state; clients send inputs, server sends state updates) vs peer-to-peer (players send state to each other; no central authority). For competitive games: always use an authoritative server to prevent cheating. Architecture: dedicated game server per match (stateful process). Players connect via UDP (WebSocket for browser-based games). Each tick (20-60ms): server receives player inputs, advances game state, sends delta updates to all players. State sync: send only the delta (changes since last tick), not full state. Delta compression: bitmask indicating which fields changed. Lag compensation: if a player has 80ms latency, the server applies their input to the state 80ms ago (lag-compensated hit detection). Client-side prediction: client applies input locally immediately (before server confirmation) to hide latency. Server reconciliation: when the server’s authoritative state arrives, correct the client prediction if needed. Infrastructure: dedicated game servers run on VMs with aggressive autoscaling (match lifecycle = 20-60 minutes). Game server orchestration: Agones (Kubernetes-based) or custom fleet management. Players are assigned to the nearest game server region.
Leaderboards at Scale
Global leaderboard: rank all players by score. Redis sorted set: ZADD leaderboard {score} {player_id}. ZRANK for a player’s rank: O(log N). ZREVRANGE for top-K: O(log N + K). For 150 million players: a sorted set with 150M entries fits in ~10GB of Redis RAM. ZRANK returns a player’s global rank in O(log N). This is the canonical real-time leaderboard implementation. Periodic leaderboards (weekly, seasonal): use a separate sorted set per time window. On the reset: rename the old set to archive, create a new empty set. Sharding for very large leaderboards: partition by score range (shard 0: 0-1000 MMR, shard 1: 1000-2000, etc.). Rank = local_rank + sum of counts in lower shards. Friends leaderboard: for “rank among friends” — query scores for the player’s friend list (bounded set, typically < 500), sort in-memory. Friends list is stored in a graph DB or Redis set. Score writes: on match completion, batch-update all player scores in one pipeline. Rate: for 10K concurrent matches ending per hour, peak write rate is manageable (not every match ends simultaneously).
Player Profile and Persistence
Profile storage: PostgreSQL for player profiles (player_id, username, email, created_at, stats JSON). Write-through cache in Redis for frequently read profiles. Match history: append-only log in a time-series or columnar store (Cassandra, BigQuery). Each match record: match_id, players (array), outcome, duration, statistics per player. Query patterns: “last 20 matches for player X” — partition by player_id, sort by timestamp. Item/inventory system: NoSQL (DynamoDB) for player inventories (player_id → {item_id: quantity, …}). High write throughput from item drops. Conditional writes for atomic inventory updates. Session management: on match end, the game server POSTs the match result to the backend API. Backend validates, updates player MMRs, records match history, updates leaderboard. This is the critical path for data integrity — idempotent endpoints with match_id as idempotency key.
{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”How does matchmaking work in a large-scale gaming backend?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Players are placed in a Redis sorted set keyed by game mode, with their MMR (Matchmaking Rating) as the score. The matchmaking service queries ZRANGEBYSCORE to find players within an acceptable MMR range. If not enough players are found, the range expands over time (e.g., ±50 initially, ±100 after 30s, ±200 after 60s). When the required number of players is found, they are removed from the queue and a match is created.”}},{“@type”:”Question”,”name”:”Why use an authoritative server model for multiplayer games?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”An authoritative server holds the true game state. Clients send inputs; the server processes them and sends state updates back. This prevents cheating — a malicious client cannot modify game state because the server validates all inputs. Peer-to-peer models allow any client to claim it won or to modify positions, making competitive integrity impossible.”}},{“@type”:”Question”,”name”:”How does lag compensation work in real-time multiplayer games?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”When a player with 80ms latency fires at a target, the server receives that action 80ms late. Lag compensation rewinds the server state to 80ms ago, checks if the shot was valid at that moment (the target was at that position), and applies the hit. This makes the game feel fair to the shooting player at the cost of the target sometimes dying "behind cover" from their perspective.”}},{“@type”:”Question”,”name”:”How do you implement a real-time leaderboard for 150 million players?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Use a Redis sorted set: ZADD leaderboard {score} {player_id}. ZRANK returns a player's global rank in O(log N), and ZREVRANGE returns the top-K in O(log N + K). A sorted set with 150M entries fits in about 10GB of Redis RAM. For periodic leaderboards (weekly, seasonal), use separate sorted sets and reset by renaming the old set to an archive.”}},{“@type”:”Question”,”name”:”How do you handle idempotency for match result processing?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Each match has a unique match_id. When the game server posts the match result to the backend API, the endpoint checks whether this match_id has already been processed (stored in a processed_matches table). If yes, return the stored result without re-executing. This prevents double-crediting MMR changes or rewards if the game server retries due to a network failure.”}}]}
See also: Meta Interview Prep
See also: Snap Interview Prep
See also: Twitter Interview Prep