Why a framework
Low-level design interviews move fast. You have roughly thirty-five minutes to clarify requirements, define an object model, design class APIs, and walk through core logic. Most candidates lose points from poor time management, not from “not knowing OOP.”
Failure modes are predictable: diving into code and drowning in edge cases before the interviewer understands your structure — or polishing every tiny detail until the clock ends with no meaningful design shown.
This delivery framework gives sequence and pacing. If your interviewer pulls you off-script for a probe, follow them — then gently return to the important bits.
01~5 min
Requirements
02~3 min
Entities
0310–15
Class design
04~10 min
Implementation
05~5 min
Extensibility
1) Requirements (~5 minutes)
Every LLD interview starts with a thin prompt: “Design Tic-Tac-Toe.” “Design a parking lot.” “Design a coffee machine.” Your job is to turn that into a spec you can design around.
Spend the first minute or two asking questions. Prime yourself with these themes:
- Primary capabilities — What operations must the system support?
- Rules and completion — What counts as success, failure, or a state transition?
- Error handling — How should invalid inputs or illegal actions be rejected?
- Scope boundaries — What’s in (core logic, rules) vs out (UI, storage, networking, concurrency, future features)?
Confirm a short written spec with the interviewer. Jot out-of-scope items too — it prevents scope creep and shows judgment.
Requirements (Tic-Tac-Toe):
1. Two players alternate placing X and O on a 3x3 grid.
2. Win = complete a row, column, or diagonal.
3. Draw = all nine cells filled with no winner.
4. Reject invalid moves (occupied cell, move after game over).
5. Support querying game state and resetting.
Out of scope:
- UI / rendering
- AI opponent
- Networked multiplayer
- NxN boards
- Undo / redo
2) Entities and relationships (~3 minutes)
Translate the spec into a few core entities with clean ownership. Scan for meaningful nouns — things that maintain state or enforce rules. Information that only attaches to something else is usually a field, not a class.
Then sketch relationships: Who is the orchestrator? Who owns durable state? Has-a / uses / contains? Where do specific rules live?
Entities:
- Game
- Board
- Player
Relationships:
- Game -> Board
- Game -> Player (2x)
3) Class design (~10–15 minutes)
Turn each entity into state + behavior, top-down. Start with the orchestrator (Game), then Board, Player, etc. Stay tied to requirements so you don’t invent bloat.
State from requirements. For each entity: which requirements does it own, and what must it remember?
Game — State:
- board: Board
- player_x / player_o: Player
- current_player: Player
- state: IN_PROGRESS | WON | DRAW
- winner: Player | None
Behavior from requirements. Small focused API — each method is a real action or query from the problem.
Game — Behavior:
+ make_move(player, row, col) -> bool
+ get_current_player() -> Player
+ get_game_state() -> GameState
+ get_winner() -> Player | None
+ get_board() -> Board
4) Implementation (~10 minutes)
Ask what depth they want before you write. Most loops need pseudocode for key methods; some want near-complete code in a language. Focus on methods that define system behavior.
- Happy path first — inputs, steps, collaborator calls, return/state change.
- Edge cases next — invalid inputs, illegal operations, wrong state.
- Write readable structure — language you use daily if they want real code.
def make_move(self, player, row, col) -> bool:
if self.state != GameState.IN_PROGRESS:
return False
if player is not self.current_player:
return False
if not self.board.can_place(row, col):
return False
self.board.place(row, col, player.mark)
if self.board.check_win(row, col, player.mark):
self.state = GameState.WON
self.winner = player
elif self.board.is_full():
self.state = GameState.DRAW
else:
self.current_player = (
self.player_o if player is self.player_x else self.player_x
)
return True
boolean makeMove(Player player, int row, int col) {
if (state != GameState.IN_PROGRESS) return false;
if (player != currentPlayer) return false;
if (!board.canPlace(row, col)) return false;
board.place(row, col, player.mark);
if (board.checkWin(row, col, player.mark)) {
state = GameState.WON;
winner = player;
} else if (board.isFull()) {
state = GameState.DRAW;
} else {
currentPlayer = (player == playerX) ? playerO : playerX;
}
return true;
}
Verification (1–2 minutes). Trace a concrete scenario tick by tick: initial state, each operation, state changes, transitions. Catch forgotten turn switches or broken win detection before the interviewer does — fixing a bug live is a positive signal.
Initial: empty board, current = X
make_move(X, 0, 0) → X at [0][0], current = O
make_move(O, 1, 1) → O at [1][1], current = X
…
5) Extensibility (~5 minutes, if time and level allow)
Often interviewer-led. They propose a twist to see whether your design evolves cleanly — not whether you can bolt on hacks.
- Junior: little or no extensibility discussion.
- Mid: one or two small follow-ups.
- Senior: several “what if we…” questions in a row.
Stay high level. Point at the seam. Example — undo: “All mutations flow through
make_move. I’d keep a command history stack, push previous state before each change, and undo() would pop and restore. The rest of the system stays untouched.”Next
Refresh OOP concepts, then guidelines & patterns, then time a classic.
Worked mini-example: 35 minutes on Tic Tac Toe
def session_skeleton():
spec = clarify() # 5m
entities = nouns(spec) # 3m
api = verbs(entities) # 10m (make_move)
implement(api.core_path) # 10m
if time_left():
show_extension_seam() # 5m
void sessionSkeleton() {
Spec spec = clarify(); // 5m
List<Entity> entities = nouns(spec); // 3m
Api api = verbs(entities); // 10m (makeMove)
implement(api.corePath()); // 10m
if (timeLeft()) {
showExtensionSeam(); // 5m
}
}
Framework anti-patterns
- Spending 20 minutes on perfect UML with zero API.
- Implementing helpers before the main verb (make_move/enter/allow).
- Extensibility essay before a working happy path.
- Restarting design when asked a twist instead of pointing at a seam.
Minute-by-minute checklist
- Minute 5: v1 bullets written.
- Minute 8: 3–6 entities with owners.
- Minute 18: public methods named; one class sketched deeper.
- Minute 28: happy + one failure traced.
- Minute 33: one extension seam named (only if level asks).
Common interview pitfalls
These mistakes show up constantly on this prompt. Name the trap, then show the fix in your design — don’t wait for the interviewer to catch you.
- Skipping requirements because the prompt “seems obvious.”
- Entity soup — twenty classes, no orchestrator.
- Implementing everything except the main user verb.
- Extensibility first, working path never.
- Freezing when twisted instead of pointing at a seam.
Interview script (say this)
Read this once out loud before a mock. It’s the spine of a strong answer — not a script to recite robotically.
- I’ll take five minutes to lock in/out of scope.
- Then nouns that own changing rules — not every noun in the story.
- Class design centers on the primary APIs.
- I implement one happy path deeply.
- I trace failure before I decorate with patterns.
- If time and level allow, I show one extension seam.
- I manage the clock out loud so we finish.
- Ask me a twist whenever you want — I’ll absorb it at the seam.
Extra verification traces
Walk these three traces on the board. If you can narrate them cleanly, your implementation section usually follows.
Staff-level follow-ups
At staff+, they twist the prompt. Answer in one sentence that names the seam — don’t redesign the whole board.
- Too slow on requirements? — Template questions; force-lock at minute 5.
- Interviewer hostile to patterns? — Use plain classes; introduce pattern only if asked.
- Ran out of time? — Narrate remaining methods; don’t silently code forever.
Where concurrency fits in the 5-step spine
- Clarify — ask: single-threaded OK, or must be thread-safe? Same machine only?
- Model — mark which objects own shared mutable state.
- APIs — note which methods are synchronized / need locks.
- Implement — write the locked critical section, not just the happy path.
- Verify — always narrate one two-thread race + the fix.