Design problems
Messaging & collaborationHardSDE-2 35 min

Design an online multiplayer game backend

Asked at
AmazonDream11MicrosoftNVIDIA

The interviewer asks

“Design the backend for a real-time online multiplayer shooter like BGMI or Free Fire: players queue for a match, get matched with players of similar skill, play a 100-player match lasting ~25 minutes, and their stats, rewards and rankings update afterwards.”

What a strong answer looks like

A strong answer splits the system into two very different planes. The real-time plane is a fleet of authoritative dedicated game servers, one process per match, talking UDP to clients at a fixed tick rate with client-side prediction, server reconciliation and lag compensation. The control plane is ordinary web backend: login, matchmaking, server allocation, profiles, inventory, leaderboards and match results, built on HTTPS, Redis, Postgres and Kafka. The interesting parts are the netcode (why the server is authoritative and how it hides 50-100 ms of latency), matchmaking (skill and latency with a widening window), fleet management (warm pools, region selection, never killing a server mid-match), and making match results and rewards idempotent and cheat-resistant.

Concepts to mention

Authoritative dedicated game serverClient-side prediction and server reconciliationEntity interpolationLag compensation with a capped rewindUDP with sequence numbers and delta snapshotsFixed tick rate and its costInterest managementSkill-based matchmaking with a widening windowWarm pool of game servers and safe scale-downRegion selection by measured pingIdempotent results and an inventory ledgerServer-side validation plus telemetry anti-cheat

1 Requirements

Functional

  • Login and player profile (level, skill rating, inventory, cosmetics)
  • Matchmaking: solo or party queue, matched by skill (MMR) and region/latency, match starts within ~60 s
  • Allocate a dedicated game server for each match and connect all players to it
  • Real-time gameplay: movement, shooting, hits, deaths, zone shrinking, synced to all players
  • Reconnect to an ongoing match after a network drop
  • Post-match: stats, XP, rewards, rank changes and leaderboards
  • Basic anti-cheat and reporting

Non-functional

  • Peak 2M concurrent players (~20,000 simultaneous 100-player matches)
  • Round-trip time to game server < 60 ms for most players in India; playable up to ~150 ms
  • Server tick rate 20-30 Hz; bandwidth per player < ~15 KB/s down to work on 4G in Tier-2/3 towns
  • Matchmaking p90 wait < 60 s at peak
  • A match in progress must survive control-plane outages (matchmaking, profile service down)
  • Rewards and purchases are never lost or doubled
  • Control-plane APIs 99.95% available

Out of scope (say it out loud)

  • Game client rendering and physics engine details
  • In-game store payments (use a payment provider)
  • Voice chat (mention as a separate SFU-based service)
  • Esports spectator broadcast

2 Estimates

Concurrent matches~20,000 at peak2M CCU ÷ 100 players per match
Game server capacity~2,000-2,500 VMs at peakA 100-player match at 25 Hz needs about 1-1.5 vCPU; a 16-vCPU VM runs ~8-10 matches. 20,000 ÷ 8-10 ≈ 2,000-2,500 VMs
Downstream bandwidth per player~10-15 KB/s25 snapshots/s × ~500 bytes delta-compressed (only nearby entities thanks to interest management)
Total game egress~200-250 Gbps2M players × ~12 KB/s ≈ 24 GB/s ≈ 190 Gbps; add headroom
Client input upstream~3 KB/s per player~60 input packets/s × ~50 bytes
Matchmaking load~1,300 players/s (~550 tickets/s)2M players each re-queue every ~25 min: 2M ÷ 1,500 s ≈ 1,330 players/s; with an average party size of ~2.5 that is ~530 tickets/s
Match result events~13 matches end/s, ~1,300 player results/s20,000 matches ÷ 1,500 s average length
Telemetry~1-2 TB/dayKill/hit/position samples for analytics and anti-cheat, compressed

3 API

