LLD overview

Low-level design overview

Practical map of LLD/OOD interviews: what they test, how they differ from system design, regional format quirks, and how interviewers score you.

Learn low-level design for the interview, not the textbook

01Objects

Right classes & ownership

02APIs

Methods that match the rules

03Extend

Absorb follow-ups cleanly

04Judge

Simple when simple works

Low-level design interviews test whether you can structure code for a self-contained problem. You get a system — Connect Four, an elevator controller, a parking lot — and you design the classes, interfaces, and relationships that make it work.
The focus is code organization: What are the right objects? How do they interact? What methods do they expose? Can the design stretch without a rewrite?

What this Lattice guide is (and isn’t)

This is a practical guide written for how interviews actually run: fast decisions, clear narration, and designs a real team would ship. It is not a classroom catalog of every pattern ever named.
A lot of online LLD content still reads like a textbook — abstract principles, exhaustive GoF lists, and inheritance-heavy OOP that modern codebases have moved past. We deliberately favor composition over deep hierarchies, simple state over pattern worship, and teaching to the mode of interviews rather than every regional edge case.
  1. Delivery framework — how to spend ~35 minutes without drowning.
  2. Fundamentals refresh — design principles, OOP concepts, the patterns that actually show up.
  3. Problem breakdowns — parking, rate limiter, elevator, booking, locker, filesystem, logging, Connect Four.
  4. Guided practice mindset — consume less, apply more; walk through solutions out loud.

Low-level design vs system design

LLD vs HLD on ride-sharing
Same product idea — map (HLD) vs blueprint for one building (LLD).
Despite similar names, these rounds barely overlap. System design is architecture at scale: traffic, storage, consistency, caching, sharding, tradeoffs. You sketch services on a whiteboard. Almost no discussion of classes or method signatures.
Low-level design is the opposite: you define objects, model data, and shape interactions for a single feature or service. Instead of boxes and arrows between microservices, you work through classes, methods, relationships, and state transitions.
Ride-sharing makes the contrast obvious. In system design you draw matching, pricing, location, an API gateway, Redis for driver locations, a match queue, notification fan-out — and talk about load and reliability. In LLD you might design Trip, a TripState enum, a PricingCalculator interface, and how those objects collaborate. No fleet-scale story required.
from enum import Enum, auto
from typing import Optional, Protocol


class TripState(Enum):
    REQUESTED = auto()
    DRIVER_ASSIGNED = auto()
    IN_PROGRESS = auto()
    COMPLETED = auto()
    CANCELLED = auto()


class PricingCalculator(Protocol):
    def calculate_fare(self, trip: "Trip") -> float: ...


class Trip:
    def __init__(self, trip_id: str, rider, pickup, dropoff):
        self.id = trip_id
        self.rider = rider
        self.driver = None
        self.pickup = pickup
        self.dropoff = dropoff
        self.state = TripState.REQUESTED
        self.fare: Optional[float] = None

    def assign_driver(self, driver) -> None:
        if self.state is not TripState.REQUESTED:
            raise ValueError(f"cannot assign in {self.state}")
        self.driver = driver
        self.state = TripState.DRIVER_ASSIGNED

    def start(self) -> None:
        if self.state is not TripState.DRIVER_ASSIGNED:
            raise ValueError(f"cannot start in {self.state}")
        self.state = TripState.IN_PROGRESS

    def complete(self, calculator: PricingCalculator) -> None:
        if self.state is not TripState.IN_PROGRESS:
            raise ValueError(f"cannot complete in {self.state}")
        self.fare = calculator.calculate_fare(self)
        self.state = TripState.COMPLETED

    def cancel(self) -> None:
        if self.state is TripState.COMPLETED:
            raise ValueError("cannot cancel a completed trip")
        self.state = TripState.CANCELLED
enum TripState {
    REQUESTED,
    DRIVER_ASSIGNED,
    IN_PROGRESS,
    COMPLETED,
    CANCELLED
}

interface PricingCalculator {
    double calculateFare(Trip trip);
}

class Trip {
    final String id;
    final Rider rider;
    Driver driver;
    final Location pickup;
    final Location dropoff;
    TripState state = TripState.REQUESTED;
    Double fare;

    Trip(String tripId, Rider rider, Location pickup, Location dropoff) {
        this.id = tripId;
        this.rider = rider;
        this.pickup = pickup;
        this.dropoff = dropoff;
    }

    void assignDriver(Driver driver) {
        if (state != TripState.REQUESTED) {
            throw new IllegalArgumentException("cannot assign in " + state);
        }
        this.driver = driver;
        this.state = TripState.DRIVER_ASSIGNED;
    }

    void start() {
        if (state != TripState.DRIVER_ASSIGNED) {
            throw new IllegalArgumentException("cannot start in " + state);
        }
        this.state = TripState.IN_PROGRESS;
    }

