What this lesson is for
You have now worked through the building blocks of low-level design (LLD): object-oriented modelling, SOLID, class diagrams, the main design patterns, and six full case studies. This final lesson turns that knowledge into interview performance. It covers:
- How a machine coding round works and how to spend 60 or 90 minutes on it.
- A scoring rubric of the kind interviewers commonly use, so you know what earns credit.
- A short, fully worked example to show the method in action, with code that compiles and runs.
- Twelve further practice problems, each with requirements to assume, key entities, patterns worth considering and the pitfalls that catch candidates.
- A checklist for reviewing your own solution before you say "done".
A machine coding round is an interview in which you design and implement a small but complete system, such as a parking lot or a cache, in a real editor within a fixed time. Some companies run it as a 60 to 90 minute live session with an interviewer; others give 90 to 120 minutes alone and then review the code with you. A related format, the LLD discussion round, asks for a class diagram and key methods on a whiteboard or shared document, with some code. The method below works for both; in a discussion round you simply write less code and talk more.
The shape of a good round
The most common failure is not a lack of knowledge. It is spending 40 minutes drawing classes and then running out of time with nothing that runs, or coding immediately and then rewriting everything when a requirement appears. A fixed plan prevents both.
A 90-minute plan
| Minutes | Phase | Output |
|---|---|---|
| 0 to 10 | Clarify requirements | Written list: functional, non-functional, out of scope |
| 10 to 20 | Model | Entities, relationships, key interfaces; a short text class diagram |
| 20 to 25 | Plan the code | Package or file list, order of implementation, what the demo will show |
| 25 to 70 | Implement | Core path first, then validation, then the second feature |
| 70 to 80 | Demo and test | A main or tests exercising normal and edge cases |
| 80 to 90 | Discuss | Concurrency, extensions, trade-offs, what you would do next |
A 60-minute plan
| Minutes | Phase |
|---|---|
| 0 to 7 | Clarify; agree on the two or three features that matter most |
| 7 to 15 | Model and name the interfaces |
| 15 to 48 | Implement the core path and the most important rule |
| 48 to 55 | Run a demo |
| 55 to 60 | Discuss concurrency and extensions |
With 60 minutes, you will not finish everything. That is expected. Say early which features you will build and which you will only describe, and get the interviewer's agreement.
Interview tip
Say your time plan aloud at the start: "I'll take about ten minutes for requirements and the model, then code the booking path first, and keep the last ten minutes for a demo and concurrency." Interviewers relax when they see you managing the clock, and they will nudge you if their priorities differ.
Step by step
1. Clarify requirements (do not skip). Ask about scope ("one lot or many?"), rules ("can a car use a truck spot?"), scale ("how many concurrent users?"), and failure cases ("what if payment fails?"). Write the answers down as three lists: functional, non-functional, out of scope. Each answer usually adds or removes a class, so this step directly saves coding time.
2. Identify entities and their responsibilities. Underline the nouns in the requirements; they are candidate classes. Underline the verbs; they are candidate methods. Then ask of each noun: does it hold data, a rule, or coordination? Rules that change (pricing, scheduling, splitting) become interfaces. Things that only differ in a value become enums, not subclasses.
3. Draw the relationships. A short text diagram is enough: who owns whom (composition), who refers to whom (association), who depends on which interface. See UML and class diagrams for notation.
4. Choose patterns deliberately. For each pattern you use, be ready to say what change it absorbs. "Strategy for pricing, because tariffs change; Observer for display boards, because new consumers appear; State for the elevator, because behaviour depends on mode." Do not add patterns that absorb nothing.
5. Code the core path end to end first. For a parking lot that is park and exit; for a booking system it is hold and confirm. Get one complete path working with simple implementations before adding validation, second strategies or extras. A thin working system beats a thick half-written one.
6. Write a demo or tests. A main method that runs a scenario and prints results, or a few unit tests. Include at least one failure case (invalid input, sold out, race). Running code is the strongest evidence you can produce.
7. Discuss what you did not build. Concurrency, persistence, extensions, scaling. Interviewers often score this conversation as highly as the code.
What to write in which order
1. Enums and value objects (VehicleType, Money, SeatStatus)
2. Interfaces for rules (PricingStrategy, SplitStrategy)
3. Entities (Spot, Ticket, Expense)
4. The coordinating service (ParkingLot, BookingService)
5. One simple implementation of each interface
6. main(): the demo scenario
7. Validation and edge cases
8. Thread safety
9. Second implementations, extras
This order means that at any point after step 6 you have something that runs. If time runs out at step 8, you can still describe it.
How you are scored
Interviewers usually score several dimensions and combine them into a hire decision. Exact rubrics vary by company and are rarely published; the table below reflects what is commonly assessed in LLD and machine coding rounds.
| Dimension | Strong | Acceptable | Weak |
|---|---|---|---|
| Requirements | Asks focused questions, writes assumptions, scopes the work with the interviewer | Asks some questions | Starts coding immediately; misses key rules |
| Object model | Clear entities with single responsibilities; enums for value differences; data separate from rules | Reasonable classes, some mixing | God class; subclasses with no behaviour; public mutable fields everywhere |
| Use of patterns | Patterns chosen for a stated reason; can name the alternative | Correct patterns, weak justification | Patterns forced in, or obvious ones missed |
| Code quality | Readable names, small methods, immutability where sensible, no duplication | Mostly readable | Long methods, magic numbers, copy-paste |
| Correctness | Runs; demo covers normal and failure cases | Runs for the happy path | Does not compile or run |
| Extensibility | New requirements fit by adding classes; candidate shows where | Some changes need edits in several places | Every change is a rewrite |
| Concurrency | Identifies the races, picks a locking granularity and explains why | Adds synchronized without reasoning | Not considered |
| Communication | Thinks aloud, checks in, handles hints well | Quiet but responsive | Defensive or silent |
Two points stand out from this table. First, correctness is necessary but not sufficient: working code with a god class scores below working code with a clean model. Second, communication runs through every row: an interviewer can only credit reasoning they hear.
Common mistake
Treating the round as a coding speed test and staying silent for 40 minutes. The interviewer cannot see your reasoning, cannot help you when you go off track, and has little to write in the feedback. Narrate decisions in a sentence or two as you make them.
A worked example: an in-memory key-value store with TTL
To show the method on a small problem, here is a compressed run through "design an in-memory key-value store where entries can expire".
Clarify. Keys and values are strings. Operations: put, put with a time to live (TTL), get, delete. An expired key behaves as if absent. Many threads use the store. Persistence and memory limits are out of scope. Expiry precision of a few milliseconds is fine.
Model. One map from key to an immutable Entry(value, expiresAtMs). An injected clock for testability. No subclasses needed.
Decide expiry. Two mechanisms, as in the movie booking holds: lazy expiry (check on read and remove) and active expiry (a periodic purge so keys that are never read again do not leak memory). Use both.
Concurrency. ConcurrentHashMap makes single-key operations atomic. One subtle point: when get finds an expired entry and removes it, another thread may have just put a fresh value under the same key. map.remove(key, expectedEntry) removes only if the mapping is still the expired entry, so the fresh value survives.
Code (about 60 lines plus a demo):
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.LongSupplier;
/** A value plus the moment it stops being visible (Long.MAX_VALUE = never). */
record Entry(String value, long expiresAtMs) {
boolean aliveAt(long now) { return now < expiresAtMs; }
}
interface KeyValueStore {
void put(String key, String value);
void put(String key, String value, long ttlMs);
Optional<String> get(String key);
boolean delete(String key);
int purgeExpired();
}
final class InMemoryKeyValueStore implements KeyValueStore {
private final ConcurrentHashMap<String, Entry> map = new ConcurrentHashMap<>();
private final LongSupplier clock;
InMemoryKeyValueStore(LongSupplier clock) { this.clock = clock; }
public void put(String key, String value) { put(key, value, Long.MAX_VALUE); }
public void put(String key, String value, long ttlMs) {
Objects.requireNonNull(key, "key"); Objects.requireNonNull(value, "value");
if (ttlMs <= 0) throw new IllegalArgumentException("ttl must be positive");
long now = clock.getAsLong();
long expires = ttlMs == Long.MAX_VALUE ? Long.MAX_VALUE : now + ttlMs;
map.put(key, new Entry(value, expires));
}
/** Lazy expiry: an expired entry is removed when someone reads it. */
public Optional<String> get(String key) {
Entry e = map.get(key);
if (e == null) return Optional.empty();
if (e.aliveAt(clock.getAsLong())) return Optional.of(e.value());
map.remove(key, e); // remove only this exact entry, not a newer put
return Optional.empty();
}
public boolean delete(String key) { return map.remove(key) != null; }
/** Active expiry: a background task would call this every few seconds. */
public int purgeExpired() {
long now = clock.getAsLong();
int[] removed = {0};
map.forEach((k, e) -> { if (!e.aliveAt(now) && map.remove(k, e)) removed[0]++; });
return removed[0];
}
int rawSize() { return map.size(); }
}
public class KeyValueStoreDemo {
public static void main(String[] args) throws Exception {
AtomicLong now = new AtomicLong(0);
InMemoryKeyValueStore kv = new InMemoryKeyValueStore(now::get);
kv.put("config", "v1");
kv.put("otp:asha", "482913", 30_000);
kv.put("session:42", "dev", 60_000);
now.set(29_999);
System.out.println("t=29.999s otp = " + kv.get("otp:asha").orElse("(none)"));
now.set(30_000);
System.out.println("t=30.000s otp = " + kv.get("otp:asha").orElse("(none)"));
now.set(61_000);
System.out.println("t=61s raw entries before purge = " + kv.rawSize());
System.out.println("purged = " + kv.purgeExpired() + ", raw entries after = " + kv.rawSize());
System.out.println("config = " + kv.get("config").orElse("(none)"));
System.out.println("delete config = " + kv.delete("config") + ", again = " + kv.delete("config"));
ExecutorService pool = Executors.newFixedThreadPool(8);
List<Future<?>> fs = new ArrayList<>();
for (int t = 0; t < 8; t++) {
final int id = t;
fs.add(pool.submit(() -> { for (int i = 0; i < 10_000; i++) kv.put("k" + (i % 1000), "t" + id, 5_000); }));
}
for (Future<?> f : fs) f.get();
pool.shutdown();
System.out.println("8 threads x 10,000 puts over 1,000 keys -> entries = " + kv.rawSize());
now.addAndGet(5_000);
System.out.println("after 5 s: purged = " + kv.purgeExpired() + ", entries = " + kv.rawSize());
}
}
Real output (Temurin JDK 21):
t=29.999s otp = 482913
t=30.000s otp = (none)
t=61s raw entries before purge = 2
purged = 1, raw entries after = 1
config = v1
delete config = true, again = false
8 threads x 10,000 puts over 1,000 keys -> entries = 1000
after 5 s: purged = 1000, entries = 0
The OTP is visible at 29.999 s and gone at exactly 30 s, because expiry is now < expiresAt. At 61 s, the session entry has expired but nobody read it, so it is still stored until the purge removes it, which is why both mechanisms are needed. Under 8 threads writing 1,000 keys, the map holds exactly 1,000 entries, and after 5 seconds the purge removes all of them.
Extensions to discuss: a maximum size with LRU eviction (see Design an LRU cache); a min-heap or timing wheel keyed by expiry time so the purge does not scan everything; snapshots to disk; and namespaces per tenant.
This whole example is roughly what a strong 45-minute answer looks like for a smaller problem.
Twelve practice problems
For each problem, set a timer for 60 or 90 minutes and follow the plan. Afterwards, compare with the notes here and run the checklist at the end of this lesson. The notes are deliberately not full solutions: the value comes from doing the modelling yourself.
1. Library management system
Requirements to assume. Members search the catalogue by title, author or subject; borrow and return copies; reserve a book when all copies are out; pay fines for late returns. A member may hold at most 5 books for 14 days each. Librarians add books and copies.
Key entities. Book (title, authors, ISBN) versus BookCopy (barcode, status), Member, Loan (copy, member, due date, returned date), Reservation (queue per book), FinePolicy, Catalogue with search indexes.
Patterns to consider. Strategy for fine policies; Observer to notify the next member in the reservation queue when a copy is returned; State for a copy (available, loaned, reserved, lost).
Pitfalls. Mixing Book and BookCopy (a title has many physical copies); scanning all books for search instead of keeping indexes by author and subject; forgetting that a returned copy with a waiting reservation must be held for that member, not put back on the shelf; computing fines with floating point.
2. Hotel booking
Requirements to assume. Search rooms by date range and type; book for a date range; cancel with a policy; check in and out; prices vary by season.
Key entities. Hotel, Room (number, type), RoomType, Booking (room, guest, check-in date, check-out date, status), Guest, PricingStrategy, CancellationPolicy, Invoice.
Patterns to consider. Strategy for pricing and cancellation; State for booking status (reserved, checked in, checked out, cancelled); Factory for creating room types.
Pitfalls. Treating availability as a boolean on Room instead of per date; date-range overlap logic (two stays overlap if startA < endB and startB < endA, with check-out day free for the next guest); double booking the same room for overlapping dates under concurrency, which needs the same locking ideas as seat holds; time zones for check-in times.
3. Food delivery
Requirements to assume. Customers browse restaurants and menus, place an order with items from one restaurant, pay, and track status. The system assigns a delivery partner. Restaurants accept or reject orders.
Key entities. Restaurant, MenuItem, Cart, Order (lines, status), Customer, DeliveryPartner, Assignment, PaymentMethod, OrderStatus.
Patterns to consider. State for the order lifecycle (placed, accepted, preparing, picked up, delivered, cancelled); Strategy for partner assignment (nearest, least busy); Observer for notifications to customer, restaurant and partner.
Pitfalls. Allowing items from two restaurants in one cart without asking; invalid status jumps (delivered before picked up) when transitions are not enforced; assigning one partner to two orders concurrently; price changes between adding to cart and paying (snapshot the price on the order line).
4. Notification service
Requirements to assume. Other services send a notification request (user, template, data). The service renders the message and sends it over email, SMS or push according to user preferences, with retries on failure and per-user rate limits.
Key entities. NotificationRequest, Template, Channel interface with EmailChannel, SmsChannel, PushChannel, UserPreferences, DeliveryAttempt, RetryPolicy.
Patterns to consider. Strategy or plain polymorphism for channels; Template Method or a renderer for message templates; Chain of Responsibility for fallbacks (push, then SMS); Decorator for adding rate limiting or logging around a channel; a queue between intake and sending.
Pitfalls. Sending synchronously inside the caller's request; retrying without backoff or idempotency, which duplicates messages; ignoring opt-outs and quiet hours; hard-coding channel choices in if-else chains. For the system-level version, see Design a notification system.
5. Logger framework
Requirements to assume. Applications log messages with levels (debug, info, warn, error). Loggers have names and a minimum level. Messages go to one or more destinations (console, file) with a configurable format. Logging must be thread-safe and should not slow the application much.
Key entities. Logger, LogLevel, LogRecord (time, level, logger name, message, thread), Appender interface (ConsoleAppender, FileAppender), Formatter, LoggerFactory.
Patterns to consider. Singleton or factory for obtaining loggers by name; Chain of Responsibility for level filtering or handler chains; Strategy for formatters; Observer-like fan-out to appenders; producer-consumer queue for asynchronous appenders.
Pitfalls. Building the message string before checking the level (wasted work for disabled debug logs); interleaved lines from multiple threads writing to one file; losing buffered logs on shutdown; using a global lock across all appenders so a slow file blocks console output.
6. In-memory key-value store with TTL
Requirements to assume. As in the worked example, plus: optional maximum number of keys with eviction, and atomic operations such as putIfAbsent and increment.
Key entities. Entry (value, expiry), KeyValueStore, EvictionPolicy, Clock.
Patterns to consider. Strategy for eviction (LRU, LFU); Decorator for adding statistics; Command objects if you add a transaction log.
Pitfalls. Only lazy expiry (memory leak for keys never read); only active expiry (stale reads between sweeps); removing a key that was just overwritten (use conditional remove); increment implemented as get-then-put, which loses updates under concurrency (use compute or merge).
7. Pub-sub message queue
Requirements to assume. Producers publish messages to named topics. Consumers subscribe to topics and receive every message published after they subscribed, in order. Consumers process at different speeds. Messages are retained until all current subscribers have read them.
Key entities. Topic (append-only list of messages), Message (id, payload, timestamp), Subscriber (offset per topic), Broker, Publisher.
Patterns to consider. Observer is the obvious pattern, but a pull model with per-subscriber offsets (as in Kafka) decouples speeds better than pushing to each subscriber directly; producer-consumer with blocking queues; Strategy for delivery semantics.
Pitfalls. A slow subscriber blocking publishers when you push synchronously; losing order when several threads deliver to one subscriber; unbounded memory if one subscriber never reads; unclear delivery guarantees (at most once versus at least once). See Message queues and Kafka and event streaming.
8. Snake and ladder
Requirements to assume. A 100-square board with snakes and ladders, 2 to 4 players, one die, players move in turn, landing on a snake head or ladder foot moves the player. The first to reach exactly 100 wins (overshooting means no move).
Key entities. Board (size, map from square to destination for snakes and ladders), Player (name, position), Dice interface, Game (players queue, turn loop, winner).
Patterns to consider. Strategy for dice (fair, loaded for tests, two dice); a queue for turn order; validation of board configuration.
Pitfalls. Separate Snake and Ladder classes with duplicate logic, when both are "jump from A to B" (a single map suffices, validated so snakes go down and ladders up); random dice that make tests impossible (inject the dice); missing the "exact 100" rule; cycles if a ladder top is a snake head that leads back.
9. Tic-tac-toe or chess
Requirements to assume. Tic-tac-toe on an n × n board for two players, detecting a win in O(1) per move; or chess with legal move validation for each piece, check and checkmate.
Key entities. Board, Cell or Square, Player, Move, Game (status, turn); for chess, Piece with subclasses (King, Queen, Rook, Bishop, Knight, Pawn), MoveValidator.
Patterns to consider. For chess, polymorphism on pieces (here subclasses are justified because each piece moves differently); Command for moves to support undo; Memento for saving game state; State for game status.
Pitfalls. Tic-tac-toe win check by scanning the whole board after every move (keep row, column and diagonal counters instead: +1 for one player, −1 for the other, win at ±n); chess special rules forgotten (castling, en passant, promotion) without at least naming them; letting a move leave your own king in check.
10. Cab booking (ride hailing)
Requirements to assume. Riders request a ride from a pickup to a drop location; the system finds nearby available drivers, offers the ride, and the first to accept gets it. Fares depend on distance, time and surge. Trips move through states.
Key entities. Rider, Driver (location, status), RideRequest, Trip (status, fare), Location, DriverMatcher, FareStrategy, PaymentMethod.
Patterns to consider. Strategy for matching and fares; State for trips (requested, accepted, driver arriving, in progress, completed, cancelled); Observer for location updates and notifications.
Pitfalls. Two drivers accepting the same ride (atomic assignment, the same race as seat booking); one driver assigned two rides; scanning every driver for each request instead of a spatial index (grid cells or geohash); surge applied after the rider saw a price. See Design ride hailing.
11. Task scheduler
Requirements to assume. Submit tasks to run once at a time, after a delay, or repeatedly at a fixed rate. A pool of worker threads runs due tasks. Tasks can be cancelled. Optionally, tasks have priorities.
Key entities. Task (id, runnable, next run time, period, status), Scheduler, a PriorityQueue or DelayQueue ordered by next run time, worker threads, Clock.
Patterns to consider. Command (a task wraps an action); producer-consumer with a blocking delay queue; Strategy for retry policies.
Pitfalls. Busy-waiting loops that burn CPU instead of waiting until the earliest task is due; a long task delaying all others when there is only one worker; drift in repeated tasks (schedule from the planned time, not from when the last run finished, unless the requirement says otherwise); cancelled tasks still running because the queue was not updated; exceptions in a task killing the worker thread. See Threads for the thread pool background.
12. File system
Requirements to assume. An in-memory hierarchical file system with directories and files; operations mkdir -p, create, write, read, list, delete, move, and path resolution with /, . and ... Optionally sizes and permissions.
Key entities. Node (abstract: name, parent), Directory (children map), File (content, size), Path parsing, FileSystem facade.
Patterns to consider. Composite: a directory contains nodes that may be files or directories, and operations such as size apply uniformly to both; Visitor for operations over the tree (search, total size); Iterator for walking.
Pitfalls. Linear search in children instead of a map by name; moving a directory into its own subdirectory (must be rejected); path parsing edge cases (//, trailing slash, .. at root); deleting a non-empty directory without a recursive flag; recursion depth on very deep trees. See File systems for how real file systems store this.
Reviewing your own solution
After each practice session, go through this list honestly. Every unchecked item is something an interviewer would likely mention in feedback.
Requirements
- I wrote down functional requirements, non-functional requirements and out-of-scope items.
- I agreed the scope for the time available before coding.
- I identified the hardest rule or invariant (no double booking, balances sum to zero) and designed around it.
Model
- Each class has one clear responsibility I can state in a sentence.
- Rules that change (pricing, scheduling, splitting) are behind interfaces.
- Types that differ only in values are enums, not subclasses.
- No class reaches into another's internals; fields are private, and value objects are immutable.
- Physical things and their per-context state are separate where needed (seat versus seat-in-show).
Code
- It compiles and runs, and the demo covers at least one failure case.
- Names are clear; methods are short; there are no magic numbers.
- Money uses integers or
BigDecimal, neverdouble. - Time comes from an injected clock, and randomness from an injected source.
- Errors are handled at the right level with clear messages.
Concurrency
- I identified every shared mutable state and every check-then-act sequence.
- Each is protected with a stated locking granularity (per object, per key, per group) and a reason.
- Multi-lock operations take locks in a fixed order.
- No lock is held during slow I/O (payments, network, disk).
Extensibility and discussion
- For three likely new requirements, I can say which classes I would add and which, if any, I would change.
- I can name each pattern I used and what change it absorbs.
- I know how the design changes with a database and several servers.
Common mistakes in the round
- Skipping requirements and discovering a key rule after 40 minutes of code.
- Designing everything up front and leaving 15 minutes to code.
- Pattern stuffing: Factory, Builder, Singleton and Visitor in a 300-line problem with no reason for any.
- A god class that holds every map and every rule.
- Inheritance for code reuse where composition or an enum fits.
- No demo: claiming it works without running it.
- Ignoring concurrency until asked, then adding
synchronizedeverywhere. - Arguing with hints. When the interviewer suggests a direction, it is usually because time is short or you missed something.
- Silent coding, which hides your reasoning.
- Over-generalising, such as a plug-in system for vehicle types when three enum values would do.
Interview questions
These are the meta-questions interviewers commonly ask during or after an LLD round, with model answers.
Q1. How do you start an LLD problem?
I spend the first few minutes clarifying scope, rules, scale and failure cases, and I write the answers as functional, non-functional and out-of-scope lists. Then I identify entities and the one invariant that matters most, such as no double booking, and agree with the interviewer which features to build in the time.
Q2. How do you decide between inheritance and composition?
I use inheritance only when subtypes genuinely behave differently and satisfy the base contract, such as chess pieces. When types differ only in values, I use an enum; when behaviour varies independently, I compose an interface such as a strategy. Composition keeps classes smaller and easier to change.
Q3. How do you choose which design patterns to use?
I start from the change I expect: pricing rules change, so Strategy; new listeners appear, so Observer; behaviour depends on mode, so State. If I cannot name the change a pattern absorbs, I leave it out. Patterns should make the code simpler to extend, not longer.
Q4. What do you implement first when time is short?
The core path end to end with the simplest correct implementation, plus a demo that runs it. Then validation and failure cases, then concurrency, then extras. That way I always have something working to show.
Q5. How do you make your design testable?
Inject dependencies through constructors, especially the clock, random sources and external services such as payment, behind interfaces. Keep logic in plain classes rather than in main. Then tests can drive time forward and use fakes deterministically.
Q6. How do you approach thread safety in an LLD round?
I list the shared mutable state and every check-then-act sequence, then choose the coarsest locking that meets the load, and refine if needed: one lock for a small system, per-object locks or compare-and-set for higher concurrency. I also confirm that no lock is held during slow I/O and that multi-lock operations use a fixed order.
Q7. How would your design change with a database and multiple servers?
In-memory locks no longer protect shared state across servers, so the database becomes the arbiter, using transactions, conditional updates or unique constraints. Caches and events are added for performance and decoupling. The domain classes and interfaces stay largely the same; repository implementations change.
Q8. The interviewer adds a requirement you did not plan for. What do you do?
I restate it, identify which part of the model it touches, and say whether it fits by adding a class (a new strategy or state) or needs a change to an existing one. If my design makes it hard, I say so honestly and describe the refactor. Interviewers mostly assess how calmly and precisely I reason about the change.
Q9. How do you handle money and time in LLD code?
Money is stored as integer minor units or BigDecimal, with explicit rounding rules. Time comes from an injected clock, uses Instant for moments and Duration for spans, and time zones only at the edges for display.
Q10. How do you know your solution is done?
It runs, the demo covers the normal path and failure cases, the main invariant holds under concurrency, and I can walk through how three likely extensions would fit. I also review names and remove dead code before saying I am finished.
Q11. What is the difference between an LLD round and a system design round?
LLD focuses on classes, interfaces, patterns and working code inside one service. System design focuses on components across machines: storage choices, scaling, caching, queues and failure handling. The skills meet at boundaries, such as concurrency control and APIs.
Q12. How should you practise for machine coding rounds?
Time-box full problems to 60 or 90 minutes in a real editor, always produce a running demo, then review against a checklist and rewrite the weakest part. Revisit a problem a week later and compare. Practise explaining aloud, because silent practice does not train the communication that is scored.
Key takeaways
- A machine coding round is scored on requirements, model, patterns, code, correctness, extensibility, concurrency and communication, not just on working code.
- Use a time plan: clarify, model, plan, code the core path, demo, discuss.
- Write code in an order that leaves something runnable as early as possible.
- Choose each pattern for the change it absorbs; leave out patterns that absorb nothing.
- Find the main invariant early and protect it, including under concurrency.
- Practise the twelve problems here with a timer, then review with the checklist.
- Talk through decisions as you make them; the interviewer can only credit what they hear.
Next lesson
You have finished the LLD track. Start again from the LLD introduction to revise, or move on to high-level design with the system design interview playbook.