EndpointWhat it does
POST/v1/auth/loginLogin with platform or phone; returns a session token
GET/v1/regions/beaconsList regional ping endpoints so the client can measure latency to each region
POST/v1/matchmaking/ticketsCreate a ticket {party_id, mode, region_pings}; returns ticket_id
GET/v1/matchmaking/tickets/{id}Poll (or receive over WS) ticket status; when matched returns server ip:port and a signed join token
DELETE/v1/matchmaking/tickets/{id}Cancel queueing
UDPgame server ip:portReal-time protocol: client inputs up, state snapshots down, with sequence numbers and acks
gRPCAllocator.Allocate(region, mode)Internal: get a ready game server from the warm pool
gRPCResults.Submit(match_id, results)Internal: game server reports final results, idempotent by match_id
GET/v1/leaderboards/{season}?around=meRank and neighbours on the seasonal leaderboard

4 Data model

playersPostgres (sharded by player_id)
player_id (PK)namelevelxpmmrmmr_uncertaintyregionbanned_untilcreated_at

Relational and transactional; read on login and cached in Redis.

inventory_ledgerPostgres
entry_id (PK)player_iditem_iddeltareason (match_reward/purchase)source_id (match_id or order_id)created_atUNIQUE(player_id, reason, source_id)

Append-only ledger; the unique key makes reward grants idempotent. Current inventory is a materialised sum.

mm_ticketsRedis
ticket_idparty (player_ids)modemmrregion_latenciesenqueued_atstatus

Short-lived (minutes). Per region+mode sorted set by MMR for fast range lookups.

game_serversKubernetes + Agones state (etcd), mirrored to Redis
server_idregionipportstate (Ready/Allocated/Draining)match_idplayers

Fleet state; the allocator moves a server from Ready to Allocated atomically.

matchesPostgres / DynamoDB
match_id (PK)regionserver_idmodestarted_atended_atstatusresults_json

Written at allocation and at end; used for reconnect and for history.

leaderboardRedis sorted sets
key = season:regionmember = player_idscore = rank points

ZINCRBY on results; ZREVRANK for 'my rank'. Rebuildable from match results.

telemetry and replaysKafka → ClickHouse / S3
match_idtickplayer_idevent (hit, kill, position sample)

Analytics, anti-cheat models, dispute review.

5 Architecture

CLIENTSEDGESERVICESASYNCDATAEXTERNALlogin, queueticketprofile, inventoryMMR poolsallocate serverwarm pool scalingReady → AllocatedUDP 25 Hzresults, telemetryXP, MMR, rewardsleaderboardfeatures, replayscacheGame client(mobile/PC)API gateway (HTTPS/WS)MatchmakerProfile + inventorysvcDedicated gameserversFleet allocatorResults + rewardsworkersKafka (results,telemetry)Anti-cheat analysisRedis (tickets, ranks)Postgres (players,ledger)ClickHouse + S3replaysCloud VMs (Agonesfleet)
ClientEdge / gatewayService / workerQueue / streamCacheDatabase / storageAI / modelThird partyrequestasync (event / queue)

Hover a box to highlight its connections.

6 Request flows

Find a match and join

  1. 1After login the client pings the regional beacons (Mumbai, Hyderabad, Singapore) and gets e.g. 28 ms, 41 ms, 75 ms.
  2. 2The client (party leader) creates a matchmaking ticket with mode, party members and region latencies.
  3. 3The matchmaker puts the ticket in the Redis pool for (region=Mumbai, mode=squad) keyed by party MMR.
  4. 4Every ~1 s, a matchmaker loop per pool scans the sorted set and builds matches of 100 players whose MMR lies within a window; the window widens the longer the oldest ticket waits.
  5. 5For a formed match, it calls the allocator, which atomically flips one warm game server in Mumbai from Ready to Allocated and passes it the expected player list.
  6. 6The matchmaker returns ip:port plus a short-lived signed join token to each client (via WS push or poll).
  7. 7Clients open UDP to the game server and present the token; the server checks the signature and that the player is on its list.
  8. 8When all players connect or a 30 s timer expires (bots fill gaps), the match starts.

One game tick

  1. 1Clients send input packets (move, aim, fire) with a sequence number ~60 times per second, and predict their own movement locally.
  2. 2Every 40 ms (25 Hz) the server processes all inputs received since the last tick, simulates movement and physics, and validates actions (speed limits, ammo, line of sight).
  3. 3For a shot, the server rewinds other players to where the shooter saw them (lag compensation, capped at ~200 ms) and checks the hit.
  4. 4The server builds a snapshot per player containing only entities relevant to them (interest management), delta-compressed against the last snapshot that player acknowledged.
  5. 5Clients apply the snapshot, correct their predicted position if it differs (reconciliation), and interpolate other players ~100 ms in the past for smooth movement.

