issue 117apr 27mmxxvi
est. 2017
Sun, 27 Apr 2026
vol. IX · no. 117
PapersAdda
placement intelligence, since 2017
640+ briefs · 24 campuses · by reservation
verified offers · sourced from r/developersIndia
razorpay₹65.00 LPA· iit-d · sde-1google₹54.00 LPA· iiit-h · swe-imicrosoft₹49.50 LPA· iit-b · sdeatlassian₹38.00 LPA· nit-w · sde-1amazon₹44.20 LPA· bits-p · sde-1uber₹42.00 LPA· iit-kgp · sde-1razorpay₹65.00 LPA· iit-d · sde-1google₹54.00 LPA· iiit-h · swe-imicrosoft₹49.50 LPA· iit-b · sdeatlassian₹38.00 LPA· nit-w · sde-1amazon₹44.20 LPA· bits-p · sde-1uber₹42.00 LPA· iit-kgp · sde-1

Low Level Design (LLD) Interview Questions 2026: Top 20 Designs [Solved]

12 min read
Interview Questions
Updated: 8 Jun 2026
Aditya Sharma
Aditya's Edit

PapersAdda 2026 Placement Cycle

By Aditya Sharma·Founder & Editor, PapersAdda

What changed in 2026 drives

Mass-recruiter offer letters are flatter for 2026 batch - the 4-5 LPA ASE band has barely budged in three years while inflation eats real wages. Premium tracks (Digital, Pro, Elite, Specialist) are still where the differential lives, and they are entirely test-driven. If you are aiming higher than the default offer, the coding round is not optional pageantry - it is the entire interview.

What I'd actually study for this

  • 01Two solid coding-round answers (1 medium-hard DSA each, with edge-case discussion) > five half-baked ones
  • 02One real project you can defend end-to-end - file paths, design decisions, and what you would change
  • 03One DBMS schema you actually built (not a textbook ER diagram), with at least 3 join-heavy queries written from memory
  • 04Three behavioural STAR stories: failure recovered, conflict handled, ownership taken

Where most candidates trip up

The single biggest mistake is treating company-specific guides as primary prep and DSA as secondary. It is the opposite. Mass recruiters use the test as a filter, but premium tracks at every IT services company use coding to allocate offer band. Spend 70% of prep time on DSA + system fundamentals, 20% on company-specific patterns, 10% on HR rehearsal. Reverse that ratio and you collect the default offer.

Editorial commentary by Aditya Sharma · written for PapersAdda · not generated, not aggregated.

Last Updated: June 2026


Why LLD Rounds Are Critical in 2026

Candidates report LLD rounds as the deciding factor in roughly 30-40% of SDE-2 and senior engineer offers at companies like Flipkart, Paytm, Swiggy, Zomato, Razorpay, and Amazon India. Based on public preparation resources and candidate-reported interview threads, LLD rounds have become standard not just at product companies but at late-stage startups.

The LLD round tests whether you can translate requirements into maintainable, extensible code before writing a single line. Interviewers assess your object-oriented thinking, SOLID adherence, and ability to identify the right design patterns.


The LLD Interview Framework

1. Gather Requirements (5 min)
   - Functional: what does the system do?
   - Non-functional: concurrency, scalability needs?
   - Out of scope: what will you NOT design today?

2. Identify Entities (5 min)
   - List nouns from requirements -> candidate classes
   - Identify relationships: has-a, is-a, uses

3. Define Interfaces and Abstract Classes (5 min)
   - What behaviors are common across variants?
   - Where do you need polymorphism?

4. Design Classes (15 min)
   - Attributes, methods, access modifiers
   - Which design pattern fits?

5. Handle Edge Cases and Concurrency (5 min)
   - Thread safety, locks, atomic operations
   - Null checks, empty states

SOLID Principles Quick Reference

PrincipleOne-line ruleCommon violation
Single ResponsibilityOne class, one reason to changeGod class that does everything
Open/ClosedExtend via inheritance, not modificationSwitching on type instead of polymorphism
Liskov SubstitutionSubclass must work wherever parent worksOverride that changes behavior silently
Interface SegregationMany small interfaces over one fat oneInterface with 20 methods, most unimplemented
Dependency InversionDepend on abstractions, not concretionsNew ClassName() deep in business logic

Design 1: Parking Lot

Asked at: Amazon, Flipkart, Paytm, Swiggy, Uber

Requirements:

  • Multiple floors, multiple spots per floor
  • Spot types: Compact, Large, Handicapped, Motorcycle
  • Vehicle types: Car, Truck, Motorcycle
  • Issue/validate tickets, calculate fee on exit
  • Real-time availability tracking
from enum import Enum
from datetime import datetime
import threading

class SpotType(Enum):
    MOTORCYCLE = 1
    COMPACT = 2
    LARGE = 3
    HANDICAPPED = 4

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

class Vehicle:
    def __init__(self, license_plate, vehicle_type):
        self.license_plate = license_plate
        self.vehicle_type = vehicle_type

class ParkingSpot:
    def __init__(self, spot_id, spot_type, floor):
        self.spot_id = spot_id
        self.spot_type = spot_type
        self.floor = floor
        self.is_available = True
        self.vehicle = None

    def park(self, vehicle):
        self.vehicle = vehicle
        self.is_available = False

    def unpark(self):
        self.vehicle = None
        self.is_available = True

class Ticket:
    def __init__(self, ticket_id, vehicle, spot, entry_time):
        self.ticket_id = ticket_id
        self.vehicle = vehicle
        self.spot = spot
        self.entry_time = entry_time
        self.exit_time = None

class PricingStrategy:
    def calculate_fee(self, duration_hours, vehicle_type):
        raise NotImplementedError

class FlatRatePricing(PricingStrategy):
    def calculate_fee(self, duration_hours, vehicle_type):
        rates = {VehicleType.MOTORCYCLE: 20, VehicleType.CAR: 40, VehicleType.TRUCK: 80}
        return rates[vehicle_type] * max(1, int(duration_hours))

class ParkingLot:
    """
    Strategy pattern for pricing. Thread-safe entry/exit.
    """
    def __init__(self, pricing_strategy=None):
        self.floors = []
        self.tickets = {}
        self.lock = threading.Lock()
        self.pricing = pricing_strategy or FlatRatePricing()
        self._ticket_counter = 0

    def add_floor(self, floor):
        self.floors.append(floor)

    def _find_spot(self, vehicle_type):
        # Vehicle -> compatible spot types
        compatible = {
            VehicleType.MOTORCYCLE: [SpotType.MOTORCYCLE, SpotType.COMPACT],
            VehicleType.CAR: [SpotType.COMPACT, SpotType.LARGE],
            VehicleType.TRUCK: [SpotType.LARGE]
        }
        valid_types = compatible[vehicle_type]

        for floor in self.floors:
            for spot in floor:
                if spot.is_available and spot.spot_type in valid_types:
                    return spot
        return None

    def park_vehicle(self, vehicle):
        with self.lock:
            spot = self._find_spot(vehicle.vehicle_type)
            if not spot:
                return None  # lot is full

            spot.park(vehicle)
            self._ticket_counter += 1
            ticket = Ticket(self._ticket_counter, vehicle, spot, datetime.now())
            self.tickets[self._ticket_counter] = ticket
            return ticket

    def exit_vehicle(self, ticket_id):
        with self.lock:
            ticket = self.tickets.get(ticket_id)
            if not ticket:
                return None

            ticket.exit_time = datetime.now()
            duration = (ticket.exit_time - ticket.entry_time).total_seconds() / 3600
            fee = self.pricing.calculate_fee(duration, ticket.vehicle.vehicle_type)
            ticket.spot.unpark()
            return fee

Design patterns used: Strategy (pricing), Factory (implicit in vehicle creation), Iterator (searching spots)


Design 2: Library Management System

Asked at: TCS Digital, Infosys Power Programmer, Amazon India

Requirements: Books, members, borrowing/returning, fines, reservations

from datetime import date, timedelta
from collections import defaultdict

class BookStatus(Enum):
    AVAILABLE = 1
    BORROWED = 2
    RESERVED = 3

class Book:
    def __init__(self, isbn, title, author):
        self.isbn = isbn
        self.title = title
        self.author = author
        self.status = BookStatus.AVAILABLE

