Send a link to your students to track their progress
60 Terms
1
New cards
"What is a low-level design (LLD) interview?"
"Design classes, interfaces, and relationships for a self-contained system. Example problems: parking lot (assign spots to cars), chess game (model pieces and moves), elevator controller (schedule floors). Focus: right objects, how they interact, what methods they expose, can it extend without rewriting. Also called Object-Oriented Design (OOD)"
2
New cards
"What is the difference between LLD and system design?"
"System design = architecture at scale (services, databases, queues, traffic) — no code. LLD = code structure for a single feature (classes, methods, state, relationships). System design draws boxes and arrows for a ride-sharing backend. LLD writes the Trip class, TripState enum, PricingCalculator interface for that same system"
3
New cards
"What are the 5 steps of the LLD delivery framework?"
"1) Requirements (~5 min), 2) Entities and Relationships (~3 min), 3) Class Design (~10-15 min), 4) Implementation (~10 min), 5) Extensibility (~5 min if time allows)"
4
New cards
"What 4 themes should you explore during LLD requirements?"
"1) Primary capabilities (what operations must it support? e.g. can cars enter/exit? can tickets be issued?), 2) Rules and completion (what defines winning, failure, or state transitions? e.g. how does Tic Tac Toe end?), 3) Error handling (invalid move on occupied cell?), 4) Scope boundaries (no UI, no networking, no AI opponent)"
5
New cards
"How do you identify entities in LLD?"
"Scan requirements for meaningful nouns and apply a filter. If it maintains changing state or enforces rules → its own entity. If it's just data attached to something else → a field on another class. For Tic Tac Toe: Game, Board, Player are entities. Cell mark ('X'/'O') is just a field on Cell, not its own class"
6
New cards
"How do you derive class state from requirements?"
"Map each requirement to what the class must remember. For Game in Tic Tac Toe: 'two players alternate' → track currentPlayer. 'game ends when someone wins' → track state (IN_PROGRESS/WON/DRAW) and winner. Only add fields that requirements justify"
7
New cards
"How do you derive class behavior from requirements?"
"For each class, what operations does the outside world need? For Game: players make moves → makeMove(player, row, col). Check whose turn it is → getCurrentPlayer(). Inspect outcome → getGameState(), getWinner(). Each method maps to a real action or question in the requirements"
8
New cards
"What is the 'Tell, Don't Ask' principle?"
"Objects manage their own state and expose behavior — don't expose getters so callers make decisions. Bad: get seat.isAvailable() then decide. Good: seat.reserve(visitor) — Seat enforces the rule itself. Workflow rules in the orchestrator (Game), data rules in the entity that owns the data (Board checks if cell is occupied)"
9
New cards
"What should you do during LLD implementation?"
"Start with the happy path: makeMove() in Tic Tac Toe — valid player, valid cell, update board, check win. Then enumerate edge cases: wrong player's turn, cell already occupied, game already over. Use pseudocode unless interviewer requests real code. Focus on the most interesting methods, not every getter"
10
New cards
"What is verification in LLD?"
"After implementing, trace through a concrete scenario. For Tic Tac Toe: 'Initial: board empty, currentPlayer=X. makeMove(X,0,0) → board[0][0]=X, currentPlayer=O. makeMove(O,1,1) → board[1][1]=O...' Catches bugs like forgotten turn-switch or wrong win detection before the interviewer finds them"
11
New cards
"What is the LLD extensibility step?"
"Interviewer proposes a change: 'add undo' or 'support NxN boards'. Point to clean boundaries. For undo: 'All state mutations flow through makeMove() — I'd add a command history stack. Undo() pops the stack and reverts state. Nothing else changes.' Stay high-level, don't rewrite code"
12
New cards
"What is KISS?"
"Keep It Simple, Stupid — the simplest solution that works is right. Most common LLD violation: adding a StrategyFactory + AbstractBuilder when a simple class would do. If a single if/else handles two payment types cleanly, don't introduce a pattern. Add complexity only when simplicity stops working"
13
New cards
"What is DRY?"
"Don't Repeat Yourself — pull duplicated logic into one place. If User, Admin, and Guest all validate emails identically, create a shared EmailValidator. But don't force it: two pieces of code that look similar but serve different purposes can stay separate. Question: is the logic conceptually the same, not just textually similar?"
14
New cards
"What is YAGNI?"
"You Aren't Gonna Need It — build for today's requirements. If 'Design a parking lot' doesn't mention EVs, don't add charging station support. Design with extension in mind but only implement what's needed now. When interviewer asks 'how would you extend this?' — talk about it, but don't code it upfront"
15
New cards
"What is Separation of Concerns?"
"Different parts of code handle different responsibilities. Bad: TicTacToe.play() contains display logic (print board), input handling (read row/col), and win-checking — changing display breaks game logic. Good: Board, Display, InputHandler as separate classes. Changing console to GUI only touches Display"
16
New cards
"What is the Law of Demeter?"
"Only talk to your immediate friends, don't chain through objects. Bad: order.getCustomer().getAddress().getZipCode() — your code knows the structure of 3 objects, breaks if any changes. Good: order.getShippingZipCode() — Order handles the navigation internally. Reduces coupling"
17
New cards
"What is SRP?"
"Single Responsibility Principle — one reason to change. Bad: Report class handles content generation, PDF formatting, and file storage — changing PDF library forces touching the Report class. Good: Report (content), PDFPrinter (formatting), FileStorage (I/O). When PDF library changes, only touch PDFPrinter"
18
New cards
"What is OCP?"
"Open/Closed Principle — open for extension, closed for modification. Bad: PaymentProcessor.process() has if/elif for credit/paypal/crypto — adding crypto means modifying existing code and risking regressions. Good: PaymentMethod interface — adding CryptoPayment means a new class, existing PaymentProcessor never changes"
19
New cards
"What is LSP?"
"Liskov Substitution Principle — subclasses must work wherever the base class works. Bad: Penguin extends Bird but throws on fly() — code calling bird.fly() breaks with Penguin. Fix: Bird has eat(). FlyingBird extends Bird and adds fly(). Penguin extends Bird only. No code needs isinstance checks"
20
New cards
"What is ISP?"
"Interface Segregation Principle — small focused interfaces. Bad: Worker interface with work(), eat(), sleep() — Robot must implement eat() and sleep() as empty stubs it will never use. Good: Workable, Feedable, Restable as separate interfaces. Robot only implements Workable"
21
New cards
"What is DIP?"
"Dependency Inversion Principle — depend on abstractions. Bad: NotificationService creates EmailSender directly in its constructor — can't test without sending real emails, can't swap to SMS. Good: accept MessageSender interface through constructor. Pass EmailSender in production, MockSender in tests, SMSSender for a new channel"
22
New cards
"What is encapsulation?"
"Keep data private, control access through methods that enforce rules. Bad: parkingLot.spots is public — anyone can modify the list directly. Good: parkingLot.parkVehicle(v) checks availability and assigns a spot, enforcing all business rules. Return copies of collections, never references to internal lists"
23
New cards
"What is abstraction?"
"Expose what something can do, hide how it does it. Bad: OrderService directly calls stripe.setApiKey() and stripe.createCharge() — tightly coupled to Stripe. Good: PaymentMethod interface with process(amount). OrderService calls payment.process(amount) and doesn't know if it's Stripe, PayPal, or a test mock"
24
New cards
"What is polymorphism?"
"Different objects respond to the same method in their own way, eliminating type-checking. Bad: ParkingLot.park() has if vehicle.type == 'car' / 'motorcycle' / 'truck' — add a new type and you touch existing code. Good: each Vehicle subclass implements getRequiredSpotSize() — ParkingLot never changes when you add Truck"
25
New cards
"When should you use inheritance vs composition?"
"Inheritance: subclasses genuinely ARE the parent and share stable identical implementation. SavingsAccount and CheckingAccount both ARE BankAccounts — share deposit()/withdraw(). Composition: behavior varies. ElectricCar doesn't share engine logic with Car — inject a Drivetrain interface instead. Default to composition"
26
New cards
"What is the Factory pattern?"
"Hides construction logic so callers don't care which concrete class they get. Bad: new EmailNotification() / new SMSNotification() scattered across the codebase. Good: NotificationFactory.create('email') in one place. Adding PushNotification = update factory only, nothing else changes. Signal: 'support multiple X types' in requirements"
27
New cards
"What is the Builder pattern?"
"Creates complex objects step-by-step with optional fields. HttpRequest.Builder().url('...').method('POST').header('Content-Type','application/json').body('{}').build() — readable, handles optional fields, validates in build(). Use for objects with 4+ optional parameters. Rarely needed for simple domain objects in most LLD problems"
28
New cards
"What is the Singleton pattern?"
"Ensures one instance exists. Usually better to pass shared objects through constructors — Singletons hide dependencies and make testing hard (can't swap with a mock). Use only when you truly need one global instance across the whole system. Default answer: probably don't use it"
29
New cards
"What is the Decorator pattern?"
"Adds behavior at runtime by wrapping objects. FileDataSource → EncryptionDecorator(source) → CompressionDecorator(source). write('data') compresses, then encrypts, then writes to file. Stack any combination without subclass explosion. Use when features are optional and combinable at runtime. Signal: 'add optional features' or 'layer behaviors'"
30
New cards
"What is the Facade pattern?"
"A coordinator that hides complexity behind a clean interface. Game in Tic Tac Toe is a facade — caller just calls game.makeMove(row, col) without knowing about Board, Player, win-checking logic. You build these naturally as orchestrators. Name it if useful to communicate; don't force the pattern"
31
New cards
"What is the Strategy pattern?"
"Replaces if/else chains with polymorphism. Bad: ShoppingCart.checkout() has if paymentType == 'credit' / 'paypal' / 'crypto'. Good: PaymentStrategy interface with pay(amount). Cart holds a strategy and calls strategy.pay(amount). Each implementation handles itself. Adding new payment = new class, cart never changes. Most common LLD interview pattern"
32
New cards
"What is the Observer pattern?"
"Objects subscribe to events and get notified automatically. Stock.setPrice(155) calls update(symbol, price) on every subscribed observer — PriceDisplay updates its screen, PriceAlert fires if price exceeded threshold. They don't know about each other. Signal: 'notify multiple components when X changes', 'update displays in real-time'"
33
New cards
"What is the State Machine pattern?"
"Encapsulates each state's behavior in its own class. VendingMachine.insertCoin() delegates to currentState.insertCoin(machine). NoCoinState prints 'Coin inserted' and transitions to HasCoinState. HasCoinState prints 'Coin already inserted'. No giant if/switch on state. Draw a state diagram (circles = states, arrows = transitions labeled with actions). Signal: word 'state' appears repeatedly"
34
New cards
"What is the most common mistake in LLD interviews?"
"Over-engineering — forcing Factory, Builder, Decorator, Singleton when a simple class would do. Interviewers notice and penalize it. Start with the simplest solution. Add patterns only when the problem naturally calls for them. If you're using 3+ patterns, you're probably forcing it"
35
New cards
"What is concurrency in LLD context?"
"Multiple threads sharing memory within one process. count++ looks atomic but is actually read-then-add-then-write — three steps that can interleave. Two threads both read count=5, both write count=6. You've lost an increment. LLD concurrency = threads + shared memory in one program. Not the same as system design concurrency across servers"
36
New cards
"What are the 3 categories of concurrency problems?"
"1) Correctness — shared state corrupted (two threads both book seat 7A), 2) Coordination — threads hand off work (API handler produces tasks, worker thread consumes them), 3) Scarcity — limited resources with many requesters (10 DB connections, 100 concurrent requests)"
37
New cards
"What is a correctness concurrency problem?"
"Shared state gets silently wrong when multiple threads read and write without coordination. Two threads both read 'seat 7A available', both book it, Alice gets a confirmation but finds Bob in her seat. A counter reads 847 when it should be 1000. The danger is wrong results, not crashes"
38
New cards
"What is the check-then-act bug?"
"Check a condition, then act — but another thread changes the condition in between. Alice checks 'is 7A available?' → yes. Bob checks → yes. Alice books it. Bob books it (overwriting Alice). Fix: hold the same lock during BOTH the check AND the action. 'I'll use a lock so the check and booking happen atomically — no thread can sneak in between'"
39
New cards
"What is the read-modify-write bug?"
"Read a value, compute from it, write back — but another thread reads the same stale value before you write. Thread A reads count=5, Thread B reads count=5, A writes 6, B writes 6. Two increments happened but count only went up by 1. Fix: wrap the entire read+compute+write in a lock"
40
New cards
"What is coarse-grained locking?"
"One lock guards all related shared state. class TicketBooking: def book_seat(self, seat_id, visitor_id): with self._lock: if seat_id in self._owners: return False; self._owners[seat_id] = visitor_id; return True. Simple, correct, default choice. Biggest mistake: releasing lock between check and update — brings the race condition right back"
41
New cards
"What is fine-grained locking?"
"One lock per resource instead of one for everything. Per-seat locks: Alice booking 7A and Bob booking 12B proceed in parallel — they only block each other when competing for the same seat. Better throughput under high machine-generated load. Risk: deadlock. Fix: always acquire multiple locks in consistent global order (smaller seat ID first)"
42
New cards
"What is a read-write lock?"
"Multiple readers can hold the lock simultaneously — reads don't corrupt each other. Writer waits for all readers to finish then gets exclusive access. Use when reads vastly outnumber writes — a config store read on every request but changed once a day. If reads and writes are ~50/50, a simple mutex is usually faster"
43
New cards
"What are atomic variables and their limit?"
"CPU compare-and-swap: 'set to new value ONLY if it currently equals expected value.' Safe for single variables without locks — use for hit counters, flags, statistics. The limit: the moment two variables must stay in sync (seat_available AND seat_owner), atomics can't help. You need a lock"
44
New cards
"What is thread confinement?"
"Partition data so each thread owns its slice — no sharing, no locks. Thread 1 handles seats A-M, Thread 2 handles N-Z. Each has its own private map. No race conditions possible for non-overlapping data. Mention when interviewer pushes hard on scalability: 'if lock contention becomes the bottleneck, we could partition data and dedicate a thread to each partition'"
45
New cards
"What are the correctness tool choices?"
"Single variable update → atomic (hit counter). Multiple fields must stay in sync → coarse-grained lock (book seat + create ticket). Operations on independent resources that don't interfere → fine-grained lock (per-seat locks). Can partition data by thread → thread confinement. Default: coarse-grained lock"
46
New cards
"What is a coordination concurrency problem?"
"Threads need to communicate and hand off work. API handler gets a request and needs background work done (send welcome email, resize photo, generate report). Naive: handler does it inline — slow response. Better: handler enqueues work, worker thread picks it up. Need efficient waiting, backpressure, thread-safe handoff"
47
New cards
"What do condition variables solve?"
"Efficient waiting without burning CPU. Busy-waiting: while queue.empty(): check again — burns 100% CPU. Sleep-polling: sleep(100ms) then check — wastes 100ms of latency. Condition variable: thread releases its lock and goes to sleep (zero CPU) until another thread calls notify(). Wakes instantly when work arrives. Always recheck condition in a while loop after waking — another thread may have already taken the work"
48
New cards
"What is a blocking queue and why is it the default for producer-consumer?"
"Thread-safe queue that blocks consumers when empty and blocks producers when full. API handler calls queue.put(task) — returns immediately. Worker calls queue.take() — blocks until work arrives, then processes it. All synchronization, waiting, and backpressure built in. Don't implement condition variables from scratch — use a blocking queue"
49
New cards
"What capacity should you give a blocking queue?"
"Size based on burst tolerance. Workers do 100 tasks/sec, want to absorb a 10-second spike → need 1000 capacity. Too small = producers block frequently hurting throughput. Too large = memory exhaustion under sustained overload. Never leave capacity unbounded — 50,000 users clicking at once will OOM your service"
50
New cards
"What are the 3 options when a blocking queue is full?"
"1) Block with put() — use for internal batch pipelines where slowing the producer is acceptable. 2) Timeout and reject with offer(timeout) — use on request paths: if queue full within 100ms, return HTTP 503. 3) Drop with offer() — use for lossy workloads like analytics events where losing some under load is acceptable"
51
New cards
"How do you shut down workers blocked on a queue?"
"1) Interrupt threads — wakes them from take() with InterruptedException. 2) poll(timeout) instead of take() — workers periodically wake, check a shutdown flag, exit if set. 3) Poison pill — submit one special sentinel task per worker. Worker exits when it pulls the sentinel. Works when you can't interrupt threads"
52
New cards
"What is the actor model?"
"Each actor has a private mailbox (queue) and processes one message at a time — no locks needed inside the actor. ChatSessionActor processes messages sequentially: no two messages are processed concurrently so its internal state is safe. Use for many independent stateful entities (chat sessions, game rooms, trading order books). For simple producer-consumer, a blocking queue is simpler"
53
New cards
"What is a scarcity concurrency problem?"
"Limited resources with many requesters. 10 DB connections, 100 concurrent requests — 90 must wait. A connection that's never returned blocks the pool. Eventually all 10 are stuck waiting on slow queries, no new requests can get a connection, service hangs. Users see timeouts. No errors, no crashes — just everything stuck"
54
New cards
"What is a semaphore?"
"Counter with N permits. acquire() decrements (blocks when 0), release() increments (wakes one waiter). Use to limit concurrent operations when you have no actual objects to hand out. APIClient: acquire permit before HTTP call → at most 5 requests in flight at once → release in finally. Always release in finally — forgotten release drains permits to zero and hangs the system"
55
New cards
"When do you use semaphore vs blocking queue for scarcity?"
"Semaphore: you just need to limit how many operations run concurrently — no objects to hand out. 'At most 5 concurrent API calls' — use Semaphore(5). Blocking queue: you need to hand out actual stateful objects. DB connections aren't interchangeable by count — each has an open socket. Use Queue holding the actual connection objects"
56
New cards
"What is resource pooling with a blocking queue?"
"Pre-create N expensive DB connections in a Queue(maxsize=N). conn = pool.take() to borrow, pool.put(conn) to return — always in finally block. If all connections checked out, take() blocks until one returns. Use poll(timeout) instead of take() on request paths — blocking forever causes user-visible hangs and thread exhaustion"
57
New cards
"What are the 3 scarcity pattern types?"
"1) Limit concurrent operations → Semaphore(N). Example: DownloadManager allows 3 concurrent downloads. 2) Limit aggregate consumption → Semaphore where 1 permit = 1MB. Example: DiskWriter caps 100MB of in-flight writes. 3) Reuse expensive objects → blocking Queue of objects. Example: ConnectionPool of 10 DB connections shared across 100 threads"
58
New cards
"What is work stealing?"
"Each worker has its own task queue. When empty, it steals from another worker's queue. ImageProcessor with 8 workers: 7 workers finish their quick resizes while 1 is stuck on a huge 4K video — the 7 idle workers steal tasks from each other's queues to stay busy. Used by Java's ForkJoinPool and Go's scheduler internally"
59
New cards
"What is the scarcity decision tree?"
"Count limit only → semaphore. Variable resource units (each op consumes different MB/memory) → semaphore where permits = units. Actual stateful objects (connections, GPU contexts, file handles) → blocking queue pool. Need to maximize utilization with variable task lengths → work stealing, batching, or adaptive pool sizing"
60
New cards
"What is the most critical concurrency rule across all 3 problem types?"
"Always release in a finally block. Semaphore: exception before release → permit leaked forever, pool slowly drains to zero. Resource pool: exception before returning connection → connection leaked, pool eventually exhausted, all threads hang waiting for connections that never come back. Interviewers will call this out immediately if you skip it"