SevyDevy

Guide · Interview prep

Low-Level Design Interview Guide: How LLD Rounds Are Judged

25 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • An LLD interview, short for low-level design interview, asks you to turn a vague problem into classes and often working code. A class is the code blueprint for one part. The round judges how you get there, not only whether the code runs.
  • Interviewers collect signals: small pieces of evidence they note while you work. Common ones are how you clarify requirements, give each class one job, name things, absorb a change and explain trade-offs.
  • Spend roughly the first ten minutes of a 45-minute round on questions and a short class list. Use a design pattern, a known reusable code shape, only for a problem you can name.
  • Expect a change request: a new rule added near the end. In this guide's tested Python, the new rule is one new class, and the existing classes have zero edited lines.
  • Beginners can stop at the line that says so, after the preparation plan. Engineers then get a tested double booking. A lock is a guard that lets one request in at a time. In our Python 3.13.16 test without one, 20,000 runs looked safe, yet a 10 millisecond pause, standing in for a slow database call, made 200 of 200 fail. They also get a PostgreSQL database rule that refuses overlapping bookings even when many copies of the app run at once.

What goes wrong when your LLD interview code runs but you still fail?

The round was scoring how you designed, not only whether the program ran. In an LLD interview, working code with one giant class, no questions asked and no reasons given can still end in a no.

Picture a common setup. You reach the final interview round at a company, and it is low-level design: design a vending machine. You hear the problem, nod and start typing. The round is made up, but its three mistakes are the ones this guide fixes.

In 40 minutes you write about 300 lines. Coins go in, snacks come out, and your test run works. A week later, the feedback arrives in three lines.

  • Did not ask what the machine must support, so missed card payments and refunds.
  • Put coins, stock, display and payment logic into one class called VendingMachine.
  • When asked to add a new payment type, had to change code in six places.

None of the three lines is about a crash. Each one is about how the design was reached, and how it behaved when the problem changed.

This guide uses one practice problem from start to end: a meeting room booking system for a co-working space. You will see what interviewers judge, a 45-minute plan, and tested Python for the core classes.

What is an LLD interview?

An LLD interview is a round where you design the classes, methods and relationships for one small system, and often write the core code. LLD stands for low-level design.

Compare it with high-level design, often just called system design. High-level design asks how servers, databases, caches and queues fit together for millions of users. Low-level design zooms into one program and asks how its code should be organised.

Typical LLD problems are small, familiar systems: a parking lot, an elevator, a movie ticket app, a cricket scoreboard, a library, a vending machine or a chess game. Everyone knows how they work, so the time goes on design, not on explaining the business.

A few words come up all the time. A class is a blueprint, like Room. An object is one real thing made from it, like one room on the third floor. A responsibility is the one job a class owns, like knowing whether a room is free.

An interface is a list of methods a class promises to provide, without saying how. Code that uses an interface depends on the promise, not on any one class behind it.

Companies run the round in two common styles. In a design discussion, you mostly talk, sketch classes and write a few key methods. In a machine coding round, you write a working program in the time given, and someone often reviews it with you afterwards.

How are LLD interview rounds judged?

LLD rounds are judged on signals, the small pieces of evidence an interviewer writes down while watching you. Your final code is one signal, but how you got there carries much of the weight.

Think of a buyer visiting a pottery workshop before placing a big order. He asks the potter to throw one sample pot while he watches. He does not only look at the finished pot. He watches how it was made.

  • The sample pot is your design, and the buyer is the interviewer.
  • How the potter centres the clay is how you clarify requirements before building.
  • How evenly the walls rise is how cleanly you split the work between classes.
  • How calmly the potter recovers from a wobble is how you handle a late change request.

Where the picture breaks: the buyer stays silent, but your interviewer talks. Their questions and hints are part of the test, and answering them well is a signal too.

An LLD interviewer keeps notes while you work, writing what each choice shows. At the end, those notes become a hire or no-hire recommendation.

Every company has its own rubric, the written list of what to judge. The table shows signals that fit this guide's practice problem, with a weak and a strong answer for each.