class Member:
    MAX_BOOKS = 5
    FINE_PER_DAY = 5

    def __init__(self, member_id, name):
        self.member_id = member_id
        self.name = name
        self.borrowed_books = {}  # isbn -> borrow_date
        self.fines = 0

    def can_borrow(self):
        return len(self.borrowed_books) < self.MAX_BOOKS

    def calculate_fine(self, isbn, due_days=14):
        if isbn not in self.borrowed_books:
            return 0
        borrow_date = self.borrowed_books[isbn]
        due_date = borrow_date + timedelta(days=due_days)
        today = date.today()
        if today > due_date:
            return (today - due_date).days * self.FINE_PER_DAY
        return 0

class LibraryCatalog:
    def __init__(self):
        self.books = {}  # isbn -> Book
        self.search_index = defaultdict(list)  # title/author -> [isbn]

    def add_book(self, book):
        self.books[book.isbn] = book
        for word in (book.title + ' ' + book.author).lower().split():
            self.search_index[word].append(book.isbn)

    def search(self, query):
        results = set()
        for word in query.lower().split():
            results.update(self.search_index.get(word, []))
        return [self.books[isbn] for isbn in results if isbn in self.books]

Design 3: ATM System

Asked at: Amazon, Wipro, Infosys

Requirements: Card insertion, PIN verification, balance check, withdraw, deposit

class ATMState(Enum):
    IDLE = 1
    CARD_INSERTED = 2
    PIN_VERIFIED = 3
    TRANSACTION = 4

class ATM:
    """
    State pattern: ATM behavior changes based on its state.
    """
    def __init__(self, cash_available):
        self.state = ATMState.IDLE
        self.cash = cash_available
        self.current_card = None
        self.current_account = None

    def insert_card(self, card):
        if self.state != ATMState.IDLE:
            raise Exception("ATM is busy")
        self.current_card = card
        self.state = ATMState.CARD_INSERTED
        return "Card inserted. Enter PIN."

    def enter_pin(self, pin, account_service):
        if self.state != ATMState.CARD_INSERTED:
            raise Exception("Insert card first")
        account = account_service.authenticate(self.current_card, pin)
        if account:
            self.current_account = account
            self.state = ATMState.PIN_VERIFIED
            return f"Authenticated. Balance: {account.balance}"
        else:
            return "Wrong PIN"

    def withdraw(self, amount):
        if self.state != ATMState.PIN_VERIFIED:
            raise Exception("Not authenticated")
        if amount > self.cash:
            return "ATM has insufficient cash"
        if amount > self.current_account.balance:
            return "Insufficient account balance"

        self.current_account.balance -= amount
        self.cash -= amount
        return f"Dispensed {amount}. Remaining balance: {self.current_account.balance}"

    def eject_card(self):
        self.state = ATMState.IDLE
        self.current_card = None
        self.current_account = None
        return "Card ejected"

Design patterns used: State, Service Layer, Repository (implied in account_service)


Design 4: Elevator System

Asked at: Google, Amazon, Goldman Sachs

Requirements: Multiple elevators, multiple floors, direction tracking, optimal dispatch

class Direction(Enum):
    UP = 1
    DOWN = 2
    IDLE = 3

class Elevator:
    def __init__(self, elevator_id, total_floors):
        self.id = elevator_id
        self.current_floor = 0
        self.direction = Direction.IDLE
        self.requests = set()

    def add_request(self, floor):
        self.requests.add(floor)

    def step(self):
        """Move one floor in current direction."""
        if not self.requests:
            self.direction = Direction.IDLE
            return

        target = min(self.requests) if self.direction == Direction.UP else max(self.requests)

        if self.current_floor < target:
            self.current_floor += 1
            self.direction = Direction.UP
        elif self.current_floor > target:
            self.current_floor -= 1
            self.direction = Direction.DOWN

        if self.current_floor in self.requests:
            self.requests.remove(self.current_floor)

    def cost_to_serve(self, floor):
        """Estimate cost (floors to travel) to serve this request."""
        return abs(self.current_floor - floor)

class ElevatorController:
    """
    Dispatch strategy: assign to elevator with minimum cost.
    """
    def __init__(self, num_elevators, total_floors):
        self.elevators = [Elevator(i, total_floors) for i in range(num_elevators)]

    def request_elevator(self, floor):
        best = min(self.elevators, key=lambda e: e.cost_to_serve(floor))
        best.add_request(floor)
        return best.id

Design 5: Vending Machine

Asked at: Amazon, Walmart, PhonePe

Requirements: Select product, insert coins, dispense product and change, handle out-of-stock. This is the canonical State pattern problem: the machine behaves differently depending on whether it is idle, has money inserted, or is dispensing.

class VendingMachine:
    """
    State pattern. The machine delegates behavior to its current state.
    Each state knows the valid transitions out of itself.
    """
    def __init__(self, inventory):
        self.inventory = inventory          # product_code -> (count, price)
        self.balance = 0
        self.selected = None
        self.state = IdleState(self)

    def select_product(self, code):
        return self.state.select_product(code)

    def insert_coin(self, amount):
        return self.state.insert_coin(amount)

    def dispense(self):
        return self.state.dispense()


class State:
    def __init__(self, machine):
        self.m = machine
    def select_product(self, code): raise Exception("Invalid action")
    def insert_coin(self, amount): raise Exception("Invalid action")
    def dispense(self): raise Exception("Invalid action")


class IdleState(State):
    def select_product(self, code):
        count, price = self.m.inventory.get(code, (0, 0))
        if count <= 0:
            return "Out of stock"
        self.m.selected = code
        self.m.state = HasSelectionState(self.m)
        return f"Selected {code}. Price {price}. Insert coins."


class HasSelectionState(State):
    def insert_coin(self, amount):
        self.m.balance += amount
        _, price = self.m.inventory[self.m.selected]
        if self.m.balance >= price:
            self.m.state = DispenseState(self.m)
            return "Payment complete. Dispensing."
        return f"Inserted. Need {price - self.m.balance} more."


class DispenseState(State):
    def dispense(self):
        code = self.m.selected
        count, price = self.m.inventory[code]
        self.m.inventory[code] = (count - 1, price)
        change = self.m.balance - price
        self.m.balance = 0
        self.m.selected = None
        self.m.state = IdleState(self.m)
        return f"Dispensed {code}. Change: {change}"

The strength of the State pattern here is that illegal actions (inserting a coin before selecting, dispensing before paying) are rejected by the base State's default methods rather than scattered if-checks. Adding a "maintenance" state later is a new class, not edits across the machine.

Design patterns used: State (machine lifecycle), Strategy (could plug in different change-making algorithms)


Design 6: Logging Framework

Asked at: Microsoft, Atlassian, Uber

Requirements: Multiple log levels, multiple output sinks (console, file, remote), filtering by level. This is the canonical Chain of Responsibility plus Strategy problem.

class LogLevel(Enum):
    DEBUG = 1
    INFO = 2
    WARN = 3
    ERROR = 4


class LogSink(ABC):
    @abstractmethod
    def write(self, level, message): pass

class ConsoleSink(LogSink):
    def write(self, level, message):
        print(f"[{level.name}] {message}")

class FileSink(LogSink):
    def __init__(self, path):
        self.path = path
    def write(self, level, message):
        with open(self.path, "a") as f:
            f.write(f"[{level.name}] {message}\n")


class Logger:
    """
    Strategy for sinks; threshold filtering before dispatch.
    Multiple sinks receive each qualifying message (fan-out).
    """
    def __init__(self, min_level=LogLevel.INFO):
        self.min_level = min_level
        self.sinks = []

    def add_sink(self, sink):
        self.sinks.append(sink)

    def log(self, level, message):
        if level.value < self.min_level.value:
            return                       # filtered out
        for sink in self.sinks:
            sink.write(level, message)

    def info(self, msg): self.log(LogLevel.INFO, msg)
    def error(self, msg): self.log(LogLevel.ERROR, msg)

The design separates three concerns cleanly: the level threshold (filtering), the sinks (where output goes), and the Logger (orchestration). Adding a remote sink (HTTP, Kafka) is a new LogSink subclass with zero changes to Logger. Asynchronous logging is added by wrapping a sink in a queue-backed AsyncSink decorator, which is the Decorator pattern layered on top.

Design patterns used: Strategy (sinks), Chain of Responsibility (level filtering), Decorator (async wrapper), Singleton (often one global logger)


Concurrency in LLD: The Part Candidates Skip

Most LLD candidates write single-threaded code and lose points when the interviewer asks "what if two threads call this at once." The three concurrency tools you should reach for, in order of preference:

First, prefer immutability. If an object never changes after construction (a Ticket, a Booking record), it needs no locking because there is no shared mutable state to corrupt. Design for immutable value objects wherever possible.

Second, use fine-grained locks, not one global lock. In the parking lot, lock per floor, not per lot, so concurrent entries on different floors do not serialize. A global lock turns your whole system into a single-threaded bottleneck under load.

Third, prefer atomic operations over locks for simple counters. A ticket-id counter should use an atomic increment (AtomicInteger in Java, a database sequence, or Redis INCR) rather than a lock around a plain integer. Atomics are lock-free and faster under contention.

The critical-section rule to state aloud: find-and-reserve must be a single atomic operation. The bug is checking availability, then assigning in a separate step, because two threads can both pass the check before either assigns. Combine the check and the mutation under one lock or one atomic compare-and-set.


Top 10 LLD Problems You Must Know

ProblemKey patternsKey classes
Parking LotStrategy (pricing), Observer (availability)Vehicle, Spot, Floor, Ticket, ParkingLot
Library ManagementRepository, IteratorBook, Member, Catalog, Loan, Fine
ATM SystemState, Service LayerATM, Card, Account, Transaction
Elevator SystemStrategy (dispatch)Elevator, Controller, Request
Chess GameStrategy (piece moves)Board, Piece, Player, Move
Hotel BookingRepository, ObserverRoom, Booking, Guest, RoomType
Ride Share (Uber/Lyft)Strategy, ObserverDriver, Rider, Trip, PricingEngine
Movie BookingRepository, FacadeMovie, Show, Seat, Booking
Food DeliveryObserver, Chain of ResponsibilityOrder, Restaurant, Delivery, Payment
Social Media FeedObserver, FactoryPost, User, Feed, NotificationService

Design Pattern Cheat Sheet for LLD

PatternUse whenExample in LLD
SingletonOnly one instance neededParkingLot, DatabaseConnection
FactoryHide object creation logicVehicleFactory, PaymentFactory
StrategyMultiple interchangeable algorithmsPricingStrategy, DispatchStrategy
ObserverNotify on state changeAvailability alerts, order status
StateBehavior depends on current stateATM, Order lifecycle
CommandEncapsulate requests as objectsElevatorRequest, Transaction
DecoratorAdd responsibilities dynamicallyLoggingWrapper, CachingWrapper

What Interviewers Want to See

Candidates report that interviewers stop the coding early and ask "why did you choose this pattern?" more often than they let candidates fully implement the system. The signal is not whether you write complete working code, but whether you can articulate design decisions. Based on public preparation resources and candidate-reported LLD interviews, the most valued answers explain what would change if a requirement changed, and which parts of the design absorb that change without breaking others.

A weak answer: "I used a class hierarchy because that is what OOP teaches."

A strong answer: "I made PricingStrategy an interface so that we can plug in hourly, flat-rate, or surge pricing without touching the ParkingLot class. Adding a new pricing model is a one-file change."


Methodology applied to this articlelast verified 8 Jun 2026
Sources used
Public exam-pattern documents, official recruiter pages, and verified candidate reports on r/developersIndia and LinkedIn.
Verification window
Page last edited 8 Jun 2026 by Aditya Sharma. Numbers and patterns sanity-checked against the most recent 2026 cycle drives we tracked.
What we did NOT do
  • No fabricated salary numbers or success rates. If we quote a range, it's sourced.
  • No noun-substituted templates. This article was not generated by swapping company names in a stock prompt.
  • No paid placements, sponsored coaching links, or affiliate-shilled course pushes.
Verification policy: /editorial-standards/. Found something incorrect? Submit a correction - we respond within 48 hours.

Explore this topic cluster

More resources in Interview Questions

Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.

Paid contributor programme

Sat this this year? Share your story, earn ₹500.

First-person experience reports help future candidates prep smarter. We pay verified contributors ₹500 via UPI per accepted story - with byline.

Submit your story →

Ready to practice?

Take a free timed mock test

Put what you learned into practice. Our mock tests match the 2026 pattern with timer, navigator, reveal, and score breakdown. No signup.

Start Free Mock Test →

Related Articles

More from PapersAdda

Share this guide: