System Design · Interview Strategy

HLD vs LLD: What Is the Difference & How Each Interview Is Evaluated

The same problem, BookMyShow seat booking, answered as a high-level design and as a low-level design, with the rubric interviewers use for each and a prep plan by experience level.

✍️ Pranjal Jain, Ex-Microsoft · IIT Kanpur 📅 10 October 2026 ⏱ 13 min read
City map on one side and a detailed building blueprint on the other, representing high-level versus low-level design
TL;DR
  • HLD = architecture of the whole system: services, databases, caches, queues, APIs, scaling and trade-offs. Deliverable: a diagram plus reasoning.
  • LLD = internals of one component: classes, interfaces, relationships, patterns, concurrency, and usually working code. Deliverable: a class design plus code.
  • Machine coding is LLD under a stopwatch: a complete, runnable, extensible program in 60 to 120 minutes.
  • HLD is judged on requirements, estimation, data modeling, scaling, trade-offs. LLD is judged on abstractions, SOLID, extensibility, correctness and code quality.
  • Freshers face mostly LLD/OOP; 2 to 6 years get both; senior roles lean on HLD. Formats vary, so confirm with your recruiter.

Indian product-company interview loops increasingly contain two different "design" rounds, and candidates regularly prepare for the wrong one. Someone walks into a Flipkart-style machine coding round ready to talk about Kafka and sharding, or into a senior HLD round and starts writing a Seat class. This post draws the line precisely, then solves one problem, a BookMyShow-style seat booking system, at both levels so you can see exactly how the deliverables differ.

The One-Line Difference

HLD asks "what are the pieces and how do they talk at scale?" LLD asks "how is one piece written so it is correct and easy to change?"

Think of designing a city vs designing a building. The city plan (HLD) decides where the highways, power plants, water supply and districts go and how they handle a million residents. The building blueprint (LLD) decides rooms, wiring and plumbing for one building, in enough detail that a contractor can build it. Both are "design", but the vocabulary, artifacts and judgement criteria are different.

HLD vs LLD Side by Side

DimensionHigh-Level Design (HLD)Low-Level Design (LLD)
ScopeEntire system across machinesOne service, module or library
Unit of designServices, databases, caches, queues, CDNsClasses, interfaces, enums, methods
Core concernsScalability, availability, latency, consistency, costCohesion, coupling, extensibility, testability, thread safety
Typical artifactsArchitecture diagram, API list, data model, capacity estimatesClass diagram, sequence of method calls, runnable code
Key vocabularySharding, replication, CAP, caching, partitioning, fan-outSOLID, composition, design patterns, state machines, locks
Failure thinkingNode and network failures, retries, failoverInvalid input, illegal state transitions, race conditions
Code writtenLittle to none (API signatures, schema)A lot; often the main deliverable
Typical prompt"Design BookMyShow for 10M users""Design the classes for BookMyShow's booking flow"

The Same Problem as HLD: BookMyShow at Scale

Prompt: "Design a movie ticket booking system like BookMyShow." Here is a compressed version of what a strong 45-minute HLD answer covers. The full design is in Design BookMyShow.

1. Requirements

  • Functional: browse movies by city, view shows and seat map, hold seats for a few minutes, pay, get a ticket.
  • Non-functional: no double booking (strong consistency on seats), seat map loads fast, handles a blockbuster opening-day spike, payment failures release seats.

2. Estimates

  • Assume 10M DAU browsing, 1M bookings/day → ~12 bookings/s average, but a popular release opening bookings can create thousands of seat-hold attempts per second on a handful of shows.
  • The read path (catalog, show listings) is 100× the write path; the hard part is contention on a few hot shows, not raw volume.

3. Architecture

client ──► CDN (posters, static) │ └──► API gateway ──► catalog-svc ──► Postgres (movies, theatres, shows) │ └──► Redis cache / Elasticsearch (search) │ ├────► booking-svc ──► seat-hold: Redis SET NX EX 600 │ │ ──► Postgres: bookings, seat inventory │ │ (unique constraint per show+seat) │ ▼ └────► payment-svc ──► payment gateway (UPI/cards) │ webhook ▼ Kafka: booking.confirmed ──► notification-svc (SMS/email) ──► analytics

