Core Concepts

This page is the map of everything this guide covers. Each concept gets a short overview here: what it is and the one decision it turns on, so you can answer a basic question from this page alone and open the deep dive when you need the detail.

They are grouped by the part of the Delivery Framework where they usually come up.

Tooling and code organization

Where the code lives and how it ships. This stays short unless the question involves many teams or many apps.

Monoliths

A monolith is one codebase that builds and deploys as one unit. Every page and every shared component sits together, one pipeline ships the whole thing, and nothing has to be coordinated across repositories. It is the right starting point for almost every frontend. The successful microservice stories nearly all began as a monolith that grew too big and got broken up, and the systems built as many small pieces from day one usually ended up in trouble. The article covers the other half of the story: the problems that appear as the codebase and the number of teams grow, so you can name them before the interviewer does.

Monorepos

A monorepo keeps many apps and the libraries they share in one repository, with one dependency install and one build graph. The payoff is coordination. A change to a shared component and every app that uses it land in a single commit, instead of publishing a new version and bumping it app by app while the apps slowly drift apart. That gives you the rule: the less version drift you can tolerate between your apps and your shared code, the more a monorepo pays off. If the apps genuinely want to move at different speeds, that coordination stops being a benefit.

Microfrontends

Microfrontends split one frontend into remotes that separate teams build, test, and deploy on their own, then compose back into a single page for the user. The article covers six ways to do that composition, from build-time packages to stitching remotes together at runtime. The decision rule matters more than the mechanism: adopt microfrontends for team structure and release independence, never because the technology is interesting. If all the pieces still have to release together, you have a modular monolith with extra steps, and the plain modular monolith is cheaper to run.

Choosing a Repo

Choosing a repo untangles the two questions candidates mix up. Monolith versus microfrontends is about how the product ships. Monorepo versus many repos is about how the code is stored and built. Those are independent, so "microfrontends or a monorepo" is not a real choice: you can have both, either, or neither. Microfrontends solve deployment and ownership problems, monorepos solve collaboration and code management problems. Most teams should start with a modular monolith, often in a monorepo, and only pay for microfrontends when team boundaries and release independence justify the cost.

Microfrontend Perf Problems

Once a page is stitched together at runtime from independently shipped remotes, a set of performance problems appears that a single app never has. The same library ships two or three times because each remote bundled its own copy. Requests queue behind each other as one remote loads and then asks for the next. Hydration piles up as every remote tries to become interactive at once. These are usually dependency governance problems rather than rendering problems, so when the page gets slower, check what got duplicated before you start profiling components.

Styling Approaches

Styling approaches compares plain CSS, CSS Modules, Tailwind, and CSS-in-JS in both its runtime and zero-runtime forms. Three axes decide it: scoping (can a class leak into a component that never asked for it), runtime cost (is CSS being generated in the browser while the user waits), and whether the approach works inside React Server Components, which rules some options out entirely. This is also the step where personal taste runs strongest. The answer that holds up is one a team could agree on for this product, not the one you personally enjoy writing.

API Design

API design turns a vague product into concrete data: the entities on the screen, the endpoints that serve them, and the exact shape of what comes back. Three rules carry the whole step. Shape every response for the screen that renders it, so the client is not stitching four calls together to draw one row. Paginate every list, because every list grows. Make every risky write safe to retry, so a flaky network cannot charge someone twice. The article also covers error shapes, which is what turns a failure into something the interface can actually show. Define this contract early, because data fetching, data sending, and performance all build on it.

Future-Proof Components

Future-proof components walks one sidebar component through everything that breaks it: server rendering, hydration, two instances on the same page, being moved into a portal, and React features that do not exist yet. Each fix removes an assumption the component was quietly making, such as "the browser exists when I run" or "there is only one of me". That is the idea worth carrying into an interview: a component is finished when it works on pages you have never seen, not when it works on yours.

State Management Tools

State management tools is about choosing between React on its own, a global store like Redux, and a server-state library that caches what the server owns. The move is to match the tool to the kind of state. Separate the data that came from the server from the state only the UI cares about, such as whether a menu is open. Most state problems in a modern app turn out to be server-state problems, and React plus a server-state tool handles those better than a global store will. Reach for the store when you have genuinely shared client state, not by default.

External Dependencies

Installing a package hands the designing, writing, testing, and maintaining of that code to someone else, often someone you have never heard of, and their failures become yours because your program now runs their code. External dependencies gives the checks that tell you a package is worth that: what to look at beyond stars and download counts, how to try a candidate before you commit to it, and what you still owe a dependency after it ships, because installing it is the start of the work rather than the end.

Data fetching

How data gets into the app: the first load, what comes later, and how live updates arrive.

Real-Time Transports

Plain HTTP is request then response. The browser asks, the server answers, the connection closes, and the server has no way to speak first. Real-time transports puts the three ways around that side by side. Long polling reopens a request each time one closes. Server Sent Events holds a single response open and streams into it. WebSocket opens a two-way connection either side can write to. Each costs something different in requests, server memory, and complexity, and the article runs the same chat over all three at once so you can watch which one delivers first.

Server Sent Events

Server Sent Events is the browser's built-in way to stream from server to client. The page opens one ordinary GET request, the server never closes it, and small text messages get written down that same response as things happen. The EventSource object opens the stream, parses the messages, and reconnects on its own when the connection drops, which is most of why people choose it. It flows one way only, so anything the client sends goes over a normal POST. It fits notifications, live dashboards, and an AI answer arriving word by word.

Broadcast Channels

Two tabs of the same app do not know about each other, and that turns into real bugs. A user logs out in one tab and the other still shows their account. They add to the cart in one and see the old cart in the other. BroadcastChannel fixes it by letting tabs, windows, iframes, and workers of the same site message each other directly in the browser, with no server in between. One thing to know before you propose it: it carries no history, so a tab opened after a message was sent hears nothing and still needs its starting state from somewhere else.

Optimistic Updates

An optimistic update changes the screen immediately, as if the server had already said yes, and sends the request in the background. It takes a full round trip out of every tap, which is the difference between an interface that feels instant and one that feels like it is thinking. It is a bet, so only take it when you can pay it back: snapshot the previous state before you change anything, restore that exact snapshot if the request fails, and tell the user when you do. Skip the bet when a false yes is expensive, such as a payment or a delete you cannot undo.

Performance

Where this product gets slow at real scale, and what you do about it.

Performance at Scale

On a landing page you optimize the first paint. On an app people keep open all day you also have to optimize the thousandth interaction and the last hour before someone logs off. Both are performance and they fail in different ways. Performance at scale covers both halves: Core Web Vitals and bundle size for the first load, then rendering work, caching, memory hygiene, and moving expensive work into a Web Worker for the long session that follows.

Virtualization

Rendering a 50,000 row grid the naive way creates 50,000 DOM nodes, and the browser spends its time laying out, painting, and reflowing rows nobody can see. Virtualization renders only the rows inside the viewport plus a small buffer above and below, so the node count stays small however long the list gets. It is the standard answer to "this list has a hundred thousand items", and it is worth knowing what it costs: row heights, scrollbar accuracy, and find-in-page all get harder once the rows are not really there.

Performance Budgets

A performance budget is a limit you enforce, not a goal you hope for. Without enforcement every app drifts in one direction, which is slower, because no single pull request ever looks like the problem. Every real budget has three parts: a number, a place where that number is checked automatically such as CI, and something that happens when it is crossed, usually a failed build. If any of the three is missing it is not a budget, it is a dashboard, and a dashboard has never stopped a regression from shipping.

Testing

What you would test, and at which layer.

Testing Strategy

Testing strategy is how you divide the work across layers: the pyramid, the trophy, and the honeycomb, what each one assumes, and which fits the product you just designed. It also covers picking a runner and deciding what belongs in a unit test rather than an integration test or a full browser run. Underneath the shapes is one rule worth saying out loud: set the project up so that writing and running a test is easier than clicking through the app by hand, because whichever one is easier is what people will actually do.

Specialized Testing Patterns

Specialized testing patterns covers the tests tutorials skip and real products need. A permission matrix maps every role to every action, and the cases that matter are the negative ones: what a viewer should not be able to do. Multi-tenant isolation checks that one customer's data can never surface in another customer's account. Internationalization catches layouts that break in a longer language or a right to left one. Accessibility checks run as tests instead of as a pass someone does at the end.

AI & LLM Integration

AI and LLM integration works one shopping assistant end to end, and the decisions generalize to any LLM feature. Build it behind a server you own and most of them fall out of that single choice: the server holds the API key, streams the reply back as it arrives, checks every action the model wants to take before it runs, grounds answers in your own product data instead of the model's memory, and falls back to the plain store experience when the model cannot help. The model is the interesting part, but the server is where the product lives.

Remember

This page is a map, not a script. Read the concept when you reach its part of the design, so that when the interviewer asks why, you have an answer that holds up.