LLD interview signals, shown on the meeting room booking problem
SignalWhat a weak answer looks likeWhat a strong answer looks like
RequirementsStarts coding at once and guesses what rooms needAsks about repeating bookings, capacity and cancelling first, then writes a short scope list
ResponsibilitiesOne BookingManager class does everythingRoom, Booking, BookingService and a room-picking rule each own one job
Naming and readabilityNames like data, temp2 and processAllNames like is_free, reserve and SmallestFitPolicy that explain themselves
Handling changeA new rule means editing old classes in many placesA new rule means adding one new class
Correctness and edge casesNever thinks about two people booking the same slotNames the double booking risk and guards it
CommunicationWorks in silence, then explains at the endThinks out loud and checks choices with the interviewer

This table has no row for how many patterns you named or how many lines you wrote. The 300 lines in the vending machine round earned nothing extra.

If you are senior, plan to drive the round yourself. Raise edge cases before you are asked, and defend trade-offs without waiting for hints.

What should you do in the first 10 minutes of a low-level design interview?

Clarify the requirements, agree on the scope and list the main objects. Do not write code yet. This is the step the vending machine round skipped.

In the practice round, a friend plays the interviewer. The prompt is one line: design a meeting room booking system. This time you follow a fixed opening.

  1. Repeat the problem back

    Say it in your own words: people book rooms for a time slot, and two bookings must never overlap in one room.

  2. Ask about features

    Can bookings repeat every week? Can people cancel? Do rooms have a capacity? The friend says yes to cancel and capacity, and no to repeats for now.

  3. Ask about rules and limits

    What if no room fits? How far ahead can people book? The answers: show an error, and up to 30 days ahead.

  4. Write the scope down

    Type a short list of what is in and what is out. Repeating bookings go in the out list, so nobody forgets they were skipped on purpose.

  5. List the nouns and verbs

    Nouns become candidate classes: Room, Booking, User, Request. Verbs become candidate methods: book, cancel, is_free.

In the practice round this takes about eight minutes (made up). It also gives you a list to point to later, when the interviewer asks why repeating bookings are missing.

Here is one way to split a 45-minute round. Read the boxes in order. The question box near the end is where the interviewer adds a twist, which you will meet later in this guide.

A 45-minute LLD round, step by step

The minutes are a suggested split, not a rule. Notice that code starts only at the fourth box, after the scope and classes are agreed.

Diagram connections:

  • Clarify requirements to List classes and jobs
  • List classes and jobs to Walk one main flow
  • Walk one main flow to Code the core classes
  • Code the core classes to Change request?
  • Change request? to Extend the design (Yes)
  • Change request? to Sum up trade-offs (No)
  • Extend the design to Sum up trade-offs
  • Clarify requirements: About 8 minutes of questions
  • List classes and jobs: Nouns, verbs and one job each
  • Walk one main flow: Book a room, end to end
  • Code the core classes: Only the important methods
  • Change request?: Interviewer adds a new rule
  • Extend the design: Add a class, keep old code
  • Sum up trade-offs: What you skipped and why

How do you pick classes and responsibilities in LLD?

Pick classes from the main nouns in the requirements, then give each class one clear job. If you cannot describe a class in one short sentence, it probably does too much.

This idea has a name: the Single Responsibility Principle, the S in SOLID. SOLID groups five design principles for object-oriented code, code built from classes and objects, that Robert C. Martin brought together in a 2000 paper. Michael Feathers coined the acronym itself around 2004.

Martin's 2014 post states it as: each software module should have one and only one reason to change. He then says the principle is about people. His examples are company roles such as the CFO, who runs finance, and the COO, who runs operations.

So if two such groups keep asking for changes to one class, split it. The old VendingMachine class broke this rule: stock, price and payment changes all landed in one file. For the booking problem, each job gets its own home.

Look at the class map below. Each box is one class, and the note says the one job it owns. Follow the arrows from the user to see who calls whom.

Class map for room booking

Each class owns one job. The room-picking rule sits behind an interface, so a new rule plugs in without editing BookingService or Room.

Diagram connections:

  • User to BookingService (book(request))
  • BookingService to SlotPolicy (interface) (pick_room)
  • SmallestFitPolicy to SlotPolicy (interface) (implements)
  • BookingService to Room (reserve)
  • Room to Booking (creates and stores)
  • User: Asks for a room and a time slot
  • BookingService: Runs the booking flow, start to end
  • SlotPolicy (interface): Decides which free room to offer
  • SmallestFitPolicy: Picks the smallest room that fits
  • Room: Knows its capacity and its bookings
  • Booking: One slot in one room; can be cancelled

