Skip to content

    Frontend System Design

    Frontend System Design: The Big Picture

    Learn what a front-end system is, how it differs from back-end design, and which layers and decisions a front-end architect owns, so you can follow the course.

    By Published 16 min read

    Share

    What you'll learn: By the end of this lesson you will be able to describe a front-end system as a set of layers between a person's device and the back end, explain five ways front-end design differs from back-end design, name the decisions a front-end architect makes, and see how this course and the back-end System Design course fit together.

    Think of a restaurant, but look at the dining room instead of the kitchen. In the kitchen you control the stoves, the staff and the schedule. In the dining room, guests arrive hungry or in a hurry, sit where they like, and leave halfway through if the food is slow. You cannot control them, and every guest is a little different.

    The front end is that dining room. It runs on someone else's device, over someone else's network, in front of a person who has no interest in how it works.

    The running example is an online store with a product page (photos, price, stock, an "Add to basket" button) and a checkout (address, payment, confirmation). Every later lesson returns to it.

    The core idea

    A front-end system is everything between a person's device and your back-end services: the code that runs in the browser, the network that carries it there, the servers and caches that deliver it, and the practices that keep it fast, safe and working after you ship it.

    Many developers hear "front end" and think "components and CSS", which is one layer. "The product page is slow" might come from a large image, a late server, a blocking script, a cache miss or a busy phone. Front-end system design means seeing all of these and deciding which you own.

    Figure 1. A front-end system spans the device, the network and delivery, and ends at the back-end APIs.

    A front-end system spans the device, the network and delivery, and ends at the back-end APIsShopper's device to Browser; Browser to Network (requests); Network to CDN and hosting (code); Network to Back-end APIs (data)A DEVICE YOU DO NOT CONTROLShopper's device: phone or laptopShopper'sdevicephone or laptopBrowser: runs your codeBrowserruns your codeNetwork: slow or offlineNetworkslow or offlineCDN and hosting: HTML, JS, CSSCDN andhostingHTML, JS, CSSBack-end APIs: data and rulesBack-end APIsdata and rulesrequestscodedata

    How to read this: the shaded zone is where your code runs and where you have the least control. Everything to its right is infrastructure you configure. The back-end APIs are where this course hands over to the System Design course.

    How front-end design differs from back-end design

    The two share goals: be fast, be correct, survive failure. The difference is the conditions. Five shape almost every front-end decision.

    1. The runtime is a device you do not control. Front-end code runs on whatever the shopper owns. At the time of writing the Baseline "core set" of browsers is Chrome, Edge, Firefox and Safari, built on different engines (Chromium, Gecko, WebKit), so the same code can behave a little differently. Baseline and MDN's compatibility tables say which features are safe.
    2. One copy of your code runs on many devices at once. A back-end release replaces code on your servers. A front-end release reaches whoever opens the page next, while people with the page already open keep the old version.
    3. There is one main thread. Most of your JavaScript, the work of building the page and the handling of taps share a single thread. While a task runs, the browser cannot respond to input. A page cannot add a second main thread.
    4. State is per person. A back end mostly manages shared data such as orders and stock. A front end also holds state that belongs to one tab: what is in the form, which tab is selected, what is loading.
    5. Failure is ordinary. Networks drop or slow down and scripts fail to load. You design so the page does something sensible when they do.
    Concern Back-end design Front-end design
    Where code runs Machines you choose and size Devices and browsers you do not control
    Release Replace code on your servers Ship to many users, some holding an old version
    Concurrency Many requests in parallel, scale out One main thread per page, work must be split up
    State Mostly shared data Shared data plus per-tab UI state
    Typical failure Overload, a dependency down Slow or offline network, script failure, old browser
    Visibility Your logs and metrics Only what the page sends back
    Trust You can enforce rules The device's owner can read and alter what runs there

    The last row matters. MDN's form validation guide says client-side validation is too easy to bypass to count as a security measure, so the server must validate too. The front end makes the experience good; the back end enforces the rules.

    A layered model of a front-end system

    This course uses one model to ask "where does this problem live?". It is a teaching tool, not a standard; teams draw the layers differently.

    Figure 2. The layers of a front-end system, with four concerns that cut across all of them.

    The layers of a front-end system and four concerns that cut across themDelivery to Network; Network to Browser runtime; Browser runtime to Application; Application to State and dataCROSS-CUTTING CONCERNSDelivery: build, deploy, CDNDeliverybuild, deploy,CDNNetwork: HTTP and cachingNetworkHTTP and cachingBrowser runtime: layout and paintBrowserruntimelayout and paintApplication: components, routingApplicationcomponents,routingState and data: fetch, cache, storeState and datafetch, cache,storePerformance: budgets, field dataPerformancebudgets, fielddataSecurity: origins, CSPSecurityorigins, CSPAccessibility: keyboard, readersAccessibilitykeyboard, readersObservability: errors, field dataObservabilityerrors, fielddata

    How to read this: the top row follows a page from the moment it is built to the moment it asks for data. The bottom row is not a step; each of those four concerns applies at every layer above it.

    Layer What lives there A question it raises on the store
    Delivery Build output, file names, CDN, release process How do we ship a price-display fix without breaking open tabs?
    Network HTTP requests, caching headers, connections Which files can the browser keep, and for how long?
    Browser runtime HTML parsing, layout, paint, the event loop Why does the page jump or freeze while images load?
    Application Components, routing, rendering strategy Is the product page built in the browser or on a server?
    State and data Where data is fetched, cached and held Who owns the basket: the page, the server, or both?

    Trace a symptom down the layers: "checkout feels slow" can be a large bundle (delivery), missing caching (network), a long task blocking taps (runtime), too much rendering (application) or sequential requests (data).

    One page load, end to end

    Here is what happens when a shopper opens a product page in a client-rendered design. Later lessons explain each step.

    Figure 3. Loading a product page and adding an item to the basket.

    sequenceDiagram accTitle: Loading a product page and adding an item to the basket accDescr: The browser requests the page from the CDN, receives HTML, then requests the script and style files, runs the script, fetches product data from the API, renders it, and later posts an add-to-basket request. participant S as Shopper participant B as Browser participant C as CDN participant A as Product API S->>B: open /product/42 B->>C: GET /product/42 C-->>B: HTML document Note over B: parse HTML, find scripts
    and stylesheets B->>C: GET app and style files C-->>B: JS and CSS Note over B: run JS on the main thread B->>A: GET /api/products/42 A-->>B: JSON (name, price, stock) Note over B: build the page,
    lay out, paint S->>B: tap Add to basket B->>A: POST /api/basket A-->>B: updated basket

    How to read this: time runs downward. In this design the shopper sees nothing useful until the browser has the HTML, the files it references and the data. Each arrow can be slow or fail, and each note is work on the one main thread.

    The order is a choice, not a law. Some products build the HTML on a server so the first response already contains the product; others generate it ahead of time. web.dev's Rendering on the Web names the options (client-side, server-side and static rendering, and hybrids) and their trade-offs; the Rendering Strategies lesson covers them.

    The decisions a front-end architect makes

    You will meet these eight decisions repeatedly. Each is a trade-off, and each is touched by a lesson in this course or a planned later course.

    Decision The question Where it is covered
    Rendering strategy Where and when is the HTML produced: in the browser, on a server per request, or ahead of time? Rendering Strategies (CSR, SSR, SSG, ISR, streaming)
    Data fetching and caching What does the page request, when, and which copies may be reused? HTTP Caching for the Front End; the planned data and APIs course
    State ownership Does a piece of state live in the URL, a component, a client store or the server? The planned React and state course
    Code splitting and shipping How is code divided, and what must the first visit download? HTTP Caching for the Front End (shipping hashed files); Loading Performance and Core Web Vitals (how splitting affects LCP and INP)
    Performance budget What limits do we set, and who is told when a change breaks them? Loading Performance and Core Web Vitals
    Accessibility Can a person with a keyboard or screen reader complete checkout? Frontend Requirements: Users, Devices and Networks; the planned case studies
    Security boundary What may run in the page, and what does the server check? The planned security course
    Observability How will we learn something broke on a device we cannot see? Loading Performance and Core Web Vitals; the planned later courses

    Two terms. A performance budget is, in web.dev's words, a set of limits imposed on metrics that affect site performance; the limits can be on quantities (script size), milestone timings (First Contentful Paint) or tool scores (Lighthouse). Accessibility is, in MDN's definition, the practice of making websites usable by as many people as possible, which also helps people on mobile devices or slow connections.

    Why one main thread matters

    MDN's event loop article explains that each job runs to completion before the next starts, and that a job that runs too long stops the page processing clicks and scrolling. web.dev defines a long task as one that runs longer than 50 milliseconds and says it can delay a tap's visible response until the task finishes. That ties to INP (Interaction to Next Paint), one of the three Core Web Vitals; the others are LCP (Largest Contentful Paint, loading) and CLS (Cumulative Layout Shift, visual stability). web.dev describes these as primarily field metrics, measured on real users' devices.

    Web Workers run scripts in background threads but cannot touch the DOM directly; they talk to the page by messages. The Event Loop and the Main Thread lesson covers splitting work up.

    Shipping to devices that hold old copies

    After a deployment, nobody's page changes by itself. A tab opened an hour ago keeps running the code it downloaded an hour ago, and caches may keep files longer.

    MDN's HTTP caching guide describes the standard approach: put a version or hash in the file name and send a long lifetime, so a new build gets a new URL and nothing stale is reused. The HTML document cannot have a changing name, so it is sent so the browser revalidates it. The two cases look like this.

    Cache-Control: no-cache

    For the HTML document: the browser may store it but must check with the server before reusing it.

    Cache-Control: public, max-age=31536000, immutable

    For a hashed file such as /assets/app.3f9a1c.js: keep it for a year (31,536,000 seconds) and do not revalidate on reload.

    That fixes the files, not the open tab. Old code in that tab can request a file the new deployment no longer serves, or send a request in a shape the new API rejects.

    Figure 4. An open tab that outlives a deployment.

    sequenceDiagram accTitle: An open tab that outlives a deployment accDescr: A tab loads version 1 of the app. A deployment then publishes version 2 and removes the version 1 files. When the old tab later requests a version 1 file, the host returns not found and the old code fails. participant T as Open tab (version 1) participant H as Hosting and CDN participant D as Deploy pipeline T->>H: load page and version 1 files H-->>T: HTML and JS D->>H: publish version 2, remove version 1 files Note over T: shopper keeps browsing
    still running version 1 T->>H: request a version 1 file it needs now H-->>T: 404 not found Note over T: the load fails and
    the feature breaks

    How to read this: the failure happens long after the deployment, in a tab it never touched. A front-end release process must plan for the period when two versions are live.

    Service workers add a related case: MDN says a newly installed version waits while the old one keeps controlling open pages. Any layer that caches your code can keep an old version alive.

    What you cannot see unless you send it

    On the back end you see requests in your logs. On the front end, an exception on a shopper's phone is invisible unless the page reports it. A small reporter is the first piece of observability.

    function report(kind, detail) {
      const body = JSON.stringify({
        kind,
        detail,
        page: location.pathname,
        release: "2026-11-07.1", // illustrative build identifier
      });
      // Queues a small POST; true means queued, not received.
      return navigator.sendBeacon("/telemetry/errors", body);
    }
    
    window.addEventListener("error", (event) => {
      report("error", { message: event.message, file: event.filename, line: event.lineno });
    });
    
    window.addEventListener("unhandledrejection", (event) => {
      report("rejection", { reason: String(event.reason) });
    });

    error fires when a script throws and nothing catches it; unhandledrejection fires when a promise is rejected and nothing handles it. MDN describes navigator.sendBeacon() as an asynchronous POST for analytics and diagnostics, limited to about 64 KiB of queued data. The release identifier tells you which version produced an error, which matters when two versions are live.

    How this course is organised

    This is the first lesson of the Frontend System Design course. The first course covers the foundations: how a browser turns bytes into pixels, how the single thread schedules work, how HTTP caching behaves, how rendering strategies compare, how loading performance is measured, and how to turn users, devices and networks into requirements. Its lessons are How a Browser Actually Works, The Critical Rendering Path, The Event Loop and the Main Thread, HTTP Caching for the Front End, Rendering Strategies, Loading Performance and Core Web Vitals, and Frontend Requirements: Users, Devices and Networks.

    Figure 5. The planned shape of the Frontend System Design series.

    flowchart LR accTitle: Planned shape of the Frontend System Design series accDescr: The first course covers browser, rendering and performance foundations. Planned later courses cover React and state, data and APIs, security, large-scale and enterprise front ends, and case studies. A["Course 1
    Browser, rendering
    and performance"] --> B["Planned
    React and state"] B --> C["Planned
    Data and APIs"] C --> D["Planned
    Security"] D --> E["Planned
    Large-scale and
    enterprise"] E --> F["Planned
    Case studies"]

    How to read this: only the first box exists today. The later boxes are plans, and their order and content may change.

    How it connects to the back-end System Design course

    The front end and back end meet at HTTP. This course links to the back-end lessons instead of repeating them.

    When you are thinking about... Read this back-end lesson
    The whole system and the three questions every design starts with System Design: The Big Picture
    What happens to a request after the browser sends it Request lifecycle
    Cache layers, TTLs and invalidation Caching basics
    Serving static files near users CDN and object storage
    The shape of the APIs the front end calls API design
    Turning a vague brief into measurable goals Requirements

    The habit is the same on both sides: start from requirements, name the trade-off, measure the result.

    In practice: redesigning an online store's product page and checkout

    NOTE

    A realistic scenario, not a case history. This is a composite example built from common patterns. It does not describe any real company, and every detail in it is an illustrative assumption.

    The situation

    An online store has a product page and a three-step checkout, built as one client-rendered single-page application. Five front-end engineers own it; a separate team owns the catalogue and checkout APIs. Product managers report lower conversion on phones than on laptops, and support tickets mention a blank screen while the page loads. The page also loads two third-party scripts (analytics and a chat widget) that the team does not control. There are no front-end error reports and no real-user performance data; the team tests on an office laptop.

    Starting architecture

    Figure 6. The starting point: one client-rendered application and two third-party scripts.

    Starting architecture: one client-rendered application and two third-party scriptsShopper to CDN (open page); CDN to Single-page app (one big bundle); Third-party scripts to Single-page app (also loaded); Single-page app to Catalog API (product data); Single-page app to Checkout API (checkout calls)Shopper: on a phoneShopperon a phoneCDN: shell and bundleCDNshell and bundleSingle-page app: runs in browserSingle-pageappruns in browserThird-party scripts: analytics, chatThird-partyscriptsanalytics, chatCatalog APICatalog APICheckout APICheckout APIopen pageone big bundlealso loadedproduct datacheckout calls

    How to read this: the shopper sees nothing meaningful until the bundle has downloaded and run and the product data has returned. The third-party scripts compete for the same main thread.

    Options considered

    Option What it fixes What it costs Risk
    Keep the application and trim the bundle Download and run time Code-splitting effort Smaller gain: the page still waits for data after the code runs
    Build product-page HTML on a server or ahead of time; keep checkout client-rendered First view of product pages A rendering server or build step; two rendering modes More moving parts; caching and personalisation rules must be right
    Rewrite on a new framework Possibly everything Months of work High: a long period with no user benefit

    The decision

    Field Decision record
    Context Product pages are the entry point and are mostly the same for every shopper; checkout is personal and interactive. Nobody can say how the page performs on real phones.
    Decision Add field measurement and error reporting first. Then render product pages ahead of time or on a server, split code so checkout loads only when needed, and check a performance budget in the build. Keep checkout client-rendered.
    Consequences, good The first view no longer waits for the whole application; regressions are caught before release.
    Consequences, bad Two rendering paths to maintain; price and stock on a pre-built page can be stale unless refetched in the browser.
    Revisit when Field data meets its goals for several weeks, or stale-price complaints pass an agreed level.

    Measurement comes first on purpose: without field data the team would be choosing between options based on a laptop.

    Rollout and verification

    Stage one ships the error reporter above, with a release identifier, plus Core Web Vitals collection in production, to get a baseline. Stage two adds the budget to the build. Stage three releases the new product page behind a flag to a small share of visitors and compares the two groups on LCP, INP, CLS, error rate and checkout completion. The flag widens only while the numbers hold; turning it off is the rollback.

    The 2 a.m. failure

    A late release publishes a new build and, in its cleanup step, deletes the previous build's files. Shoppers who opened checkout before the release tap "Continue". The old code requests a step file that no longer exists, the host answers 404, and the dynamic import() in the old code rejects (MDN says it does so when the module fetch fails), so the step never appears. This is Figure 4.

    Detection: the reporter shows a spike of rejection events tagged with the previous release, and checkout completion drops while the new release's numbers look normal. First checks: the release tag on the failing reports, the failing URL (a hashed file from the old build) and the cleanup step in the deployment log. Mitigation: restore the old build's files, which fixes open tabs at once. Follow-up: keep old files longer than a typical session, catch import failures and ask the shopper to reload, and add the rule to the release checklist.

    Architect's notes

    • Measure before you choose. Real-user data tells you whether the problem is code size, waiting for data or third-party scripts.
    • Decide early: the rendering approach per page type, where each kind of state lives, the performance budget and how old tabs are handled. These are costly to change later.
    • Design for the slowest device and network you support, and test there.
    • Treat third-party scripts as dependencies. They run on your main thread and can fail; name an owner for each.
    • Keep the trust line clear. The browser improves the experience; the server enforces the rules.

    Common mistakes

    • Treating the front end as only the UI layer. Delivery, caching and observability often explain the problem.
    • Judging performance on a developer laptop. Use field data from real devices.
    • Assuming one version of the code is live. Old tabs and cached files keep running after a release.
    • Trusting the client. Browser validation is for experience; the server must check again.

    Key takeaways

    • A front-end system is everything between the device and the back end: code, network, delivery and the practices that keep it healthy.
    • The runtime is a device you do not control, so design for the slowest and oldest devices you intend to support.
    • One main thread runs most of your code and handles input; long tasks make a page feel broken.
    • A release does not replace what users already have, so two versions can be live at once.
    • State is both shared and per tab; deciding where each piece lives is a design decision.
    • The eight decisions: rendering strategy, data fetching and caching, state ownership, code shipping, performance budget, accessibility, security boundary, observability.
    • Measure in the field, because you cannot see the device unless the page tells you.

    Practice exercises

    Exercise 1 - Starter (20 min): Pick a site you use daily. Describe what happens between your device and its back end when you open its home page, using the five layers of Figure 2 as headings. Mark any layer you cannot describe.

    Exercise 2 - Intermediate (1 hour): In your browser's developer tools, open the network panel on a product or article page. Group the downloaded files by the layer whose decision produced them, then find the caching headers on three files and judge them against the MDN caching guide.

    Exercise 3 - Intermediate (2 hours): Add the error reporter to a small page with a /telemetry/errors endpoint that logs the body. Trigger a thrown error and a rejected promise and confirm both arrive.

    Exercise 4 - Advanced (half day): Write a one-page decision record for the store assuming most shoppers are on slow networks. Choose an option, justify it, and list three measurements to collect before and after.

    In this course:

    Elsewhere on the site:

    Official documentation

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments