Note
How frontend architecture evolves as a team scales
A real-world case study on how frontend architecture evolves as a team grows — the honest version, including the tradeoffs nobody talks about.
Most articles about frontend architecture show you the destination: the clean folder structure, the mature design system, the fully-tested codebase. What they rarely show is the path — the messy, honest one, full of decisions that made sense at the time and stopped making sense later.
I put together a small open-source case study to walk through that path directly: how the architecture behind a real internal product evolved as the team behind it grew from two developers to a mid-size frontend team.
Same product, three very different stages
To make the evolution concrete, the case study follows one fictional product, FlowDesk, through three stages — or "eras" — each with its own team size, its own priorities, and its own architecture.
FlowDesk starts as a simple team directory. As the team behind it grows, so does the product: task management gets added, then notifications and scheduling. Each new feature is also what forces the architecture underneath it to change.
| Era | Team size | What FlowDesk gained | What architecture had to answer |
|---|---|---|---|
| Era 1 | 2 developers | A simple team directory | Minimal tooling, type-based structure, no ceremony |
| Era 2 | 3 developers | Task management | Component/prop sprawl, testing basics, Storybook, a late data-layer migration |
| Era 3 | Mid-to-large team | Notifications & scheduling | Type-based and feature-based structure coexisting on purpose |
Era 1: two people, zero ceremony
With two developers, communication overhead is close to zero. Conventions can live in people's heads instead of in tooling. This era is intentionally minimal: a type-based folder structure, no testing, no component documentation, direct fetch calls instead of a data-fetching library.
None of that is a gap — it's a deliberate choice. The cost of more structure wasn't justified yet.
Era 2: growing pains
At three people, things start to strain. Screen count and component count grow fast, often because product asks for "the same component, but with a few more props" instead of a new one. Under delivery pressure, components get built as decoupled as possible — without always knowing what future requirements will look like.
This era also introduces testing, but with clear, intentional boundaries: components and hooks get covered, page-level tests are deliberately left out. And a real win shows up too — every component ships with its own Storybook story, which ends up enabling components to be shared across teams. The catch: design tokens are still hardcoded in the app, so sharing a component often means manually adjusting them to fit.
Late in this era, a data-fetching library gets introduced — which means rewriting hooks that were already working. Migrating code that already works is always more expensive than it looks from the outside.
Era 3: coexistence, not a rewrite
By the time the team reaches a mid-size frontend org, a full rewrite isn't something the business can absorb — delivery doesn't stop for it. So instead of migrating everything at once, two structural patterns are allowed to coexist on purpose: the original type-based structure stays where it is, while new work adopts a feature-based structure from day one. Old code only moves into the new pattern when it's already being touched for an unrelated reason.
One friction point never fully resolves, even by this stage: testing coverage never quite catches up with how fast the team and codebase grow. It's one of the clearest examples in the whole case study of a tradeoff that doesn't resolve itself with more people or more structure — it just becomes a long-running cost.
Why I built this
Most resources on frontend architecture teach patterns in the abstract, disconnected from the team and business context that actually justified them. I wanted to show the opposite: architecture as a series of tradeoffs made under real constraints, including the ones that don't age perfectly.
The full case study, with illustrative code for each era, is open source and available on GitHub.