bigonotes
CS ConceptsLLD

How to Approach an LLD Interview

A step-by-step framework for low-level design interviews: requirements, entities, class design, implementation and extensions.

6 min readLast updated LLD

A low-level design (LLD) interview asks you to design the classes behind a small system (a parking lot, a vending machine, a rate limiter) and usually write working code in about 45 minutes. Interviewers are judging structure as much as code: do you scope the problem, pick sensible classes, and leave room to extend? Following the same framework every time keeps you from getting lost in syntax before the design is clear.

The framework at a glance#

Every LLD interview follows the same six steps:

Step Time Output
1. Requirements and scope ~5 min 3–5 requirement sentences, actors, out-of-scope list
2. Entities and relationships ~5 min Classes with the data they hold, and who depends on whom
3. Class design ~10–15 min Enums, interfaces, class skeletons with signatures and pass
4. Implementation ~15–20 min The core methods, with edge cases handled
5. Extensibility and follow-ups remaining Self-review, extension points, concurrency discussion
6. Dry run throughout Trace one example end to end

A typical Python preamble:

from abc import ABC, abstractmethod
from datetime import datetime          # datetime.now() for timestamps
from enum import Enum
from typing import Dict, List, Optional, Tuple

Step 1: Requirements and scope (~5 minutes)#

Figure out what kind of system you're building before you design anything.

Actors#

Who uses the system? Just customers, or also an admin or an automated process? For a vending machine: customers and a restocker.

Also pin down the rules of completion: what counts as success, failure, or the end of the process (e.g. a game ends on a win or a full board).

Core use cases#

Write 3–4 concrete actions the system must support. These become your public API methods. For a vending machine:

  1. Customer inserts money.
  2. Customer selects a product.
  3. System dispenses the product and returns change.

Clarifying questions: VAME#

Letter Ask Example
Variations "Are they all the same?" Vehicle sizes, seat types, VIP users
Algorithms "How does it choose or calculate?" Pricing rules, matching, routing, spot assignment
Multiplicity "Is there only one?" One or many floors, gates, machines; one-to-one or one-to-many
Exceptions "What if it's full, lost, invalid?" Full lot, out of stock, invalid code

Out of scope#

Explicitly name what you won't build unless asked: UI, concurrency, real payment integration, persistence. Interviewers like this because it shows you set boundaries. For example: "For this interview I'll treat card payments, concurrency and UI as out of scope and focus on the state machine and inventory logic. Does that work?"

What a finished Step 1 looks like (tic-tac-toe)#

Requirements

  1. Two players alternate placing X and O on a 3x3 grid.
  2. A player wins by completing a row, column or diagonal.
  3. The game is a draw if all nine cells fill with no winner.
  4. Invalid moves are rejected (occupied cell, moving after the game ends).
  5. The current game state can be queried, and the game can be reset.

Out of scope: UI, AI opponent, networked multiplayer, N×N boards, undo/redo.

Step 2: Entities and relationships (~5 minutes)#

Turn the requirements into classes, then connect them.

Entities#

From the requirements, name the classes and the information each one holds. Nouns in the requirements are good candidates.

Relationships#

Then decide how they interact:

  • Which entity is the orchestrator driving the main workflow?
  • Which entities own durable state?
  • How do they depend on each other: has-a, uses, contains? (See Relationships.)
  • Where should each rule live? Put a rule in the class that owns the data it checks.

What a finished Step 2 looks like (parking lot)#

Entities

  1. Vehicle (license_plate, size)
  2. ParkingSpot (spot_id, size, vehicle)
  3. ParkingTicket (ticket_id, entry_time, vehicle, spot)
  4. ParkingLot (holds all ParkingSpots)
  5. ParkingLotManager (orchestrates enter, exit and payment)

Relationships

  • ParkingTicket → Vehicle, ParkingSpot
  • ParkingLot → ParkingSpot
  • ParkingLotManager → ParkingLot, strategies

Step 3: Class design (~10–15 minutes)#

Write the skeleton: enums, interfaces, classes, __init__ attributes and method signatures with type hints, with pass for the bodies. This proves you know how the pieces connect before you get into details.

