AlgoInsight

Free lesson · Frontend System Design

The round nobody prepares for correctly

Hard18 min readfrontendsystem-design

There's a particular kind of rejection that stings more than the others, and it goes like this. You sit the machine-coding round. You're asked to build something - a searchable list, an autocomplete, a star-rating widget - and you build it. It works. You demo it at the end, the thing functions, you type and results appear, and you walk away thinking that went fine. A week later you get the rejection, and you cannot understand it, because the thing worked. You did what they asked. How is that a fail?

This lesson exists to answer that question, because the gap between "the thing worked" and "you passed" is the single most misunderstood thing about low-level design rounds - and it's where most of the rejections live. The uncomfortable truth, stated plainly so the rest of the module can build on it: in an LLD machine-coding round, getting it working is the floor, not the ceiling. Functionality is necessary - you certainly fail if it doesn't work - but it is nowhere near sufficient. Two candidates can both produce a working autocomplete and one gets the offer and one gets the rejection, and the difference is everything else - how the code was structured, whether it handled the cases real software hits, whether it could absorb a new requirement, whether the interviewer could follow the thinking, whether the time was spent wisely. Those are the things being scored, and most candidates don't even know they're on the rubric.

So before this module teaches you to build autocompletes and infinite scrolls and nested comment threads, it has to retune what you think the round is measuring. If you walk in believing the goal is "make it work," you will optimize for the wrong thing - you'll race to functionality, skip the planning, write whatever code gets there fastest, ignore the edge cases, and present a working demo that quietly fails on every dimension that actually decides the outcome. This lesson lays out what the round is, what's genuinely being scored, and - the heart of it - why candidates get rejected even when their code runs. Everything else in Module 10 is in service of clearing not just the floor but the ceiling.

What an LLD machine-coding round actually is

Let's be precise about the format, because "LLD round" means slightly different things at different companies and the variations matter.

A low-level design (LLD) machine-coding round asks you to build a real, working piece of frontend - a UI component or a small application - from a prompt, usually in a constrained window of 45 to 90 minutes. The prompt is deliberately open: "build an autocomplete search," "build an image carousel," "build a todo app with filtering," "build a star-rating component." You write actual code - typically in React (sometimes vanilla JavaScript, occasionally a framework of your choice) - and at the end you have something that runs and that the interviewer can interact with. Some rounds are live (you share your screen and code while the interviewer watches and talks with you); some are take-home (you're given the problem and a deadline and submit a repository); a few are hybrids (take-home build, then a live review). The live format is the most common and the most revealing, so this module assumes it as the default while noting where take-home differs.

The word "machine coding" is the key. It distinguishes this round from the two other technical rounds you'll face, and understanding the distinction tells you what mode to be in:

It is not a DSA round. A data-structures-and-algorithms round asks you to solve a self-contained algorithmic puzzle (reverse a tree, find the shortest path) where the answer is a clever function and the scoring is about correctness and complexity. Machine coding is the opposite kind of work: there's usually no clever algorithm at the center; the challenge is building software - structuring a feature, managing state, handling interactions and asynchronicity, organizing components. You can be a strong algorithmist and a weak machine-coder, and vice versa, because they test different muscles. If you walk into a machine-coding round looking for the "trick," you've misread it - there usually isn't one.

It is not an HLD round. High-level system design asks you to architect at scale - to talk through how you'd design a system serving millions of users, drawing boxes, discussing tradeoffs, capacity, data flow, and the kind of distributed-systems and architecture concerns the rest of this course's case-study modules cover. HLD is mostly discussion and diagrams; you rarely write running code. Machine coding (LLD) is the inverse: you write running code for a single, contained feature, and the concerns are implementation-level - component decomposition, props APIs, state, edge cases, code cleanliness. HLD asks "how would this system be shaped?"; LLD asks "build this thing, well, right now." Frontend interviews increasingly include both a machine-coding round and a frontend-HLD round, and conflating them - discussing scale and architecture when you're supposed to be coding, or diving into code when you're supposed to be designing - is itself a failure mode.

So the honest one-line definition to hold: a machine-coding round is a simulation of you doing real, day-to-day frontend engineering - building a contained feature in working code - compressed into an hour and watched closely. That framing, "a simulation of real work," is the thread that explains the entire rubric, because once you see the round as the interviewer asking "what would it actually be like to have this person build features on my team?", every scoring dimension falls out naturally.

graph TD
    Tech[Technical interview rounds] --> DSA[DSA<br/>algorithmic puzzles<br/>→ clever function, complexity]
    Tech --> HLD[HLD / System Design<br/>architect at scale<br/>→ diagrams, tradeoffs, discussion]
    Tech --> LLD[LLD / Machine Coding<br/>build a contained feature<br/>→ WORKING CODE, 45-90 min]
    LLD --> Sim[A simulation of real<br/>day-to-day frontend engineering]
    Sim --> Q["Interviewer's real question:<br/>'what is it like to build<br/>features with this person?'"]
This diagram places the LLD machine-coding round among the technical interview rounds. A DSA round tests algorithmic puzzles, where the answer is a clever function judged on correctness and complexity. An HLD or system design round tests architecting at scale, mostly through diagrams, tradeoffs, and discussion rather than code. The LLD or machine-coding round is different from both: you build a contained feature in actual working code, typically in 45 to 90 minutes. The key insight is that machine coding is a simulation of real day-to-day frontend engineering, so the interviewer's true underlying question is: what would it be like to build features with this person on my team?

What's actually being scored

Now the rubric - the dimensions that decide the outcome. The reason candidates fail is almost always that they optimize for the first dimension and neglect the rest, so it's worth seeing the whole list and internalizing that functionality is one line item among many.

Functionality and correctness. Does it work? Does it meet the stated requirements? Did you handle the core behavior the prompt asked for? This is table stakes - you must clear it - but clearing it earns you no special credit, because everyone is expected to clear it. Think of it as the price of admission to being evaluated on everything else.

Component decomposition and architecture. How did you break the problem into pieces? Is there a sensible component structure - a parent that coordinates, children with clear responsibilities, logic separated from presentation - or is it one enormous component doing everything? This is, in my experience, the dimension that most separates strong from weak candidates, because good decomposition is hard to fake and reveals how you actually think about building software. A clean component tree says "I know how to structure a frontend"; a single god component says the opposite, no matter how well it runs.

Code quality and readability. Is the code clean, readable, consistently named, and organized - or is it a tangle of cryptic variables, copy-pasted blocks, magic numbers, and dead code? The interviewer is reading your code as you write it and asking "would I want to maintain this? would I want this in our codebase?" Messy code that works still reads as a junior signal, because real engineering is mostly reading and modifying code, and code nobody can follow is a liability however well it runs today.

Edge cases and real-world robustness. Did you handle the states real software hits - empty (no results, empty list), loading (data in flight), error (the request failed), and boundary conditions (one item, a thousand items, very long strings, rapid input)? Or did you only build the happy path where everything succeeds and the data is always perfect? Handling edge cases is one of the clearest seniority signals there is, because juniors build the happy path and seniors instinctively ask "what happens when this is empty / slow / broken?" An autocomplete that works when you type "react" but shows nothing graceful when the API fails or returns no matches is an incomplete piece of engineering.

Extensibility. Can your design absorb a new requirement without being torn apart? This matters enormously because interviewers deliberately add requirements mid-round - "now make the results keyboard-navigable," "now support multiple selection," "now add categories" - precisely to test whether your design was rigid or flexible. A well-decomposed, well-abstracted solution absorbs the new requirement with a localized change; a rigid one requires you to rewrite half of it, and the interviewer watches which happens. Extensibility is where the component-design choices you made early pay off or punish you.

Communication. In a live round, are you thinking out loud - explaining your approach, narrating tradeoffs, flagging what you're skipping and why - so the interviewer can follow your reasoning? Or are you coding in silence, leaving them to guess whether you know what you're doing? Communication is scored heavily in live rounds because the round is a collaboration simulation, and an engineer who codes in total silence is hard to work with regardless of output. The interviewer can only evaluate the thinking they can see.

Requirements clarification and scoping. Did you ask questions before diving in - clarifying ambiguities, confirming priorities - and did you scope the work sensibly, identifying a minimal core to build first and deferring the nice-to-haves? Or did you assume, guess, and try to build everything? Clarifying and scoping is the difference between an engineer who delivers the right thing and one who builds the wrong thing impressively. The best candidates spend their first few minutes not coding - clarifying and planning - and it's one of the strongest signals they send.

Time management and incremental delivery. Did you get something working early and iterate, or did you build elaborate scaffolding and run out of time with nothing functional? Did you allocate your time across the requirements, or sink forty minutes into pixel-perfect CSS and never finish the core behavior? Time management - and specifically the discipline of incremental delivery, having a working slice at every checkpoint - is what separates a candidate who finishes a clean MVP plus extras from one who has an impressive-looking half-thing that doesn't run.

Best-practices awareness. Do you show awareness of the cross-cutting qualities - basic accessibility (keyboard operability, semantic elements, the things Module 6 covered), performance sensitivity (debouncing input, not re-rendering the world), sensible state management - woven into the build rather than bolted on? You're not expected to do everything, but demonstrating awareness of these concerns (even saying "I'd debounce this in production; let me at least stub it") signals the seniority that this whole course is about.

Why candidates get rejected

Now the heart of the lesson - the specific, recurring failure modes that sink candidates whose code runs. I'm laying these out in detail because recognizing them is half of avoiding them, and because nearly every rejection traces to one or more of these, not to "the code didn't work."

Failure 1 - coding before clarifying or planning. The most common and most damaging mistake: the candidate reads the prompt and immediately starts typing. No clarifying questions, no plan, no scoping - straight to code. This fails on multiple dimensions at once. You risk building the wrong thing (you assumed a requirement the interviewer didn't intend, or missed one they did). You commit to a structure before you've thought about what structure the problem needs, which often means a rigid design you'll fight later. And you signal to the interviewer that you don't plan - that you're the kind of engineer who dives into code without thinking, which is a real-world red flag. The strongest candidates spend the first five to ten minutes not coding - clarifying requirements, sketching the component structure, stating their plan aloud - and that investment pays off across the whole round. Diving straight to code feels productive (you're "making progress") but it's the productive-looking path to a rejection.

Failure 2 - the god component. The candidate builds everything in one giant component - all the state, all the logic, all the markup, all the event handlers, tangled together in a single 300-line file. It might work, but it screams "I don't know how to structure frontend code," which is precisely what the round is testing. Good decomposition - a coordinating parent, focused child components, logic extracted into hooks or functions, presentation separated from behavior - is one of the highest-signal things you can demonstrate, and its absence is one of the clearest rejection triggers. The god component also makes extensibility impossible (Failure 7), so when the interviewer adds a requirement, the monolith has to be hacked apart. Poor decomposition is arguably the single most common code-quality reason strong-seeming candidates fail.

Failure 3 - building everything, finishing nothing. The candidate tries to build the complete feature with all bells and whistles in one pass - every requirement, polished styling, every edge case - and runs out of time with a half-built, non-functional mess. Nothing works end-to-end; there's no demonstrable slice. This fails the most basic expectation (something working) and reveals poor judgment about scope and time. The correct approach - which the next lesson details - is incremental: build a minimal working version of the core first (it works, even if ugly and incomplete), then layer on requirements and polish, so that at every moment you have something demonstrable and you're never caught with nothing. A candidate with a clean, working MVP plus two extras beats a candidate with an impressive-looking thing that doesn't run, every time. "Build everything at once" is a trap that feels ambitious and ends in nothing to show.

Failure 4 - only the happy path. The candidate builds the case where everything works - data loads, results exist, the user does the expected thing - and handles none of the real-world states: no empty state (what shows when there are no results?), no loading state (what shows while data is fetching?), no error state (what happens when the request fails?), no boundary handling (one item, huge lists, rapid input, long strings). This reads as junior because handling these states is exactly what separates software that survives contact with reality from a demo. Interviewers notice immediately when an autocomplete shows a blank void on no matches, or freezes with no feedback while loading, or breaks when the API errors. Even acknowledging the cases you don't have time to fully build ("I'd show a loading spinner here and an empty state here - let me stub them") rescues much of the signal. Ignoring edge cases entirely is a top rejection reason.

Failure 5 - silent coding. In a live round, the candidate codes in total silence - heads down, typing, never explaining the approach, the structure, the tradeoffs, or what they're doing and why. The interviewer is left unable to follow the reasoning, unable to help (they often want to nudge you back on track, but can't if they don't know where you are), and unable to assess the thinking behind the code. Since the round is a collaboration simulation, an engineer who can't or won't communicate while building is hard to evaluate and hard to imagine working with. Even strong code suffers when the interviewer couldn't follow how you got there. Narrating your thinking - "I'm going to lift this state to the parent so siblings can share it," "I'll skip styling for now and come back" - is not optional polish; it's a scored dimension, and silence forfeits it.

Failure 6 - over-engineering. The opposite of Failure 3, and just as fatal: the candidate builds elaborate abstraction for requirements that don't exist - a generic configurable framework when a simple component was asked for, premature optimization, layers of indirection, gold-plating - and either runs out of time or produces something needlessly complex. Over-engineering signals poor judgment (you couldn't tell what the problem needed) and often costs you the actual deliverable. The skill being tested includes restraint - building what's needed, cleanly, with structure that's appropriate to the problem, not maximal. Premature abstraction is a real-world senior anti-pattern, and demonstrating it in an interview reads as a lack of seniority, not an excess of it. The right amount of structure is "enough to be clean and extensible for plausible requirements," not "infinitely generic."

