Progress · 0/5 phases
🗂️ Phase 3 — Core LLD Problems (Days 36–55)
11 min read · Days 36–55 · Notion
Core insight: Every LLD interview problem is really asking one question: can you identify the changing parts and isolate them from the stable parts? The 6 problems in this phase each have a different ‘axis of change’. Learn to spot it before designing anything.
🧠 Why this phase exists
Patterns are tools. This phase is the workshop where you use them on real problems under structured conditions. Each problem is a canonical LLD question that appears regularly in staff-level interviews at Google, Amazon, Uber, and Flipkart. Solve each one completely — class diagram, interfaces, key code, and design rationale.
💻 LLD problem-solving process (use for every problem)
- Read the requirements. Identify: what are the nouns? (entities/classes) What are the verbs? (methods/operations)
- Identify the primary axis of change: what will change most often as requirements evolve?
- Define interfaces for the unstable parts. Concrete classes for the stable parts.
- Apply relevant SOLID principles. Name the patterns you’re using.
- Draw class diagram first, then sequence diagram for the most complex flow.
- Write code for the critical path — the 3–5 classes that do the most important work.
- State your tradeoffs explicitly: what does your design sacrifice for extensibility?
📚 Problems in order
Day 36–38 — Parking Lot
Requirements clarification questions (always ask first):
- Multiple floors? Multiple entry/exit points?
- Vehicle types: bike, car, truck? Different spot sizes?
- Pricing model: flat rate, hourly, daily?
- Spot reservation in advance?
- Is payment handled inside this system or external?
Entities: ParkingLot, ParkingFloor, ParkingSpot, Vehicle, Ticket, EntrancePanel, ExitPanel, ParkingDisplayBoard, PricingStrategy
Key design decisions:
ParkingSpotis abstract. Subtypes:BikeSpot,CompactSpot,LargeSpot. Each spot knows its size and whether it’s free.Vehicleis abstract. Subtypes:Bike,Car,Truck. Each vehicle knows which spot type it fits.- Spot assignment:
ParkingLot.getAvailableSpot(vehicle)— finds the nearest free spot matching vehicle type. Isolate spot-finding logic behind aSpotFindingStrategyinterface (Strategy pattern) — nearest, first-available, or reserve-specific. - Pricing:
PricingStrategyinterface.HourlyPricing,FlatRatePricing,WeekdayWeekendPricingimplement it. Injected intoTicketat creation time. - Concurrency: two cars entering simultaneously must not be assigned the same spot.
ParkingSpot.assignSpot()must be thread-safe. UsesynchronizedorAtomicBoolean isOccupied. - Display board: Observer pattern.
ParkingDisplayBoardobserves spot state changes and updates available count.
SOLID check:
- S:
ParkingLotdoes not compute pricing. That’sPricingStrategy. - O: new vehicle type = new
Vehiclesubclass. No change toParkingLot. - L:
BikeSpot.assignSpot(truck)should work (return false, not throw). Never throw from a substituted type. - I:
Payableinterface only onTicket, not onVehicle. - D:
ParkingLotdepends onSpotFindingStrategyinterface, not concrete implementation.
Sequence for ‘car enters’: EntrancePanel.entry(vehicle) → ParkingLot.getAvailableSpot(vehicle) → SpotFindingStrategy.find() → ParkingSpot.assignSpot(vehicle) → Ticket.create(spot, vehicle, timestamp) → EntrancePanel.printTicket(ticket)
Day 39–40 — Chess Game
Requirements:
- Two-player game on 8×8 board
- All 6 piece types with correct movement rules
- Check and checkmate detection
- Stalemate detection
- Move history (for undo, or just logging)
Entities: Game, Board, Cell, Piece, Player, Move, MoveValidator
Key design decisions:
Pieceis abstract. Subtypes:King,Queen,Rook,Bishop,Knight,Pawn. Each overridesgetValidMoves(board)returning aList<Cell>.- Move validation axis of change: each piece has unique movement rules. This is the primary axis.
Piece.getValidMoves()is polymorphic — new piece type = new subclass. OCP satisfied. Boardis aCell[8][8]matrix.Cellhas aPiece(nullable).Board.movePiece(from, to)is the core operation.- Check detection:
Game.isKingInCheck(player)— for every opponent piece, check if any valid move lands on the king’s cell. O(pieces × valid_moves). - Move history: Command pattern. Each
Moveis aCommandwithexecute()andundo().MoveHistorystack enables undo. - Special moves: en passant, castling, pawn promotion. Each handled in the respective
Piecesubclass’sgetValidMoves(). Don’t polluteBoard. - State pattern for game status:
GameStateinterface.ActiveState,CheckState,CheckmateState,StalemateState. Game transitions between states after each move.
SOLID check:
- S:
Kingknows how to move.Gameknows when the game ends.Boardknows how to move a piece. Three separate responsibilities. - O: Add
Jokerpiece (chess variant) = newJoker extends Piececlass. Zero changes toBoardorGame. - L: All
Piecesubtypes must honourgetValidMoves()contract — return valid moves, never throw.
Day 41–42 — Elevator System
Requirements clarification:
- Number of elevators? Number of floors?
- Elevator capacity?
- Dispatch algorithm: FCFS, nearest elevator, SCAN (like a hard drive scheduler)?
- Emergency mode? Maintenance mode per elevator?
Entities: ElevatorSystem, Elevator, ElevatorButton, HallButton, Floor, ElevatorDoor, ElevatorPanel, ElevatorController, DispatchStrategy
Key design decisions:
- Dispatch strategy is the primary axis of change.
DispatchStrategyinterface.NearestElevatorStrategy,RoundRobinStrategy,SCANStrategyimplement it.ElevatorControlleris injected with a strategy. - Elevator state machine:
ElevatorStateinterface. States:IdleState,MovingUpState,MovingDownState,DoorOpenState,MaintenanceState. State pattern handles transitions. - Request queuing: each elevator has a
TreeSet<Integer>of pending floor requests, sorted for efficient SCAN dispatch. - Hall buttons:
HallButton(floor, direction)— a call from floor 5 going up. Sent toElevatorControllerwhich dispatches using strategy. - Concurrency: multiple hall button presses and elevator arrivals happen concurrently.
ElevatorControllerrequest queue must be thread-safe (ConcurrentLinkedQueueorsynchronized). - Observer:
ElevatornotifiesElevatorDisplayBoardand door system on state change.
The key insight: the elevator scheduling problem is the same as OS disk scheduling (SCAN/LOOK algorithms). The design decision that most candidates miss: which object owns the dispatch decision? Answer: ElevatorController — not Elevator and not Floor.
Day 43–44 — Vending Machine
Requirements:
- Select item, pay, dispense, give change
- Multiple payment methods (coin, card)
- Handle invalid selection, insufficient funds, out-of-stock
- Admin: restock items, set prices
Entities: VendingMachine, Item, Slot, Inventory, PaymentProcessor, CoinProcessor, CardProcessor, VendingMachineState, Display
Key design decisions:
- State pattern is the core. States:
IdleState(waiting for selection),ItemSelectedState(item chosen, waiting for payment),PaymentReceivedState(payment complete, dispensing),OutOfStockState,MaintenanceState. - Each state implements the full
VendingMachineStateinterface:selectItem(),insertCoin(),insertCard(),dispense(),cancel(). Invalid operations in a state return an error or no-op — never throw (LSP). - Payment strategy:
PaymentProcessorinterface.CoinProcessorandCardProcessorimplement it. Change calculation is only inCoinProcessor. - Inventory:
Map<SlotCode, Item>with stock count.Inventory.isAvailable(code)drives the state machine’s transition decision. - Change dispensing: coin denominations held in the machine. Greedy algorithm to compute change. If exact change impossible — refund and return to idle.
SOLID check:
- O: add
CryptocurrencyProcessor→ new class, no changes toVendingMachine. - S:
VendingMachinedoes not compute change.CoinProcessordoes. - D:
VendingMachinedepends onPaymentProcessorinterface.
Day 45–47 — Library Management System
Requirements:
- Books, members, librarians
- Borrow, return, renew
- Search by title, author, ISBN, category
- Fine calculation for late returns
- Reservations: reserve a book that’s currently checked out
- Notifications when reserved book becomes available
Entities: Library, Book, BookItem (physical copy), Member, Librarian, BookLending, Reservation, FineCalculator, SearchCatalog, NotificationService
Key design decisions:
- Book vs BookItem:
Bookis the logical entity (title, author, ISBN).BookItemis a physical copy with a barcode and aBookStatus(AVAILABLE, CHECKED_OUT, RESERVED, LOST). A book can have multipleBookIteminstances. - Lending flow:
Librarian.checkoutBook(member, bookItemBarcode)— validates member can borrow (limit, active fines), createsBookLendingrecord, updatesBookItem.status = CHECKED_OUT. - Fine calculation:
FineStrategyinterface (DIP).DailyFineStrategy,TieredFineStrategy. Fine =FineStrategy.calculate(daysLate). - Reservation + Observer: when a
BookItemis returned,LibrarynotifiesReservationService.ReservationServicechecks for waiting members.NotificationService.notify(member, book)is called. Observer pattern. - Search:
SearchCataloginterface.TitleSearch,AuthorSearch,ISBNSearch,CategorySearchimplement it. Or: a singleSearchCatalogwith aSearchFilterStrategy that composes predicates. - Member state machine:
ACTIVE,BLACKLISTED(unpaid fines),SUSPENDED. State affectscanBorrow()result.
Day 48–50 — Movie Ticket Booking System (BookMyShow)
Requirements:
- Cities, cinemas, screens, shows, movies
- Browse shows, select seats, book, pay, cancel
- Multiple payment methods
- Seat locking during booking (prevent double booking)
- Cancellation policy and refunds
Entities: BookingSystem, City, Cinema, CinemaHall, Show, Movie, Seat, Booking, Payment, CancellationPolicy
Key design decisions:
- Seat locking (the hardest part): when a user selects seats, lock them for 10 minutes. Two users cannot book the same seat simultaneously.
- Option A: optimistic locking —
Seat.versionfield.UPDATE seat SET status=LOCKED, version=version+1 WHERE id=? AND version=?. Retry on conflict. - Option B: distributed lock — Redis
SET seat:{id} user:{userId} EX 600 NX. Fails if already locked. - In LLD context:
Seat.lock(userId)withsynchronizedorReentrantLock. In production, this moves to the database/cache layer.
- Option A: optimistic locking —
- Seat hierarchy:
Seatis abstract.SilverSeat,GoldSeat,PlatinumSeatwith different pricing. Pricing viaSeatTypeenum orPricingStrategyper seat type. - Booking state machine:
PENDING(seats locked, payment not done) →CONFIRMED(payment done) →CANCELLED→REFUNDED. State pattern. - Cancellation policy:
CancellationPolicyinterface.FullRefundPolicy(>24h before),50PercentRefundPolicy(12–24h before),NoRefundPolicy(<12h before). Strategy pattern. Injected intoBooking. - Search:
ShowFinder.findShows(city, movie, date)returns available shows. Uses repository pattern internally.
SOLID check:
- S:
Bookingdoes not compute refund.CancellationPolicydoes. - O: add a new cancellation policy = new class, zero changes to
Booking. - D:
Bookingdepends onCancellationPolicyinterface,PaymentProcessorinterface. Neither on concrete types.
Day 51–55 — Design review: all 6 systems
- Day 51: revisit Parking Lot. Can you explain every design decision in 5 minutes without notes?
- Day 52: revisit Chess. Can you add a new piece type (Empress = Rook + Knight) with zero changes to existing classes?
- Day 53: revisit Elevator. What happens if a third dispatch strategy is needed? How many files change?
- Day 54: revisit Vending Machine + Library. Name every pattern used in each and justify it.
- Day 55: self-timed mock — 45 minutes. Design a Hotel Management System from scratch. No notes.
⚠️ Common mistakes
Mistake 1
❌ Putting business logic in the entity class.
Booking.calculateRefund() means every time the refund policy changes, you edit Booking. At a company with 10 cancellation policies, Booking becomes a God class.
✅ Correct approach: CancellationPolicy interface injected into Booking. Refund logic lives in the policy. Booking calls policy.calculateRefund(booking). Policy changes never touch Booking.
Mistake 2
❌ Designing without asking about concurrency upfront.
A parking lot with 1,000 simultaneous entries. A booking system with 500 users selecting the same seat. Concurrency is not an afterthought — it changes which data structures and locking mechanisms you choose.
✅ Correct approach: In the requirements phase, ask: what’s the peak concurrency? Which resources are shared? Design shared resources to be thread-safe from the start: AtomicBoolean, ReentrantLock, ConcurrentHashMap.
Mistake 3
❌ Modelling every noun as a class immediately.
Not every noun needs its own class. CinemaHallSeatRow is probably just an attribute of Seat, not a class. Over-modelling creates complexity with no extensibility benefit.
✅ Correct approach: A class earns its existence when: (1) it has its own behaviour (methods), (2) its attributes vary independently from other entities, (3) it’s referred to by multiple other classes. Bare data containers are structs, not classes.
Mistake 4
❌ Designing the happy path only.
The interviewer WILL ask: what happens when the card payment fails mid-booking? What if the elevator is in maintenance mode? What if the reserved book is lost? Happy path design is not a complete design.
✅ Correct approach: For every state machine, explicitly design all error transitions. For every operation, define the failure contract: what state is the system in after a failure? What does the caller receive? This is the signal of production-grade thinking.
🏢 How real companies designed these systems
BookMyShow — Seat locking at scale: Their actual implementation uses Redis distributed locks with TTL. When you click a seat, a Redis SET NX EX 600 command claims it atomically. If another user already claimed it, your request fails with “seat unavailable.” The lock auto-expires if payment doesn’t complete. This is the production version of the Seat.lock() method you implement here.
Google Maps — Elevator scheduling analogy: The elevator dispatch algorithm (SCAN) is the same algorithm used in hard drive scheduling. Google’s routing engine uses similar sweep-line techniques for route computation. The pattern recurs across domains.
Uber — State machines everywhere: Every Uber entity (trip, driver, payment) is a state machine. State transitions are events persisted to Kafka. The state machine logic lives in a single service with explicit state transition tables. The same pattern as the Vending Machine State pattern, at distributed system scale.
📖 Resources
- Grokking the Object Oriented Design Interview (Educative.io) — covers all 6 systems in this phase
- LeetCode Discuss — search “LLD” for community solutions to each problem
- GitHub: tssovi/grokking-the-object-oriented-design-interview (free markdown version)
- Object-Oriented Analysis and Design with Applications — Booch (for deeper theory)
- Draw your class diagrams in draw.io or Excalidraw, not on paper. Build the habit of clean digital diagrams.