System design interview
Design a multiplayer game server
Ticks, prediction, lag compensation, matchmaking and match fleets.
Press Play to follow a request through the design, or step through it.
- Client and edge
- Service
- Data store
- Cache and queue
- Alice taps Play. The app sends her ID and the regions where its ping test came back under 80 ms.
- The lobby reads her skill rating from the player database.
- It queues a ticket: player, rating, region and time joined. The request returns, and the app shows 'searching'.
- The matchmaker takes tickets from the queue and builds groups of 64 within a rating band, widening the band as tickets wait.
- A group is complete, so the matchmaker asks the allocator for a server in that region.
- The allocator picks a machine with a free slot, starts the match on it, and sends the address and a join token for each player.
- Client
- Lobby service: login, queue, rejoin
- UDP relay: checks join token
- Match queue
- Player DB: ratings, results
- Match server: authoritative, 30 Hz
- Matchmaker: skill, region, wait
- Fleet allocator: free slots by region
On this page
In 2 minutes
I would make the game server authoritative: clients send only inputs, and the server runs the world at 30 ticks a second in a 33 ms slot, using about 10 ms of one core per match. Traffic goes over UDP through a relay that checks a join token. To keep bandwidth near 5 KB a second per player, I quantize values, send deltas against the last acknowledged snapshot, and use interest management to send only nearby players, which is about 9 times less than sending everything.
On the client, prediction with reconciliation makes your own movement instant, other players are drawn about 100 ms in the past by interpolation, and the server rewinds targets for hit checks to what the shooter saw, capped at 200 ms. Players queue through a lobby. A matchmaker groups 64 tickets by region and skill, widening the rating band as they wait, and a fleet allocator starts the match on a regional machine that has a free slot.
In our estimate, 640,000 players mean 10,000 matches, 400 machines and about 710 queue joins a second. A dropped player keeps their slot for 60 seconds and rejoins with the same token. At the end, the server writes results and ratings in one idempotent transaction before shutting down.
Step 1 of 6: Clarify the scope
Interviewer asks
Design the backend for a fast online shooter or racing game with up to 64 players in a match. Everyone should see a fair, consistent world even though each player is 50 to 150 ms away from the server. Cover how players are matched and how matches are started.
Questions to ask first
Ask these before you draw anything. Under each one is why its answer changes the design.
- How many players are in a match, and how fast must the world update?
64 players at 30 updates a second sets the work per tick and the bandwidth. A slower game could use a much simpler design.
- Is the game competitive, so cheating matters?
If it does, the server must decide what happens, and clients can only send what the player pressed.
- Can we use UDP, and how bad are the player networks?
Late data is useless in a fast game, and UDP lets us drop it. Mobile networks lose packets, so the design must survive loss.
- Are players spread across many regions, and may we match across them?
Matching across regions raises everyone's latency. Regional queues and fleets keep ping low but make queues smaller.
- What must be saved after a match?
If only results and ratings matter, the match state itself can live in memory and be thrown away.
Requirements
Functional
- Join a queue and be placed in a match with players of similar skill and a good connection.
- Simulate the match on the server, and send each player a view of the world 30 times a second.
- Let players move, aim and shoot, with hits decided fairly.
- Let a player who drops out rejoin the same match within 60 seconds.
- Save the results and update skill ratings when the match ends.
Non-functional
- The server runs 30 ticks a second, each one finished within 10 ms of CPU, so no tick overruns its 33 ms slot.
- A player's own movement shows on screen at once, whatever the round-trip time of 50 to 150 ms.
- Hit checks are fair for shooters up to 200 ms behind: a shot is judged by what the shooter saw.
- 90% of players are placed within 30 seconds, with a ping under 80 ms to the server.
- Match results are never lost once a match ends.
- Handles 640,000 players in 10,000 live matches, and about 710 queue joins a second.
Step 2 of 6: Size it
Each match is a small, independent world, so the fleet scales by adding machines. The hard parts are the 33 ms tick and the network. Filtering and delta compression cut traffic about 9 times, and the client must hide the rest of the latency.
Assumed for the exercise, not facts about a real system:
- 640,000 players are online at the busiest time, all in matches of 64. A match lasts 15 minutes (900 s).
- The server ticks at 30 Hz and sends each player a snapshot every tick. A full entity state is about 24 bytes, and a delta against the last acknowledged snapshot averages 8 bytes. Each UDP packet adds about 40 bytes of headers.
- With interest management, a player is sent about 16 of the 63 other players at a time, the ones that are near or in view.
- A client sends 30 input packets a second, of about 40 bytes. One 32-core machine runs 25 matches, at about one core for each match, with headroom for the system.
- Matches in play
- 640,000 ÷ 64 players
- 10,000 matches
- Snapshot per player
- 16 entities × 8 B = 128 B, plus 40 B headers = 168 B; × 30 a second. With no deltas or filtering: 63 × 24 + 40 = 1,552 B, × 30
- ≈ 5 KB/s with both tricks, against ≈ 46.6 KB/s without, about 9 times more
- Fleet traffic
- Out: 640,000 × 5 KB/s = 3.2 GB/s. In: 640,000 × 30 × 40 B = 0.77 GB/s
- ≈ 26 Gbit/s out and ≈ 6 Gbit/s in, spread across regions
- Game servers
- 10,000 matches ÷ 25 per machine
- 400 machines
- Tick budget
- 1,000 ms ÷ 30 ticks
- 33 ms a tick, so keep simulation under 10 ms for 3x headroom
- Matchmaking rate
- 640,000 ÷ 900 s ≈ 711 joins a second; 10,000 ÷ 900 s ≈ 11 matches
- ≈ 710 queue joins and ≈ 11 match starts a second
Step 3 of 6: Sketch the path of one request
Step through the diagram at the top of the page to watch a request move through these parts.
A Client first talks to the Lobby service over ordinary HTTPS. The lobby reads the player's skill from the Player DB, and puts a ticket in the Match queue. The Matchmaker takes tickets, groups 64 players of similar skill and region, and asks the Fleet allocator for a game server.
The allocator picks a machine with a free slot in that region and starts the match on a Match server. The server is authoritative: it owns the true state of the world. Clients send only their inputs, such as move, aim and fire, and the server simulates all 64 players 30 times a second.
During play, the client sends UDP packets to a UDP relay, which checks a join token and forwards them to the match server. The server answers with a snapshot every tick. When the match ends, the server writes results and new ratings to the Player DB, and tells the allocator its slot is free.
Other paths through the system
Play a match
- Alice's client sends an input packet over UDP: move, aim, fire, with a sequence number. It sends 30 a second.
- The relay checks the join token and forwards the packet to the match server.
- Each 33 ms tick, the server applies all inputs, advances the world, decides hits, and sends Alice a delta snapshot of what is near her.
- Her client compares the snapshot with its own prediction, corrects any difference, and draws the other players slightly in the past.
Finish the match
- The last inputs arrive, and the match ends because a team won or the timer ran out.
- The server writes the result and each player's new rating in one transaction, keyed by the match ID so a retry changes nothing.
- The database confirms the commit. Only now may the server shut the match down.
- The server reports that its slot is free, and the allocator returns it to the pool for the next match.
Step 4 of 6: Go deep on the hard part
The authoritative server and its snapshots
Interviewer asks
Who decides what happens in the world, and how do you keep traffic small enough for 64 players at 30 updates a second?
In a competitive game the server is the single source of truth.
Read moreShow less: The authoritative server and its snapshots
A client never says 'I am at this position'. It says 'I pressed forward and fired'. The server applies those inputs to its own copy of the world with the same rules every time. A modified client can lie about its inputs, but it cannot lie about where it is or whether a shot hit.
The server runs a fixed tick, 30 times a second, so each tick has 33 ms. In every tick it reads the inputs that arrived, advances the physics, resolves hits and sends snapshots. If a tick takes more than 33 ms the world slows down for everyone, so we budget 10 ms of CPU and keep the work per match on one core, with no locks between matches.
Use UDP. In a fast game an old position is worth nothing, and TCP would hold back new data behind a lost packet. With UDP the game adds its own small layer: sequence numbers on every packet, and a separate reliable channel for rare events such as 'you were eliminated'.
Cut the bandwidth. Sending all 63 other players in full every tick costs 63 × 24 B + 40 B = 1,552 B a packet, or about 46.6 KB/s per player. Three things shrink it:
- Quantize. Send positions as 16-bit whole numbers instead of 32-bit floats. For a map 650 m wide, 16 bits gives about 1 cm of precision.
- Delta compress. Send only what changed since the last snapshot the client acknowledged. The client's acknowledgement says which one that was. If it has not acknowledged for a while, the server falls back to an older baseline.
- Interest management. Send only the entities a player could care about, such as those within 100 m or in line of sight. Cheaters also lose a way to see through walls, because hidden players are never sent.
With these, a packet carries about 16 entities × 8 B + 40 B = 168 B, so about 5 KB/s per player. That is 9 times less, and it makes 640,000 players about 26 Gbit/s across the fleet instead of 240.
Hiding latency and keeping hits fair
Interviewer asks
A player is 100 ms from the server. How does the game still feel instant and stay fair?
Waiting a round trip to see your own character move would feel awful.
Read moreShow less: Hiding latency and keeping hits fair
Three techniques work together.
Client-side prediction. The client applies its own inputs to its own copy of the world at once, and keeps a list of the inputs the server has not yet confirmed. Each snapshot says 'I have processed your inputs up to number N'. The client resets to the server's state, then replays its inputs after N. Usually the result matches the prediction and nothing visible happens. When the server disagreed, for example because another player bumped you, the client blends smoothly to the corrected position.
Interpolation. Other players are not predictable, so draw them slightly in the past. With snapshots 33 ms apart, a client draws other players about 100 ms behind the latest one, which is about three snapshots. It then always has two snapshots to blend between, and one lost packet does not cause a stutter. The price is that you see opponents as they were 100 ms ago.
Lag compensation. If a shooter sees a target 100 ms in the past, a shot at what they see would miss when judged against the target's true position now. So the server keeps a history of each player's position for about the last 200 ms. When a fire input arrives, the server works out what the shooter saw: the shot's travel time of about 40 ms (half of an 80 ms round trip) plus the 100 ms interpolation delay, so roughly 140 ms. It rewinds the targets to that moment, tests the hit, and then returns to the present.
The cost is fairness for the victim. A target who ran behind cover 100 ms ago may be hit after they believe they were safe. To limit the damage, cap the rewind at 200 ms, so a very laggy player cannot shoot into the distant past, and treat players above that ping as playing at a disadvantage.
Anti-cheat basics. The server validates every input: speed limits, rates of fire, and legal actions. It never sends hidden entities. It rate-limits packets and checks the join token. For aim assistance and similar cheats, it keeps statistics, such as accuracy and reaction time, and flags outliers for review. Client-side detection is a separate and endless contest. The authoritative server is the part that always works.
Matchmaking, fleets and reconnects
Interviewer asks
How do you put 64 players of similar skill into a match close to them, and start a server for it in seconds?
Matchmaking. Each ticket carries a rating, a region and the time it joined.
Read moreShow less: Matchmaking, fleets and reconnects
The matchmaker keeps tickets in memory, grouped by region and game mode, ordered by rating. It looks for 64 tickets inside a rating band. A new ticket starts with a tight band, say plus or minus 100 points. For every 10 seconds of waiting, the band widens by 50 points, up to a limit such as 500, and the region set can widen too. Short waits then give fair matches, and long waits give some match instead of none. Parties queue as one ticket with the average rating.
Allocation. Each region has a fleet of game machines. Each machine sends a heartbeat that says how many match slots it has free. The allocator picks a machine with room and starts the match there, passing the player list and a join token for each player. Keep a spare pool of warm machines, since starting a machine takes minutes, and scale the fleet by the 11 matches a second and the time of day. When deploying a new version, mark old machines as draining: they take no new matches and finish the ones they have.
Joining. The token says which match and which player a connection belongs to. The relay checks it, and a stolen address alone is useless. The relay also hides the real server addresses from players, which blunts attacks aimed at a single server.
Reconnects. When a player's packets stop, the server keeps their character for 60 seconds, idle or run by a simple bot, and does not release the slot. The lobby records which match each player is in, so a restarted app is offered 'rejoin'. The same token reconnects to the same server, which sends a full snapshot, not a delta, since the client has no baseline.
Results. A match lasts 15 minutes and its state lives in memory. At the end, the server writes the result and new ratings in one transaction, keyed by the match ID, and only then shuts down. A retry writes nothing twice. If a server crashes mid-match, the match is voided and nobody's rating changes, which is rare and acceptable. Saving state every few seconds just to survive that would cost more than it saves.
Step 5 of 6: Name the trade-offs
Who decides
An authoritative server that simulates everything over Clients that report their own positions and hits
Cheating is far harder and everyone sees one consistent world. The cost is server CPU for every match, and the need to hide latency on the client.
Transport
UDP with sequence numbers and acknowledgements over TCP or WebSocket
A lost packet never blocks newer data. The cost is that we build our own loss handling, and some networks and firewalls treat UDP badly.
Shots
Lag compensation, with a 200 ms rewind cap over Judge hits against the present
Players hit what they aim at on their own screens. The cost is that a victim is sometimes hit just after reaching cover.
Match fleet
Regional fleets with regional queues over One global pool and queue
Every player gets a low ping. The cost is smaller queues in quiet regions, which means longer waits or wider skill bands.
Step 6 of 6: Say what breaks next
Questions an interviewer asks once the design is on the board. Answer each one out loud first.
- How would you lower latency for players far from any region?
Add a region closer to them, since ping is mostly distance. Put the UDP relay at many edge locations, so the first hop is short and the traffic then travels on a good private network to the server. Never mix a far player into a match of near ones without telling them, since it hurts everyone's feel.
- What changes for 1,000 players in one world?
Interest management becomes the whole design. Split the world into a grid of cells, run each cell or group of cells on its own server, and send each player only the cells around them. Entities crossing a border are handed to the next server. A single match server cannot hold the world, and the tick must still fit in 33 ms.
- How do you let people spectate or replay a match?
Have the match server also write its snapshots, or its inputs, to a stream. A spectator service reads the stream, delays it by 30 seconds or so to prevent cheating by outside help, and sends it to viewers. A replay is the same stream kept in storage and played back through the same simulation.
- What happens when a game server is overloaded mid-match?
The tick time rises above its budget, so alert on tick time long before it reaches 33 ms. Stop sending new matches to that machine and, if needed, lower the snapshot rate for that match from 30 to 20 a second. Do not try to move a live match, because its state is only in memory.