4. Deep dives an interviewer expects

  • Preventing double booking: hold seats with an atomic Redis SET seat:{show}:{seat} {user} NX EX 600 for all seats in one Lua script, and enforce correctness with a database unique constraint on (show_id, seat_id) for confirmed bookings. The cache is the fast path; the database is the source of truth.
  • Hold expiry and payment races: a payment that completes after the hold expired must be refunded or re-validated, never silently double-sold.
  • Hot show spike: virtual waiting room, rate limiting per user, shard seat state by show ID.
  • Idempotent payment callbacks, reconciliation jobs.

Notice what is absent: no class names, no method signatures, no patterns. Everything is about components, data, consistency and scale.

The Same Problem as LLD: The Booking Module in Code

Prompt: "Design the low-level classes for BookMyShow's booking flow. Multiple users may try to book the same seats concurrently. Seat pricing differs by category." A strong LLD answer starts with entities and behaviour, then code.

1. Entities and relationships

  • Movie, Theatre has many Screen, Screen has many Seat (with SeatCategory).
  • Show = movie + screen + start time.
  • Booking has user, show, seats, amount and a BookingStatus state machine: PENDING → CONFIRMED or PENDING → EXPIRED/CANCELLED.
  • SeatLockProvider interface: in-memory implementation now, Redis implementation later.
  • PricingStrategy interface: category-based now, dynamic/weekend pricing later (Strategy pattern).

2. Code (Java)

Java: core booking module
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

enum SeatCategory { SILVER, GOLD, RECLINER }
enum BookingStatus { PENDING, CONFIRMED, EXPIRED, CANCELLED }

record Seat(String id, SeatCategory category) {}
record Show(String id, String movieId, String screenId, Instant startsAt, List<Seat> seats) {}

interface PricingStrategy {
    long pricePaise(Show show, Seat seat);
}

final class CategoryPricing implements PricingStrategy {
    private final Map<SeatCategory, Long> prices;
    CategoryPricing(Map<SeatCategory, Long> prices) { this.prices = prices; }
    public long pricePaise(Show show, Seat seat) { return prices.get(seat.category()); }
}

interface SeatLockProvider {
    boolean lockAll(String showId, List<String> seatIds, String userId);   // all or nothing
    void unlockAll(String showId, List<String> seatIds, String userId);
    boolean isLockedBy(String showId, String seatId, String userId);
}

final class InMemorySeatLockProvider implements SeatLockProvider {
    private record Lock(String userId, Instant expiresAt) {}
    private final Map<String, Map<String, Lock>> locks = new ConcurrentHashMap<>();
    private final Duration ttl;
    InMemorySeatLockProvider(Duration ttl) { this.ttl = ttl; }

    public boolean lockAll(String showId, List<String> seatIds, String userId) {
        Map<String, Lock> showLocks = locks.computeIfAbsent(showId, k -> new HashMap<>());
        synchronized (showLocks) {                       // per-show lock: shows don't block each other
            Instant now = Instant.now();
            for (String s : seatIds) {
                Lock l = showLocks.get(s);
                if (l != null && l.expiresAt().isAfter(now) && !l.userId().equals(userId)) return false;
            }
            for (String s : seatIds) showLocks.put(s, new Lock(userId, now.plus(ttl)));
            return true;
        }
    }

    public void unlockAll(String showId, List<String> seatIds, String userId) {
        Map<String, Lock> showLocks = locks.get(showId);
        if (showLocks == null) return;
        synchronized (showLocks) {
            for (String s : seatIds) {
                Lock l = showLocks.get(s);
                if (l != null && l.userId().equals(userId)) showLocks.remove(s);
            }
        }
    }

    public boolean isLockedBy(String showId, String seatId, String userId) {
        Map<String, Lock> showLocks = locks.get(showId);
        if (showLocks == null) return false;
        synchronized (showLocks) {
            Lock l = showLocks.get(seatId);
            return l != null && l.userId().equals(userId) && l.expiresAt().isAfter(Instant.now());
        }
    }
}