Failure 7 - the rigid design that can't extend. When the interviewer adds a requirement mid-round (and they usually will), the candidate's design can't absorb it - adding the feature requires rewriting large parts, or the candidate freezes because their structure didn't anticipate change. This is the delayed cost of Failures 1 and 2: a design committed-to without planning, or a god component, has no clean seams to extend along. Extensibility is deliberately probed, so a rigid design is a designed-in trap the interviewer springs on purpose. The defense is decomposition and sensible abstraction up front - not over-engineering (Failure 6), but a structure with clean boundaries where a new requirement lands in one place rather than rippling everywhere.

Failure 8 - time mismanagement. The candidate spends their time disproportionately - forty minutes perfecting the CSS of one element while the core behavior is unbuilt, or going deep on a minor sub-feature while the main requirement languishes. They mismanage the clock and finish with the wrong things done. Good time management means allocating time proportional to value, getting the core working first, treating polish as a last priority, and keeping an eye on the clock so you land a coherent deliverable. A candidate who spent their hour on a beautiful button and a broken core has demonstrated exactly the wrong instincts.

graph TD
    Start[Candidate builds the feature] --> Works{Does it work?}
    Works -->|no| Reject1[Reject - failed the floor]
    Works -->|yes| Diff{Clears the differentiators?}
    Diff -->|"clean decomposition?"| D1
    Diff -->|"edge cases handled?"| D2
    Diff -->|"extends cleanly?"| D3
    Diff -->|"communicated clearly?"| D4
    Diff -->|"scoped & managed time?"| D5
    D1 & D2 & D3 & D4 & D5 -->|mostly yes| Offer[Offer / higher level]
    D1 & D2 & D3 & D4 & D5 -->|mostly no| Reject2["Reject - 'but it worked!'<br/>the misunderstood rejection"]