    void complete(PricingCalculator calculator) {
        if (state != TripState.IN_PROGRESS) {
            throw new IllegalArgumentException("cannot complete in " + state);
        }
        this.fare = calculator.calculateFare(this);
        this.state = TripState.COMPLETED;
    }

    void cancel() {
        if (state == TripState.COMPLETED) {
            throw new IllegalArgumentException("cannot cancel a completed trip");
        }
        this.state = TripState.CANCELLED;
    }
}

How LLD interviews vary by company and region

The core idea is the same everywhere, but format shifts enough that you should know the main patterns — then ask your recruiter what your target expects.
  • Pseudocode vs real code. US big tech often wants class definitions and method logic in a real language (Java, Python, C++) even if nothing compiles. Many interviews in India and elsewhere lean toward structured pseudocode. Lattice breakdowns use readable Python-style pseudocode; classics include fuller references where useful.
  • Pattern vocabulary. At big tech, naming Factory or Strategy matters less than sound reasoning — bring a pattern up when it naturally fits. Mid-size and some APAC loops ask about patterns more directly. Recognize the fit; don’t force a catalog.
  • Ambiguity. Requirements are often vaguer outside the US — you’re expected to ask grounding questions and drive scope. US prompts can be slightly more defined, but clarifying questions still win points.

What interviewers score

LLD assessment dimensions
Analysis · class design · code quality · extensibility · communication.
Rubrics differ by company, but the same five skills show up again and again. They’re checking whether you can turn a small, scoped problem into code a real team would want to maintain.
  • Problem analysis. Extract entities and responsibilities, ask a few clarifying questions, frame the problem before typing. Jumping straight into code is a common fail.
  • Class design. Right responsibilities, clear method signatures, clean ownership and boundaries. Weak class design makes everything downstream harder.
  • Code quality. Encapsulation, sensible state, composition vs inheritance judgment, naming, dependency direction — even in pseudocode, it should look disciplined.
  • Extensibility. Most loops add a small follow-up. Strong candidates absorb it without a rewrite. Practical flexibility beats anticipating every possible future.
  • Communication. Clear narrative, thoughtful tradeoffs, adjusting when probed. Talking out loud while you design is half the signal.

What changes at staff+

Staff bar
Extensibility, concurrency follow-ups, evolution story.
  • Name seams early when variation is real (Strategy for pricing, Factory for types).
  • Walk a failure path, not only the happy path.
  • If asked “two threads enter,” show the race and the lock/atomic.

How to use this series

  1. Delivery framework — timed spine + STAR narration hook
  2. OOP core concepts
  3. Guidelines, STAR, pattern instincts
  4. Pattern details (Strategy, Observer, State, Factory, …)
  5. Concurrency posts, then classic E2E designs

Worked mini-example: what “good” sounds like

Prompt: “Design a parking lot.” Weak answer jumps to Redis and microservices. Strong Lattice-style answer stays in-process objects for 25 minutes.

# Verbal outline (not production code)
# 1) Requirements: types, assigner, ticket, fee cents, rejects
# 2) Nouns: ParkingLot, ParkingSpot, Ticket, VehicleType
# 3) API: enter / exit
# 4) Trace: full lot + double exit
# 5) Twist: "two gates" → lock around find+occupy

Anti-patterns in LLD interviews

  • HLD cosplay — boxes and arrows for a class-design round.
  • Pattern laundry list — Strategy/Factory/Observer before state ownership.
  • Silent races — never mentioning check-then-act on shared seats/spots.
  • Infinite clarifying — minute 12 still asking UI questions.
  • Anemic models — bags of getters; all logic in a “ManagerGod”.

Decision checklist before you draw

  1. Is this LLD (objects) or HLD (services)? Confirm with interviewer if ambiguous.
  2. What is v1 in scope / explicitly out of scope?
  3. Which noun mutates, and who protects the invariant?
  4. What’s the single primary API path you’ll implement?
  5. What’s one failure path and one concurrency note you’ll volunteer?

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.

  • Treating LLD as tiny HLD — services first, classes never.
  • Never locking a v1; clarifying until time dies.
  • Ignoring how companies differ (code-heavy vs design-heavy).
  • Staff answer that never mentions evolution or concurrency.
  • Memorizing diagrams without ownership language.

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.

  1. LLD tests object modeling, APIs, invariants, and communication under time.
  2. It’s not system design — zoom is classes and methods.
  3. I’ll use a timed spine: requirements, entities, design, implement, extend.
  4. Companies vary: some want compiling code, some want clear design talk.
  5. Interviewers score clarity of ownership and trade-offs.
  6. Staff+ adds extensibility narrative and concurrency follow-ups.
  7. This Lattice series goes framework → principles → concurrency → classics.
  8. I’ll practice one classic timed per day.

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.

  • How do you prep? — One timed classic daily + post-mortem pitfalls list.
  • Code vs talk? — Ask interviewer format; default to talk+pseudocode unless they want compile.
  • Staff bar? — Seams for change + concurrency + evolution story.

← Lattice