| Scale | Users / Games | Data Size | Server Load | Latency | Notes |
|---|---|---|---|---|---|
| 100 users | ~50 concurrent games | Small in-memory state | Single server handles all | Low latency | Simple in-memory objects, no persistence needed |
| 10,000 users | ~5,000 concurrent games | Moderate memory, some persistence | Multiple servers, load balancing | Low to moderate latency | Need database for game state, player profiles |
| 1,000,000 users | ~500,000 concurrent games | Large data, distributed storage | Horizontal scaling, caching | Moderate latency | Sharding game data, caching hot games |
| 100,000,000 users | ~50,000,000 concurrent games | Very large data, multi-region | Global load balancing, CDN | Low latency via edge | Complex partitioning, eventual consistency |
Board, Player, Game classes in LLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
At small scale, the in-memory game state works fine. As users grow to thousands, the database storing player and game data becomes the first bottleneck. It struggles with many read/write requests per second. This slows down game updates and player actions.
- Database scaling: Add read replicas to handle more queries. Use connection pooling to reduce overhead.
- Caching: Cache frequently accessed game states and player info in memory (e.g., Redis) to reduce DB load.
- Horizontal scaling: Add more application servers behind a load balancer to handle more concurrent games and players.
- Sharding: Partition game and player data by user ID or game ID to distribute load across multiple databases.
- CDN and edge computing: For very large scale, use CDN to serve static assets and edge servers to reduce latency.
- At 10,000 users with 5,000 concurrent games, assuming 1 action per second per game: 5,000 QPS to database.
- Single PostgreSQL instance handles ~5,000 QPS, so near capacity.
- Memory per game state ~10 KB, total ~50 MB for 5,000 games in memory.
- Network bandwidth: 5,000 QPS * 1 KB per request = ~5 MB/s, well within 1 Gbps link.
Start by describing the components: Board, Player, Game classes and their responsibilities. Then discuss how data grows with users and games. Identify the first bottleneck (usually database). Propose clear, stepwise scaling solutions like caching, read replicas, and sharding. Use numbers to justify your choices. Keep answers structured and focused.
Your database handles 1000 QPS. Traffic grows 10x to 10,000 QPS. What do you do first?
Answer: Add read replicas and implement caching to reduce load on the primary database before considering sharding or more complex solutions.
Practice
Board, Player, and Game classes?Solution
Step 1: Understand the role of the Board class
The Board class holds the layout and current state of the game, such as positions of pieces or marks.Step 2: Compare with Player and Game classes
The Player class stores player details, and the Game class manages turns and rules, not the board state.Final Answer:
Board class -> Option DQuick Check:
Board = game state holder [OK]
- Confusing Player with Board for state storage
- Thinking Game class stores board state
- Assuming Score class manages board
Solution
Step 1: Identify the correct constructor syntax in JavaScript
In JavaScript ES6+, the constructor method inside a class is namedconstructor.Step 2: Check other options for errors
class Player { Player(name, id) { this.name = name; this.id = id; } } uses a method named Player instead of constructor; function Player(name, id) { this.name = name; this.id = id; } is a function, not a class; class Player { def __init__(self, name, id): self.name = name; self.id = id } uses Python syntax.Final Answer:
class Player { constructor(name, id) { this.name = name; this.id = id; } } -> Option AQuick Check:
JavaScript class constructor = constructor() [OK]
- Using method named same as class instead of constructor
- Mixing Python syntax in JavaScript
- Defining constructor as a separate function
game.playTurn() once?class Player {
constructor(name) { this.name = name; }
}
class Board {
constructor() { this.state = ['-', '-', '-']; }
mark(position, symbol) { this.state[position] = symbol; }
}
class Game {
constructor() {
this.board = new Board();
this.players = [new Player('Alice'), new Player('Bob')];
this.currentPlayerIndex = 0;
}
playTurn() {
const player = this.players[this.currentPlayerIndex];
this.board.mark(0, this.currentPlayerIndex === 0 ? 'X' : 'O');
this.currentPlayerIndex = 1 - this.currentPlayerIndex;
return this.board.state;
}
}
const game = new Game();
console.log(game.playTurn());Solution
Step 1: Analyze initial state and playTurn logic
Board state starts as ['-', '-', '-']. Current player index is 0, so symbol 'X' is placed at position 0.Step 2: Update currentPlayerIndex and return state
After marking, currentPlayerIndex switches to 1. The returned board state is ['X', '-', '-'].Final Answer:
['X', '-', '-'] -> Option AQuick Check:
First turn marks 'X' at position 0 [OK]
- Assuming 'O' is placed first
- Not updating currentPlayerIndex
- Confusing board state initialization
class Game {
constructor() {
this.players = ['Alice', 'Bob'];
this.currentPlayerIndex = 0;
}
nextTurn() {
this.currentPlayerIndex += 1;
if (this.currentPlayerIndex > this.players.length) {
this.currentPlayerIndex = 0;
}
}
}Solution
Step 1: Understand player index bounds
Array indices go from 0 to length-1. If currentPlayerIndex equals players.length, it is out of bounds.Step 2: Check condition for resetting index
The condition uses > players.length, which misses the case when currentPlayerIndex == players.length, causing an error.Final Answer:
The condition should be >= players.length, not > -> Option BQuick Check:
Index reset condition must include equality [OK]
- Using > instead of >= for index reset
- Ignoring zero-based indexing
- Thinking player array type causes index error
Board, Player, and Game classes. Which design choice best supports adding new game rules and multiple player types (e.g., human, AI) without changing existing code much?Solution
Step 1: Consider extensibility and separation of concerns
Inheritance allows creating specialized Player types without modifying base Player class, supporting new behaviors.Step 2: Use modular design for rules
Extending Game with separate rule classes or modules keeps code clean and easy to maintain.Final Answer:
Use inheritance: create subclasses like HumanPlayer and AIPlayer from Player, and extend Game with rule classes -> Option CQuick Check:
Inheritance and modular rules = easy extension [OK]
- Putting all logic in one class causing messy code
- Using global variables leading to hard-to-maintain code
- Storing rules in Board instead of Game