This diagram shows why working code still gets rejected. The candidate builds the feature. If it doesn't work, that's an immediate reject for failing the floor. If it does work, the evaluation moves to the differentiators: is the decomposition clean, are edge cases handled, does it extend cleanly, was the thinking communicated clearly, and was the work scoped with good time management? If most of these are yes, the candidate gets the offer or a higher level. If most are no, the candidate gets rejected despite working code - the misunderstood rejection where the candidate thinks "but it worked!" The point is that passing the floor only qualifies you to be judged on everything else.

What interviewers actually want

Step back from the failure list and you can see the unifying picture: every dimension and every failure mode flows from one question the interviewer is really asking - "what would it be like to have this person build features on my team, day to day?" The machine-coding round is the closest proxy any interview has to the actual job, which is exactly why it's weighted the way it is. In the real job, "it works" is assumed; what makes someone a good engineer to work with is that their code is clean (others can read and modify it), well-structured (it decomposes sensibly), robust (it handles the messy real cases), extensible (it absorbs the inevitable new requirements), and that they communicate, scope, and manage their time like a professional. The rubric isn't arbitrary interview hoops; it's a faithful list of what makes someone good to build software with.

This is also precisely where seniority shows. A junior engineer makes it work. A senior engineer makes it work and clean and extensible and robust and accessible, while communicating the reasoning, having clarified the requirements and scoped the build, landing it within the time. The round doesn't just decide pass/fail - it often decides level, and the level is read off these differentiators. When you hear "they downleveled me" or "they bumped me up," it's usually these dimensions doing the deciding. So the goal isn't to scrape past the floor; it's to demonstrate, through how you build one small feature, that you build software the way a senior engineer does.

I'll be honest about why this is worth dwelling on: when nobody told me the rules of this round early on, I optimized for the wrong target - I raced to working demos and wondered why stronger-credentialed friends with uglier demos got the offers. The rules were never hidden maliciously; they were just unstated, and the entire purpose of this lesson is to state them, so you optimize for what's actually scored instead of for the comforting illusion that "it works" is the goal.