final class Booking {
    final String id; final String userId; final Show show; final List<Seat> seats; final long amountPaise;
    private BookingStatus status = BookingStatus.PENDING;
    Booking(String id, String userId, Show show, List<Seat> seats, long amountPaise) {
        this.id = id; this.userId = userId; this.show = show; this.seats = seats; this.amountPaise = amountPaise;
    }
    synchronized BookingStatus status() { return status; }
    synchronized void transition(BookingStatus next) {
        boolean ok = status == BookingStatus.PENDING && next != BookingStatus.PENDING;
        if (!ok) throw new IllegalStateException(status + " -> " + next);
        status = next;
    }
}

final class BookingService {
    private final SeatLockProvider locks;
    private final PricingStrategy pricing;
    private final Map<String, Set<String>> confirmedSeats = new ConcurrentHashMap<>(); // showId -> seatIds
    private final Map<String, Booking> bookings = new ConcurrentHashMap<>();

    BookingService(SeatLockProvider locks, PricingStrategy pricing) {
        this.locks = locks; this.pricing = pricing;
    }

    Booking hold(String userId, Show show, List<String> seatIds) {
        Set<String> taken = confirmedSeats.computeIfAbsent(show.id(), k -> ConcurrentHashMap.newKeySet());
        for (String s : seatIds) if (taken.contains(s)) throw new IllegalStateException("seat taken: " + s);
        if (!locks.lockAll(show.id(), seatIds, userId)) throw new IllegalStateException("seats held by someone else");

        List<Seat> seats = show.seats().stream().filter(s -> seatIds.contains(s.id())).toList();
        long amount = seats.stream().mapToLong(s -> pricing.pricePaise(show, s)).sum();
        Booking b = new Booking(UUID.randomUUID().toString(), userId, show, seats, amount);
        bookings.put(b.id, b);
        return b;
    }

    void confirm(String bookingId) {                      // called after payment succeeds
        Booking b = bookings.get(bookingId);
        Set<String> taken = confirmedSeats.get(b.show.id());
        synchronized (taken) {
            for (Seat s : b.seats) {
                if (!locks.isLockedBy(b.show.id(), s.id(), b.userId)) {
                    b.transition(BookingStatus.EXPIRED);     // hold lapsed: refund path
                    return;
                }
            }
            b.seats.forEach(s -> taken.add(s.id()));
            b.transition(BookingStatus.CONFIRMED);
        }
        locks.unlockAll(b.show.id(), b.seats.stream().map(Seat::id).toList(), b.userId);
    }
}

3. What the LLD interviewer probes next

  • "Why an interface for SeatLockProvider?" (Dependency inversion: swap in Redis for multi-instance deployment without touching BookingService.)
  • "Add weekend surge pricing." (New PricingStrategy, or a decorator over the base strategy; open/closed principle.)
  • "Two users click the same seat at the same millisecond." (Walk through the per-show synchronized block; all-or-nothing locking.)
  • "Why not one global lock?" (It serializes every show in the country; per-show locks keep contention local.)
  • "Notify the user on confirmation." (Observer or an event publisher interface.)

Compare the two answers: the HLD solved double booking with Redis SET NX plus a database unique constraint across many servers; the LLD solved it with an interface, a per-show lock and a state machine inside one process. Same requirement, different altitude. Patterns used here are covered in Top 10 Design Patterns for Interviews.

LLD Discussion vs Machine Coding Round

LLD discussion roundMachine coding round
Duration45–60 minutesCommonly 90–120 minutes of coding, plus review
OutputClass diagram, key methods, partial codeComplete, runnable program (often in your own IDE)
Persistence/UINot neededIn-memory storage, CLI or method-call driver
Judged onDesign reasoning and communicationWorking code, modularity, extensibility, edge cases, readability
Typical problemsParking lot, elevator, chess, vending machineSplitwise, snake and ladder, cab booking, cache with eviction, rate limiter, library system
Follow-up"How would you extend it?"A new requirement added mid-round or in review

In machine coding, a working, well-structured 70% solution beats a beautiful but non-running 100% design. Get the core flow running first, then add features. Worked problems: Parking Lot, Splitwise, Snake and Ladder.

How Each Round Is Evaluated

Rubrics differ by company, but the dimensions below reflect what interviewers commonly write in feedback.

HLD rubric

DimensionStrong signalWeak signal
RequirementsAsks clarifying questions, separates functional and non-functional, scopes downStarts drawing immediately
EstimationQuick numbers that drive decisionsSkipped, or 15 minutes of arithmetic
API and data modelClear endpoints, entities, keys, access patternsVague "we store it in a DB"
ArchitectureCoherent components, clear data flowBuzzword soup (Kafka everywhere)
Deep divesGoes deep on the hardest part (consistency, hot keys)Stays shallow everywhere
Trade-offsNames alternatives and why one was chosenPresents one option as obviously right
Failure handlingRetries, idempotency, failover, degradationAssumes nothing fails

LLD / machine coding rubric

DimensionStrong signalWeak signal
Requirement captureLists use cases and constraints before codingCodes the first idea
AbstractionsRight entities; interfaces at points of variationGod class; everything in main
SOLID and patternsPatterns that solve a stated extensibility needPatterns forced in for show, or none where needed
CorrectnessCode runs; handles invalid states and edge casesDoes not compile or crashes on the demo
ConcurrencyIdentifies shared state, chooses lock granularityIgnores it or synchronizes everything globally
ReadabilityClear names, small methods, sensible packagesSingle 400-line file with cryptic names
ExtensibilityNew requirement fits with small, local changesNew requirement needs a rewrite

Which Companies Ask HLD, LLD or Machine Coding?

This is based on commonly reported candidate experiences and changes often; teams within the same company differ, so treat it as orientation and confirm the loop with your recruiter.

Company typeCommonly reported design rounds
Flipkart, PhonePe, Swiggy, Razorpay, Meesho, Cred, Zeta and similar Indian product companiesMachine coding round is common, especially for SDE-1/SDE-2; HLD added for SDE-2 and above
AmazonSystem design for SDE-2 and above; object-oriented design questions also appear, particularly at SDE-1/SDE-2
GoogleSystem design for experienced levels; few dedicated LLD rounds, though code quality is judged in coding rounds
MicrosoftDesign round varies by team: can be HLD, LLD or a mix
Uber, Atlassian, and other global product companies in IndiaMix of system design and code/LLD-style design rounds depending on level
Service companies moving candidates to product rolesLighter design questions; OOP fundamentals and some HLD for senior roles

Company-specific breakdowns are in LLD Interview Preparation India and System Design Interview Preparation India 2026.

What to Expect by Experience Level

0–2 years

LLD and OOP heavy

OOP concepts, an LLD problem or machine coding round. Occasionally a light HLD such as a URL shortener. Interviewers look for clean abstractions, not distributed systems depth.

2–6 years

Both, often separately

A machine coding/LLD round and an HLD round are both common for SDE-2. Expected to design one component well and reason about scale at a basic-to-solid level.

6+ years

HLD dominant

One or two HLD rounds with deep trade-off discussion, sometimes a "design a component of your past system" round. LLD may appear as code review or API design.

How to Prepare for Both Rounds

For LLD and machine coding

  1. Master OOP mechanics in one language: interfaces vs abstract classes, enums with behaviour, immutability, collections, basic concurrency (locks, concurrent maps, atomic types).
  2. Learn 8 to 10 patterns by the problem they solve: Strategy, State, Factory, Observer, Builder, Decorator, Singleton, Chain of Responsibility, Command.
  3. Solve 12 to 15 classic problems under a timer, actually running the code: parking lot, elevator, Splitwise, snake and ladder, vending machine, library, chess, LRU cache, rate limiter, cab booking, ATM, in-memory KV store with transactions.
  4. Use a fixed project skeleton: models, services, strategies, exceptions, a Main driver. Saves 10 minutes every round.
  5. Practice the extension: after each problem, add one new requirement and see how much code changes.

