2Brute2Best
πŸ”₯0
LLD Problem-Solving Playbook

How to approach any new LLD problem from scratch.

In DSA you go Brute β†’ Better β†’ Optimal by removing repeated work. In LLD you go Vague prompt β†’ Naive MVP β†’ Pain point β†’ Clean design by finding what changes and hiding it behind the right boundary.

01

The 5-step thinking process (use this in every interview)

01

Clarify & scope the 45-minute target

Minutes 0–5

Interview prompts are vague on purpose ("Design BookMyShow", "Design an ATM"). Never jump straight into classes. Ask a few focused questions to fix the boundary:

  • β€’

    Core happy path: which 2–3 methods will the caller use 90% of the time?

  • β€’

    Failure & edge cases: invalid transitions, not enough stock, partial failure?

  • β€’

    Concurrency: single-threaded in memory, or do threads race for the same thing (seat locking, rate limiting)?

  • β€’

    Out of scope: you design in-memory classes and interfaces β€” not SQL schemas, REST controllers, or UI.

02

Extract the nouns β€” and reject the false classes

Minutes 5–12

Underline every noun in the requirements and put each one in a bucket. Beginners fail by turning every noun into a heavy class:

  • β€’

    Value struct: plain data with no rules (Ticket, Money, Coordinates, LogMessage).

  • β€’

    Stateful class / manager: owns invariants and transitions (Order, SeatLockManager, AtmMachine).

  • β€’

    Interface: anything with more than one implementation (PricingStrategy, PaymentGateway, EvictionPolicy).

  • β€’

    Not a class: trap nouns like Price, Timestamp, Status become fields, enum class values or return types.

03

Sketch the naive MVP and ask "what breaks tomorrow?"

Minutes 12–18

Picture the simplest single-class version first, then stress-test it against change. A pattern is only ever the cure for a concrete smell:

  • β€’

    Long if/else on which algorithm? Every new rule edits the core class (breaks Open-Closed).

  • β€’

    Boolean flags (isPaid_, isCancelled_)? Illegal combinations compile and cause bugs.

  • β€’

    One method calls 4 subsystems directly? Impossible to test without real gateways and databases.

04

Map only the real hotspots to patterns

Minutes 18–25

Add an interface or pattern only where something genuinely varies. In most interviews 2–3 patterns working together is the sweet spot:

  • β€’

    Pluggable rule or calculation β†’ Strategy (A1) injected through the constructor.

  • β€’

    Strict lifecycle with guarded transitions β†’ State (A4).

  • β€’

    One event, many independent reactions β†’ Observer (A3).

  • β€’

    Multi-step workflow β†’ Facade (A9), or Command (B4) when you need undo / rollback.

05

Write modern C++23 β€” ownership, invariants, locks

Minutes 25–45

Turn the UML boxes into clean C++23 that compiles and proves its invariants in `main()`:

  • β€’

    Ownership (F5): std::unique_ptr<Interface> for owned polymorphic parts, value members by default, references for borrowing. No raw new / delete.

  • β€’

    Errors (F7): return std::expected<T, Error> or std::optional<T> for normal rejections, not -1 or exceptions.

  • β€’

    Concurrency: guard shared state with std::mutex + std::scoped_lock, and never hold a lock across slow I/O such as a payment call.

02

Pain point β†’ design pattern decision tree

Don't memorise patterns one by one. Start on the left with the pain hurting your naive code, follow the branch that matches, and open the chapter that fixes it.

What is hurting in your naive design?
Growing if/else or switch inside a method
Branching on which pricing / routing / scoring algorithm to useA1 Β· Strategy β†’
Branching on the object's own lifecycle status (Placed β†’ Paid β†’ Shipped)A4 Β· State β†’
Branching on a string/enum only to pick which class to createA5 Β· Factory Method β†’
A sequence of checks where any step can reject or pass the request onB3 Β· Chain of Responsibility β†’
Classes are tightly coupled β€” one edit forces 5 others
One event must trigger many independent side-effectsA3 Β· Observer β†’
Many components talk to each other directly (NΓ—N mesh)B6 Β· Mediator β†’
Business code directly creates a concrete SQL / HTTP clientA6 Β· Dependency Inversion Principle (DIP) β†’
A third-party SDK has the wrong method signature or typesA8 Β· Adapter β†’
Callers must call 4–6 subsystems in a fragile orderA9 Β· Facade β†’
Object creation is messy or wastes memory
Constructor has 6+ optional parameters or cross-field rulesB1 Β· Builder β†’
Need matching families of objects that must never be mixedB5 Β· Abstract Factory β†’
Need to copy a configured object through a base-class pointerC4 Β· Prototype β†’
Thousands of objects duplicate the same heavy read-only dataC5 Β· Flyweight β†’
Exactly one process-wide config / registry is neededB2 Β· Singleton β†’
Need stackable features, trees, or undo / redo
Optional add-ons stack in any order without 2^N subclassesC1 Β· Decorator β†’
A group and a single item must be treated the same way (folders/files)C2 Β· Composite β†’
Need an audit log, a queue, or undo via inverse actionsB4 Β· Command β†’
Need to save and restore private state without exposing fieldsC3 Β· Memento β†’
Need access control, custom traversal, or new operations
Need caching, lazy loading, rate limiting or auth in front of a serviceB7 Β· Proxy β†’
Fixed algorithm skeleton, but 1–2 steps vary per subclassB8 Β· Template Method β†’
Walk a custom collection without exposing its containersB9 Β· Iterator β†’
Two dimensions vary independently (Entity Γ— Renderer)C6 Β· Bridge β†’
Fixed set of types, but new operations are added all the timeC7 Β· Visitor β†’
Evaluate user-written rule strings against some contextC8 Β· Interpreter β†’