The core insight: necessary versus differentiating

If you take one mental model from this lesson, make it this two-tier framing, because it reorganizes how you'll spend every minute of the round:

The misunderstood rejection - "but it worked!" - is the candidate who cleared the necessary tier and assumed that was the finish line, when it was merely the entry to the tier that decides the outcome. Internalizing this changes your strategy completely: you stop treating "make it work" as the whole game and start treating it as the fast first milestone that frees you to spend the bulk of the round on the differentiators - clean structure, real-world states, a design that extends, narration throughout. The candidates who win are the ones who reach "it works" quickly and cleanly and then spend their remaining time on everything that actually moves the score.

Important: In an LLD machine-coding round, functionality is the floor, not the ceiling - necessary but nowhere near sufficient. You're scored on table stakes (it works, core requirements met - clears the floor, earns nothing special) and, decisively, on differentiators: component decomposition (the highest-signal dimension), code quality/readability, edge cases (empty/loading/error - a seniority signal), extensibility (interviewers deliberately add requirements to test this), communication (think aloud - it's a collaboration simulation), requirements clarification and scoping, time management / incremental delivery, and best-practices awareness (a11y, perf). The "but it worked!" rejection is the candidate who cleared the necessary tier and neglected the differentiating one. The round is a simulation of real day-to-day frontend engineering, so the interviewer's true question is "what's it like to build features with this person?" - and seniority is read off the differentiators.

Interview: Reframe how you spend the round around necessary-vs-differentiating: "I treat 'make it work' as the fast first milestone, not the goal - because functionality is table stakes, and the round is really scored on decomposition, code quality, edge cases, extensibility, communication, scoping, and time management. So I'd spend the first few minutes clarifying requirements and sketching the component structure out loud rather than diving into code, build a clean minimal working core quickly so I always have something demonstrable, then spend the bulk of my time on the differentiators - handling empty/loading/error states, keeping the decomposition clean so a new requirement lands in one place, and narrating my reasoning throughout. The mistake I'd avoid is racing to a working demo and assuming that's a pass, since plenty of working demos get rejected on everything else." Showing you understand that working code is the floor - and naming the differentiators - is itself a strong signal.

How to use this module

This lesson is the lens for everything that follows in Module 10. The next lesson turns this rubric into a working method - the concrete, repeatable approach (clarify → plan → decompose → build incrementally → handle edge cases → communicate throughout) that produces high scores on the differentiators rather than just clearing the floor. After that, the module builds the reusable building blocks every machine-coding problem draws on - component design and props APIs, config-driven UI, skeleton states, routing, state management, internationalization - each of which maps directly to a differentiator (decomposition, extensibility, edge cases, best practices). And then the module works through the canonical problems - autocomplete, infinite scroll, nested comments, carousels, accordions, pagination, real-time chat - applying the method and the building blocks to each, so that by the end you've not only seen the patterns but internalized the way of working that turns "it worked" into "you're hired." Read this lesson as the rubric; read everything after it as the training to ace it.

What you should carry out of this lesson

An LLD machine-coding round asks you to build a real, working frontend feature in 45–90 minutes - a simulation of day-to-day frontend engineering, distinct from algorithmic DSA rounds and architecture-focused HLD rounds. The defining truth is that functionality is the floor, not the ceiling: it's necessary (failing it is an automatic reject) but nowhere near sufficient, because the offer is decided by the differentiators - component decomposition (the highest-signal dimension), code quality, edge-case handling (empty/loading/error, a seniority signal), extensibility (deliberately probed by added requirements), communication (it's a collaboration simulation), requirements clarification and scoping, time management with incremental delivery, and best-practices awareness. Candidates get rejected despite working code through recurring failure modes: coding before clarifying, the god component, building everything and finishing nothing, only the happy path, silent coding, over-engineering, rigid designs that can't extend, and time mismanagement. The unifying lens is that the interviewer is asking "what's it like to build features with this person?", seniority is read off the differentiators, and the winning strategy is to reach a clean working core quickly so you can spend the bulk of the round on everything that actually moves the score.

With the rubric established, the next lesson makes it actionable: the working method for approaching any machine-coding problem - the repeatable sequence that systematically hits every differentiator, from the first clarifying question to the final edge-case pass - so that you're not just aware of what's scored but have a concrete process for scoring well on it.

Back to Frontend System Design