For HLD

  1. Learn the building blocks: load balancers, caching, databases and sharding, queues, CDNs, consistency models.
  2. Use a fixed answer framework: requirements → estimates → API → data model → architecture → deep dives → trade-offs.
  3. Do 15 to 20 classic designs out loud, timed: URL shortener, chat, news feed, rate limiter, notification system, ride-hailing, ticket booking, payment system, video streaming.
  4. Read real engineering blogs to see how actual systems made trade-offs.

If you are starting from zero on HLD, follow System Design for Beginners: A Step-by-Step Roadmap.

Mistakes Candidates Make in Each Round

In HLD rounds

  • Writing classes and method bodies instead of components and data flow.
  • Drawing 15 boxes without explaining what flows between them.
  • Never discussing consistency for the one place it matters (seats, payments, inventory).
  • Not driving the conversation: waiting for the interviewer to ask every question.

In LLD and machine coding rounds

  • Talking about microservices, Kafka and sharding when asked for class design.
  • Over-engineering: six patterns and twenty interfaces for a 90-minute problem, then running out of time with nothing working.
  • Under-engineering: one class with if/else chains on type strings.
  • Ignoring concurrency when the problem clearly has shared mutable state (bookings, inventory, elevators).
  • Not running the code before time is up.
💡
A simple self-check during the interviewAsk yourself: "Am I drawing machines or classes?" If the round is HLD and you are drawing classes, zoom out. If it is LLD and you are drawing machines, zoom in. When unsure which round you are in, ask the interviewer in the first minute; it is a perfectly professional question.

Frequently Asked Questions

What is the difference between HLD and LLD?▾
High-level design (HLD) describes a system's architecture: services, data stores, communication, scaling, and trade-offs, typically drawn as boxes and arrows. Low-level design (LLD) describes the internals of one component: classes, interfaces, relationships, design patterns, and often working code. HLD answers how the system scales; LLD answers how the code is structured.
Is LLD the same as machine coding?▾
They overlap but are not identical. An LLD round is often a discussion with class diagrams and partial code. A machine coding round asks you to write a complete, runnable, modular program in 60 to 120 minutes, which is then judged on correctness, structure and extensibility. Machine coding is the stricter, hands-on form of LLD.
Do freshers get HLD questions?▾
Freshers usually face DSA plus object-oriented or LLD-style questions. Full HLD rounds are more common from around two to three years of experience onward, though some companies ask light HLD questions such as designing a URL shortener even for juniors. Check with your recruiter which rounds your loop includes.
Which design patterns are most useful for LLD interviews?▾
Strategy, Factory, Singleton (used carefully), Observer, State, Builder, Decorator and Chain of Responsibility cover most problems. Interviewers care more that a pattern solves a real extensibility need in your design than that you name many patterns.
How long is an HLD or LLD interview?▾
Discussion-style HLD and LLD rounds are typically 45 to 60 minutes. Machine coding rounds commonly run 90 to 120 minutes of coding, sometimes followed by a review discussion. Exact formats vary by company and change over time.
Should I write code in an HLD interview?▾
Usually not beyond API signatures, schemas and perhaps a small algorithm such as a rate-limiting check or ID generation. Spending HLD time on class definitions is a common mistake; focus on components, data flow, scaling and trade-offs.
Which language should I use for LLD rounds?▾
Use the object-oriented language you are fastest and most fluent in. Java is most common in Indian LLD rounds because interfaces, enums and access modifiers make design intent explicit, but C++, Python, Kotlin, Go and TypeScript are generally accepted.

Master HLD and LLD Interviews

Prepflix covers both high-level system design and low-level/machine coding rounds with structured practice problems and mock interviews.

Explore the Prepflix Course →
Pranjal Jain
Pranjal Jain

Ex-Microsoft SDE · IIT Kanpur · Founder of Prepflix. Helps engineers crack startup and product company interviews across India.