One choice here is worth copying. Room answers whether it is free. BookingService never reaches into a room's list of bookings to decide for it.

This habit is often called Tell, Don't Ask: tell an object what to do, instead of pulling out its data and deciding outside it. Martin Fowler's 2013 note says the principle helps people remember that object-oriented code bundles data with the functions that work on it.

Fowler also writes that he does not use the slogan himself. He looks to co-locate data and behaviour instead, which often leads to similar results. Quote the goal, not the slogan, if an interviewer pushes back.

When should you use design patterns in an LLD interview?

Use a design pattern only when you can name the problem it solves in this design. A pattern added just to show you know it makes the design harder to read.

A design pattern is a known, reusable shape for a common design problem. The best-known catalogue is the 1994 book Design Patterns by Gamma, Helm, Johnson and Vlissides, which describes 23. These five fit many classic LLD problems.

  • Strategy: swap one rule for another without touching the caller, like different ways to pick a room.
  • Factory: one place that creates objects, so callers need not know the exact class. The book describes two related patterns, Factory Method and Abstract Factory.
  • Observer: tell many listeners when something happens, like sending a reminder when a booking is made.
  • State: change behaviour based on the current stage, like a vending machine that is idle, has money, or is dispensing.
  • Singleton: exactly one shared instance. Use it rarely. It adds global state, data any part of the program can reach, which hides who depends on it. It can also make unit tests, small automatic checks of one piece of code, harder.

A good practice rule: say the problem before the pattern. First: the room-picking rule will probably change. Only then: so I will put it behind a Strategy.

Google's public code review guide makes the same point about real code. It tells reviewers to be especially vigilant about over-engineering, and to have developers solve the problem they know needs solving now.

How do interviewers test your design with a change request?

They add a new rule near the end and watch how much old code you must change. If the rule lands as one new class, your split of responsibilities was right.

In the vending machine round, the change was card payments, and the single class needed edits in six places. In the practice round, the friend asks: some teams want the room nearest their desks, not the smallest room that fits.

The room-picking rule is already behind an interface. In Python, that interface is an abstract base class: a class that marks some methods as required, and can still hold normal methods. A subclass is a class built from another one, inheriting its methods. Python will not create an object of any subclass that leaves a required method unfilled.

Here is the core code, with the new rule already added as NearestRoomPolicy. It runs together with the second block later in this guide.

python
from abc import ABC, abstractmethod
from dataclasses import dataclass
import threading


class NoRoomFree(Exception):
    pass


class SlotTaken(Exception):
    pass


@dataclass(frozen=True)
class Request:
    user: str
    people: int
    start: int  # hour on a 24-hour clock, so 15 means 3 pm
    end: int

    def __post_init__(self):
        if self.start >= self.end:
            raise ValueError("a booking must end after it starts")


class SlotPolicy(ABC):
    @abstractmethod
    def pick_room(self, rooms, request):
        ...


class SmallestFitPolicy(SlotPolicy):
    def pick_room(self, rooms, request):
        fits = [r for r in rooms if r.capacity >= request.people
                and r.is_free(request.start, request.end)]
        return min(fits, key=lambda r: r.capacity, default=None)


class NearestRoomPolicy(SlotPolicy):
    def __init__(self, distance):
        self.distance = distance  # metres from the team's desks

    def pick_room(self, rooms, request):
        fits = [r for r in rooms if r.capacity >= request.people
                and r.is_free(request.start, request.end)]
        return min(fits, key=lambda r: self.distance[r.name], default=None)


class BookingService:
    def __init__(self, rooms, policy: SlotPolicy):
        self.rooms = rooms
        self.policy = policy

    def book(self, request):
        room = self.policy.pick_room(self.rooms, request)
        if room is None:
            raise NoRoomFree(request)
        return room.reserve(request)

Two parts matter most. The abstractmethod marker makes Python refuse to create any policy object that has not filled in pick_room. BookingService receives its policy from outside, so it never knows which rule it runs.

Request is a frozen dataclass: Python writes its constructor, the method that builds each object, from the listed fields, the values each object stores. Assigning to a field later raises an error, so code cannot change a request by accident. Its check refuses a booking that does not end after it starts. Times are whole hours to keep the example short.

NearestRoomPolicy takes each room's distance in metres from the team's desks. We compared the file before and after adding it. The only new lines are this class and the four demo lines that use it, and BookingService and Room have zero edited lines. A room missing from the distance map raises KeyError, Python's error for a missing dictionary key, so pass every room.

That is the O in SOLID, the Open-Closed Principle, credited to Bertrand Meyer in 1988. Software should be open for extension but closed for modification. Meyer's version relied on inheritance, building a new class on top of an existing one; the interface-based reading used here became common in the 1990s.

Here is what a change request did to each design. Compare how many places each new rule had to touch.

A change request on two designs

The vending machine change touched six places in one class. The booking change added one class and edited none.

Diagram connections:

  • Interviewer's change to One VendingMachine class (Vending machine round)
  • One VendingMachine class to Edits in six places
  • Interviewer's change to Add NearestRoomPolicy (Practice round)
  • Add NearestRoomPolicy to Pass it to BookingService
  • Interviewer's change: A new rule must be added
  • One VendingMachine class: Coins, stock, display, payment together
  • Edits in six places: Each edit can break something old
  • Add NearestRoomPolicy: One new class with pick_room
  • Pass it to BookingService: BookingService and Room: zero edits

What mistakes make you fail a low-level design interview?

The mistakes below are the opposites of the signals in the table, so each one costs a signal the interviewer wanted to write down. Most are about process, not the language.

  • Coding before asking anything, so you solve a different problem from the one the interviewer had in mind.
  • A god class: one class that knows and does everything, like the VendingMachine class.
  • Inheritance everywhere, even where an object could simply hold another object. The Design Patterns book advises favouring this holding, called composition, over class inheritance.
  • Getters and setters on every field. These are methods that only read or write one field, and they let other classes reach in and make decisions that belong to the object.
  • Patterns named but never needed, which eats time and hides the real design.
  • Ignoring edge cases such as an empty room list, a booking that ends before it starts, or two users at once.
  • Long silences. The interviewer cannot score thinking they cannot hear.

Keep this list next to your practice notes. After each mock round, tick the ones you fell into, and pick one to fix in the next round.

How should you prepare for an LLD interview in a few weeks?

Learn a few principles well, practise classic problems out loud, and do timed mock rounds with someone who interrupts you. Reading finished designs alone does not train you to talk while you design.

Here is one five-week plan for someone with a full-time job, at about an hour on weekdays and three hours on Saturdays. Stretch or shrink it to fit what you already know.

  • Week 1: the five SOLID principles, each tried on a small example you write yourself.
  • Week 2: the five patterns above, with one problem each pattern really fits.
  • Weeks 3 and 4: one classic problem every two days, such as a parking lot, an elevator and a movie ticket app.
  • Week 5: three timed mock rounds with a friend, each ending with a change request you have not seen.

Practise in the language you will use on the day. If the company lets you pick, pick the one you type fastest in. Fighting the syntax costs minutes you need for design.

Google's code review guide lists what its reviewers look for, including design, functionality, complexity, tests, naming, comments and style. Review your own practice code against that list after each problem.

How do you stop two people booking the same room at once?

Make the check and the save one step that only one request can enter at a time. Inside one Python program, a threading.Lock does this.

A running program is called a process, and a thread is one line of work inside it. Concurrency means several threads, or other pieces of work, running at overlapping times. A race condition is a bug where the result depends on which piece of work happens to run first.

Here is the risk. Two people ask for Room A, 3 to 4 pm, a moment apart. Both checks see the room as free, both bookings go in, and the room is double-booked.

python
class Booking:
    def __init__(self, room_name, request):
        self.room_name = room_name
        self.user = request.user
        self.start = request.start
        self.end = request.end
        self.cancelled = False

    def cancel(self):
        self.cancelled = True


class Room:
    def __init__(self, name, capacity):
        self.name = name
        self.capacity = capacity
        self.bookings = []
        self._lock = threading.Lock()

    def is_free(self, start, end):
        return all(b.cancelled or end <= b.start or start >= b.end
                   for b in self.bookings)

    def reserve(self, request):
        with self._lock:
            if not self.is_free(request.start, request.end):
                raise SlotTaken(self.name)
            booking = Booking(self.name, request)
            self.bookings.append(booking)
            return booking


if __name__ == "__main__":
    rooms = [Room("Room A", 4), Room("Room B", 10)]
    service = BookingService(rooms, SmallestFitPolicy())
    first = service.book(Request("u1", 3, 15, 16))
    print("u1 got", first.room_name)
    second = service.book(Request("u2", 3, 15, 16))
    print("u2 got", second.room_name)
    try:
        service.book(Request("u3", 2, 15, 16))
    except NoRoomFree:
        print("u3 got no room at 15-16")
    first.cancel()
    third = service.book(Request("u3", 2, 15, 16))
    print("after cancel, u3 got", third.room_name)
    metres = {"Room A": 40, "Room B": 5}
    nearest = BookingService(rooms, NearestRoomPolicy(metres))
    fourth = nearest.book(Request("u4", 2, 10, 11))
    print("nearest rule, u4 got", fourth.room_name)
    try:
        SlotPolicy()
    except TypeError as err:
        print("TypeError:", err)

The with self._lock line is the key. Python's threading documentation says a with block calls acquire on entry and release on exit, like a try and finally pair. So the lock is released even when SlotTaken is raised.

We checked that too: after a refused booking, the lock reports itself unlocked. is_free also skips cancelled bookings, and one booking may end exactly when the next starts. A 3 to 4 pm booking and a 4 to 5 pm booking both go in.

Run it and check the output

Save the two Python blocks, in order, as one file called booking.py. Run python3 booking.py on Python 3.12 or 3.13. On Python 3.13.16 it prints these six lines.

text
u1 got Room A
u2 got Room B
u3 got no room at 15-16
after cancel, u3 got Room A
nearest rule, u4 got Room B
TypeError: Can't instantiate abstract class SlotPolicy without an implementation for abstract method 'pick_room'

What just happened? Room A was the smallest fit for u1, so u2 got Room B. u3 found both rooms taken until u1 cancelled. The nearest rule sent u4 to Room B, 5 metres away, where the smallest rule would pick Room A.

Python 3.11 runs the same file but words the last error differently: with abstract method pick_room.

Why a passing test proves little about races

We ran two threads against reserve at the same moment, as of 19 October 2026 on CPython 3.13.16, the standard Python. We used its default build, whose global interpreter lock (GIL) lets only one thread run Python code at a time. We did not test Python 3.13's experimental free-threaded build, which can turn the GIL off. The table shows how many runs ended with two bookings in one slot.

Two threads booking Room A, 3 to 4 pm, at once (CPython 3.13.16, one sandbox)
SetupRunsRuns that double-booked
No lock, no pause20,0000
No lock, 10 ms pause between check and save200200
Guide's lock, as written20,0000
Guide's lock, 10 ms pause inside it2000

The 10 millisecond (ms) pause stands in for a slow database call. Without it, the broken version looked safe 20,000 times in a row. With it, the same code failed every time.

So a quick test that passes says little about races. Name the race in the interview even when your demo works, and show the lock that closes it.

The second check can refuse a free room

reserve checks is_free again, even though the policy already checked. The first check only chose a room. The second check, inside the lock, is the one that counts.

That has a cost. We added a 10 ms pause between picking and reserving. In 100 of 100 runs, both requests picked Room A, and the loser got SlotTaken while Room B was still free.

A production BookingService would catch SlotTaken and ask the policy again. Mention this fix even if you do not code it.

What changes when the booking app runs in many processes or servers?

A threading.Lock only guards one process. With two or more processes, on one server or many, each keeps its own lock and bookings, so the guard must move into the shared database. We checked this: two processes on one machine each booked Room A for 3 to 4 pm.

A unique rule on room and start time is not enough for free-form times. In our PostgreSQL 16.15 test, it accepted 3 to 4 pm and 3:30 to 4:30 pm in the same room. It works only when every booking is stored as fixed slots, such as one row per whole hour.

PostgreSQL's documentation suggests an exclusion constraint for ranges instead. It is a rule that no two rows may match on all the listed tests: here, the same room and overlapping times.

sql
CREATE EXTENSION IF NOT EXISTS btree_gist;

CREATE TABLE booking (
    room   text      NOT NULL,
    during tstzrange NOT NULL,
    EXCLUDE USING gist (room WITH =, during WITH &&)
);

INSERT INTO booking VALUES
    ('Room A', '[2026-10-20 15:00+05:30, 2026-10-20 16:00+05:30)');
INSERT INTO booking VALUES
    ('Room A', '[2026-10-20 16:00+05:30, 2026-10-20 17:00+05:30)');
INSERT INTO booking VALUES
    ('Room A', '[2026-10-20 15:30+05:30, 2026-10-20 16:30+05:30)');

A tstzrange is a range of timestamps that carry a time zone. The square bracket includes the start and the round bracket excludes the end, so the 4 to 5 pm row fits beside the 3 to 4 pm row. The 3:30 to 4:30 pm row is refused with the error below. psql, PostgreSQL's command-line tool, also prints a DETAIL line, not shown here, naming both clashing ranges in your connection's time zone setting.

text
ERROR:  conflicting key value violates exclusion constraint "booking_room_during_excl"

The btree_gist extension lets the equality test on room join the range test. It is one of PostgreSQL's contrib modules, which some packages ship separately, such as postgresql-contrib. Without it, PostgreSQL 16.15 refused to create the table.

We also raced two sessions, two separate connections to the database. The second insert waited about 1.6 seconds for the first session's transaction, its group of changes, to commit, or be saved for good. Then it failed with the same error. The database, not the app server, picked the winner.

How do you explain your low-level design in a system design interview?

Explain it in the order you built it: scope, classes, one main flow, the change point, then risks. Keep each part short, and check with the interviewer before moving on.

  1. State the scope

    Read your in and out lists aloud. This shows you made choices on purpose, and stops surprise questions about skipped features.

  2. Name each class and its one job

    One sentence per class, such as: Room knows its capacity and whether it is free.

  3. Walk one flow end to end

    Follow a single booking from the user's request to the saved Booking, naming each method call on the way.

  4. Point to where change goes

    Show where a new rule would plug in, such as a new SlotPolicy, before the interviewer asks.

  5. Name the risks

    Mention double booking, the lock that guards it, and the database rule you would add for many processes or servers.

  6. Admit what you skipped

    Say what you left out and what you would add next, like repeating bookings or reminders.

If the interviewer stops you to ask why Booking is not just a dictionary, have the one-line reason ready. A booking can be cancelled, so it has behaviour of its own.

Questions people ask

What is the difference between an LLD interview and a system design interview?
An LLD interview focuses on the code inside one program: classes, methods and how they work together. A system design interview, also called high-level design, focuses on how servers, databases and queues fit together at scale. A company may run either round or both, so ask the recruiter which ones to expect.
Which programming language should I use in an LLD interview?
Use the language you write most fluently, as long as it has classes and some way to express interfaces. Java, Python, C++, C# and TypeScript all do. Check the company's allowed list with the recruiter, and do not switch languages just for the interview.
Do I need to write full working code in an LLD round?
It depends on the format. In a design discussion, you usually write class outlines and a few key methods. In a machine coding round, you are expected to deliver a working program in the time given. Ask the recruiter which format to expect, and practise that one.
How many design patterns should I know for an LLD interview?
There is no official number. Know the five in this guide well: Strategy, Factory, Observer, State and Singleton. Between them they fit the classic problems named here, such as Strategy for picking a room and State for a vending machine. Knowing when each one does not fit matters more than a long list.
Should I draw a UML class diagram in an LLD interview?
Ask whether the interviewer wants UML. UML, the Unified Modeling Language, is a standard notation for drawing classes and their links. If they do not ask for it, simple boxes with class names, key methods and arrows are enough, as long as the interviewer can follow them.
What are the most common LLD interview questions?
Classic practice problems include a parking lot, an elevator, a movie ticket booking system, a library, a vending machine, a cricket scoreboard, a chess game and a splitting-expenses app. They are small and familiar, so the time goes on your classes, rules and edge cases.
How long does it take to prepare for an LLD interview?
There is no fixed answer; it depends on how much object-oriented code you already write. The plan in this guide uses five weeks at about an hour a weekday. Spend the early part on principles and patterns, and most of the time solving problems out loud, with timed mocks near the end.

Sources and further reading

Practise this with Lala

Start