Front-end · Senior

Front-end architecture: componentization, design systems and monorepos

In a small app, any structure works. The problem shows up when the team grows, screens multiply and every change seems to break three distant things. Front-end architecture is the set of decisions that keeps a large app understandable, consistent and cheap to evolve — and it's what a senior is expected to lead.

Componentization: from atom to page

The foundation of everything is breaking the UI into reusable pieces. Atomic design offers a useful vocabulary: atoms (button, input), molecules (search field = input + button), organisms (a complete header), templates and pages. It's not dogma — it's a way of thinking about composition: small, dumb pieces at the bottom, orchestrating pieces on top.

A good component does one thing and doesn't know where it will be used. The moment it "knows" the screen that contains it, it has become coupling disguised as reuse.

Design system and design tokens

A design system is the shared library of components + usage rules that ensures buttons, colors and spacing are the same across the whole product. At its heart are design tokens: named variables for design values (--color-primary, --space-md). Changing the entire brand becomes changing tokens, not hunting hex codes through the codebase.

:root {
  --color-primary: #4f46e5;
  --space-md: 16px;
}
.btn { background: var(--color-primary); padding: var(--space-md); }

Organize by feature, not by type

Many projects collapse by organizing by file type (one giant folder of components, another of hooks, another of utils). At scale, organize by feature: everything that belongs to "checkout" — components, state, API calls, tests — lives together. That way you read a feature in one place, and deleting it means deleting a folder.

src/
  features/
    checkout/   (components, hooks, api, tests)
    perfil/
  shared/       (design system, generic utils)

Choosing state management

There is no "best" — there's the one that fits the nature of the data. Server state (API data) calls for caching tools like React Query. Local UI state stays in the component. Global client state (theme, session) calls for Context or a lightweight store. The typical mistake of the novice senior is throwing everything into a giant Redux. Separate by type of state and the complexity plummets.

Module boundaries

What keeps a large app healthy are clear boundaries: each feature exposes a public API (an index) and hides the rest. Other features import only what was exposed. Like apartments in a building — you use the common hallway, you don't walk through your neighbor's living room. This lets you refactor the inside of a module without breaking the world.

The monorepo trade-off

A monorepo means keeping several packages/apps in a single repository. It has real advantages, but real costs too:

AdvantagesCosts
Easy shared codeMore complex build tooling
Atomic refactoring (one PR)CI gets slow without smart caching
Single version, no driftLarge repository, more governance

The decision is contextual: small teams gain little and pay for the tooling; organizations with lots of shared code gain a lot. Senior interviews often ask you to defend exactly this choice — and what's expected isn't the "right" answer, it's the reasoning about trade-offs.

How to structure a large app, in short

  • Small, decoupled components, composed from the bottom up.
  • Design system + tokens as the single source of visual truth.
  • Organize by feature, with explicit module boundaries.
  • State segmented by type (server, UI, global).
  • Monorepo only when the sharing gains outweigh the tooling cost.

Conclusion

Front-end architecture is about managing coupling: small pieces, clear boundaries, segmented state and visuals centralized in tokens. Decisions like monorepo are trade-offs, not truths. With a solid structure, the next senior conversation is delivering it fast to the user.

Read also

Back to track