Match ends and rewards are granted

  1. 1The game server sends Results(match_id, per-player placement, kills, damage) to the results API, which writes it to Kafka; it retries until acknowledged.
  2. 2Results workers compute XP, new MMR and rewards for each player.
  3. 3Rewards are written to the inventory ledger with a unique key (player_id, 'match_reward', match_id), so a duplicate event does nothing.
  4. 4Leaderboard scores are updated in Redis with ZINCRBY, guarded by a processed-match set so replays do not double-count.
  5. 5The allocator marks the server Ready again or shuts it down if the fleet is over capacity.

7 Deep dives

Netcode: authoritative server, prediction and lag compensation

Never trust the client: if clients decide hits or positions, cheaters teleport and one-shot everyone. The server owns the truth and runs a fixed-step simulation (20-30 Hz for mobile battle royale, 60-128 Hz for competitive PC shooters). Waiting 60 ms for the server before moving your own character feels terrible, so the client predicts locally: it applies its own inputs immediately and keeps a buffer of unacknowledged inputs. When a snapshot arrives saying 'at input #812 you were at (x, y)', the client resets to that state and replays inputs 813 onwards (reconciliation); small errors are smoothed. Other players are rendered by interpolating between two received snapshots, about 100 ms behind. That creates a problem for shooting: you aim at where you saw the enemy, which is in the past. The server fixes this with lag compensation: it keeps ~1 s of position history per player, rewinds targets to the shooter's view time (estimated from RTT plus interpolation delay), checks the hit, then restores. Cap the rewind (~200 ms) so players with terrible ping cannot shoot people who already took cover. Use UDP with your own sequence numbers and acks: TCP head-of-line blocking would freeze the game on a single lost packet, while a lost snapshot is simply superseded by the next one.

Matchmaking: skill vs wait time vs ping

Each ticket carries a party MMR (for example a Glicko/TrueSkill-style rating with an uncertainty), the mode and measured latencies to each region. Tickets are pooled per (region, mode) in Redis sorted sets keyed by MMR. A matchmaker loop runs every second per pool: take the oldest tickets first, collect tickets within an MMR window around them, and form a match when 100 slots fill while respecting party sizes. The window starts narrow (±50) and widens with wait time (±300 after 45 s); after ~60 s a ticket may also be offered a second region if its ping there is acceptable, or the match is filled with bots, which mobile games commonly do for low-rank players. Each pool is owned by one matchmaker worker (lease in Redis or etcd) so two workers never put the same ticket into two matches; formation uses an atomic 'claim tickets' step (Lua script or WATCH/MULTI) to be safe. Party tickets are atomic units. Measure what matters: p50/p90 queue time per region and rank band, and match quality (MMR spread, ping spread).

Fleet management: warm pools and safe scale-down

Starting a VM takes minutes, but players expect a match within seconds of being matched. So each region keeps a warm buffer of game server processes in the Ready state (for example 10-15% of current allocated count, larger before evening peak and festival events). Agones on Kubernetes is a common open-source choice; GameLift is the managed option on AWS. Allocation is an atomic state change from Ready to Allocated, so two matches never land on the same process. Scaling up adds nodes when the Ready buffer drops below target. Scaling down is the tricky part: you must never kill a node that hosts a running match. Nodes are cordoned, their Ready servers removed, and the node is deleted only when its last match ends (up to ~30 minutes). Pack matches onto fewer nodes (bin packing) so nodes empty out faster. Game servers run one match per process so a crash affects 100 players, not 1,000. Region placement follows players: Mumbai and Hyderabad cloud regions cover most of India with 20-50 ms RTT; very poor networks benefit from regional relays.

Results, rewards and anti-cheat without trusting anyone

Match results drive real value (rank, items, sometimes paid rewards), so the path must be exactly-once in effect. The game server is the only source of results; clients never report their own kills. The server submits results with match_id and retries until acknowledged; consumers are idempotent because the ledger row has a unique key (player_id, reason, source_id) and leaderboard updates check a processed-match set. Inventory is an append-only ledger, not a mutable counter, which makes audits and refunds simple. Anti-cheat has layers: server-side validation every tick (movement speed, fire rate, line-of-sight), client integrity checks, and offline analysis of telemetry in ClickHouse to spot aimbots (inhuman reaction times, perfect tracking) and wallhacks (aiming at players behind walls). Bans are applied with a delay and in waves so cheat makers cannot easily tell which change got them caught. Reports from players feed the same pipeline.

8 Trade-offs

Dedicated authoritative servers vs peer-to-peer

Peer-to-peer with a host player is cheap and fine for 2-8 player casual games, but the host has a latency advantage, can cheat, and the match dies if they leave. For 100 players and competitive play, dedicated servers are the only option despite the hosting cost (roughly a few paise per player-hour: one 16-vCPU VM at under $1/hour hosts ~900 players).

UDP with custom reliability vs TCP/WebSocket

Game state is time-sensitive; old snapshots are useless. UDP lets you drop and supersede instead of retransmitting. Important events (kill feed, inventory pickup) get selective reliability on top. WebSocket/TCP is acceptable for turn-based or slow games (Ludo, card games) and much simpler.

Tick rate vs cost and bandwidth

Higher tick rate means fairer hits but more CPU and bandwidth. 128 Hz costs about 5x the CPU of 25 Hz. For a mobile battle royale on Indian 4G, 20-30 Hz with good interpolation is the right balance; competitive 5v5 PC games justify 64-128 Hz with 10 players per server.

Strict skill matching vs fast queues

Tight MMR windows make fair games but long waits at high ranks and off-peak hours. A widening window plus bots for low ranks keeps queues short. Make it a per-pool configuration so designers can tune it.

9 Bottlenecks

Evening peak and event spikes

Concurrency in India peaks around 8-11 pm and jumps when a new season launches. Pre-scale the fleet from a forecast an hour ahead, raise the warm buffer, and rate-limit login/matchmaking with a visible queue rather than letting the control plane collapse.

Hot matchmaking pools

One popular mode in one region can get 1,000+ tickets/s. Shard the pool by MMR band (each band owned by one worker) and allow borrowing across adjacent bands as wait time grows.

Bandwidth in late game and crowded areas

When 40 players land in one town, snapshots grow. Interest management (send only nearby or visible entities, lower update rate for distant ones), quantised positions and delta compression keep packets under the MTU.

10 Failure handling

Control plane outage during matches

Running matches only need their game server process; they continue normally. Results are buffered on the game server (and local disk) and resubmitted when the results API recovers. New matchmaking pauses, which players notice, but no match is lost.

Game server crash

Affects one match (100 players). Detect via missed heartbeats, mark the match aborted, give players a 'match cancelled' notice and do not apply rank losses. Process-per-match isolation limits blast radius.

Player's network drops

The server keeps the player's character in the world (vulnerable) for ~60-90 s. On reconnect, the client asks the control plane for its active match (from the matches table), gets a new join token and resumes with a full snapshot.

Whole region degraded

Matchmaking stops sending tickets to that region and offers players the next-best region by measured ping. Matches already in the region either finish or are cancelled without penalty.

11 What impresses

  • Separates the real-time data plane from the control plane and says matches must survive a control-plane outage
  • Explains prediction, reconciliation and lag compensation correctly, including why the rewind is capped
  • Chooses UDP and explains head-of-line blocking in TCP
  • Does the bandwidth maths per player and checks it against Indian 4G networks
  • Handles scale-down: never kill a node with a running match; bin-pack to drain nodes
  • Makes rewards idempotent with a ledger and a unique (player, source) key

12 Mistakes

  • Letting clients report hits, kills or positions as truth
  • Running real-time gameplay over HTTP request/response
  • Putting game state in a central database or Redis on every tick
  • Ignoring latency and region placement for players
  • Autoscaling game servers like stateless web pods and killing matches on scale-down
  • Granting rewards with a non-idempotent 'add 100 coins' update

13 Follow-ups

Q1How would you support spectators and esports broadcast with a delay?
Q2How do you handle a party where one friend is in Delhi and another in Chennai?
Q3How would voice chat work for squads?
Q4How would you detect and handle a speed hack?
Q5What changes for a turn-based game like Ludo or rummy with real-money stakes?
Q6How do you roll out a new game server version while matches are running?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free