Frontend
Lesson 8 of 8About 3 min readSuggest an edit

Frontend architecture at scale

A frontend that twenty people change every day needs architecture. The main problem is no longer rendering; it is letting teams work in parallel without breaking each other, while keeping the product fast and consistent.

Module boundaries and feature folders

Organising by technical type (components/, hooks/, utils/) scatters one feature across the whole tree. Organising by feature keeps code that changes together in one place:

src/
  features/
    billing/
      components/
      api.ts
      index.ts      <- the only public entry point
    search/
  shared/
    ui/             <- design system components
    lib/            <- framework-free helpers
  app/              <- routes, layout, providers

Each feature exposes a small public API through its index.ts. Other features never reach into its internal files. Dependencies point one way: app depends on features, features depend on shared, and shared depends on nothing in the app.

Rules nobody enforces decay, so encode them in lint:

// eslint.config.js
rules: {
  "no-restricted-imports": ["error", {
    patterns: ["@/features/*/*"],
  }],
}

Design systems and tokens

A design system is a shared library of components plus the rules for using them. It puts accessibility and behaviour in one place where they can be fixed once.

Build it on design tokens: named values for colour, spacing, type and radius, usually exposed as CSS custom properties. Keep two layers. Primitive tokens name raw values (--blue-600); semantic tokens name purposes (--color-action). Components use only semantic tokens, so themes and dark mode only remap values. Treat it as a product with owners and versioning.

State ownership

Most “state management” problems are really unclear ownership. Sort state by where its truth lives:

Kind Source of truth Typical home
Server data The backend A query cache
URL state The address bar Router params, search params
Form state The form, until submit A form library or local state
UI state One component or subtree Local state, lifted as needed

Server data is a cache of someone else’s state; don’t copy it into a global store. Filters and pagination belong in the URL so they survive reloads and can be shared. The global client state left over is usually small.

Monorepos

A monorepo keeps apps and shared packages in one repository, with workspaces (npm, pnpm or Yarn) and a task runner such as Nx or Turborepo. The benefit is atomic change: you can update a shared component and every consumer in one pull request. The cost is build time and blast radius, so you need caching, “affected only” builds and tests, and ownership rules for shared code.

Micro-frontends

Micro-frontends split one product into separately built and deployed frontends owned by different teams, composed at build time, on the server or in the browser. They solve an organisational problem: teams that must release independently. They bring real costs: duplicated dependencies, inconsistent UX, harder performance work and more complex local development. If your teams can share one deploy pipeline, a well-bounded modular monolith usually gives most of the benefit for far less cost.

Performance and bundle ownership

Bundle size grows one reasonable dependency at a time. Give each route or feature a size budget checked in CI, show the size difference on every pull request, and make a team responsible for each entry point, for example through a CODEOWNERS file.

Recording decisions

An architecture decision record (ADR) is a short Markdown file stored with the code: context, the decision, alternatives considered and consequences. Write one when a choice is expensive to reverse, such as a state library, a styling approach or adopting micro-frontends. Later, anyone can revisit the decision knowing what it was meant to solve.

How to decide

  • Start with feature folders and lint-enforced import rules.
  • Put shared UI in a design system built on semantic tokens.
  • Keep server data in a query cache and shareable state in the URL.
  • Choose a monorepo for shared code; choose micro-frontends only for independent deployment needs.
  • Give bundles owners and budgets.
  • Write an ADR for anything you would have to explain twice.

Test what you just read

The Frontend assessment picks questions near your level and explains every answer.