Enums for the variations#

Avoid raw strings for fixed sets of values.

class VehicleSize(Enum):
    MOTORCYCLE = 1
    CAR = 2
    TRUCK = 3

class SpotSize(Enum):
    COMPACT = 1
    REGULAR = 2
    LARGE = 3

Interfaces for the hidden complexity#

Anything you scoped out or that is likely to vary (pricing, assignment, payment) goes behind an abstract interface. Implement one simple concrete version.

class SpotAssignmentStrategy(ABC):
    @abstractmethod
    def get_available_spot(
        self, lot: "ParkingLot", vehicle: "Vehicle"
    ) -> Optional["ParkingSpot"]:
        pass

class PricingStrategy(ABC):
    @abstractmethod
    def calculate_price(self, ticket: "ParkingTicket") -> float:
        pass

Core classes#

For each class:

  1. __init__: what it receives (injected dependencies) and what it stores. Name your data structures and their trade-offs ("a dict keyed by size gives O(1) lookup").
  2. Methods: the actions it performs, with parameter and return types. One job per method.
  3. Data-management methods: how data gets in and out (add, remove, update, get).
class Vehicle:
    def __init__(self, license_plate: str, size: VehicleSize):
        self.license_plate = license_plate
        self.size = size

class ParkingSpot:
    def __init__(self, spot_id: str, size: SpotSize):
        self.spot_id = spot_id
        self.size = size
        self.vehicle: Optional[Vehicle] = None

    def is_free(self) -> bool:
        return self.vehicle is None

class ParkingTicket:
    def __init__(self, ticket_id: str, vehicle: Vehicle, spot: ParkingSpot):
        self.ticket_id = ticket_id
        self.vehicle = vehicle
        self.spot = spot
        self.entry_time = datetime.now()
        self.exit_time: Optional[datetime] = None

class ParkingLotManager:
    def __init__(self, lot: "ParkingLot",
                 spot_strategy: SpotAssignmentStrategy,
                 pricing_strategy: PricingStrategy):
        self.lot = lot
        self.spot_strategy = spot_strategy
        self.pricing_strategy = pricing_strategy
        self.active_tickets: Dict[str, ParkingTicket] = {}

    def park_vehicle(self, vehicle: Vehicle) -> Optional[ParkingTicket]:
        pass

    def check_out(self, ticket: ParkingTicket) -> float:
        pass

Step 4: Implementation (~15–20 minutes)#

Now fill in the method bodies, in this order of habits:

  1. Ask first whether the interviewer wants pseudocode or working code.
  2. Talk before you type. Describe each method in plain English, step by step. If a method needs to do a second thing, give that its own helper.
  3. Delegate the hard parts. Don't hand-roll sorting, database queries or complex math; call your strategy or helper.
  4. Handle the sad paths: None inputs, invalid states, invalid arguments, full or empty containers.
  5. Keep state consistent. When one object changes, update everything that refers to it (free the spot and drop the ticket).

Step 5: Extensibility and follow-ups#

The follow-ups test whether your design bends without breaking into special cases.

  • Spot your own bugs first. Pointing out a flaw before the interviewer does shows maturity.
  • Prove Open/Closed. Never say "I'd modify the existing method". Point to the extension point: "a new pricing rule is a new PricingStrategy subclass". See Open/Closed Principle.
  • Revisit what you scoped out. Bring concurrency and persistence back verbally: where is the race (usually a check-then-act on shared state), and would you use a lock, a per-entity lock, or a database transaction?

Step 6: Dry run#

Walk one concrete scenario through your code. For each conditional, trace one input that makes it true and one that makes it false. This catches off-by-one and state-cleanup bugs before the interviewer does.

Key takeaways#

  • Scope first: actors, 3–5 use cases, VAME clarifications, explicit out-of-scope list.
  • Entities come from nouns; put each rule in the class that owns the data.
  • Skeleton before implementation: enums, interfaces for anything that varies, signatures with pass.
  • Implement with one job per method and handle sad paths and state cleanup.
  • Close by naming extension points and discussing concurrency.

Found a mistake? Tell me on LinkedIn ↗

Further reading#