What FSD Actually Fixed (and What It Didn't)

Sep 09, 2026
10 min read
What FSD Actually Fixed (and What It Didn't)

For a long time, "frontend architecture" was almost a contradiction in terms. Backend had layers, domains, bounded contexts, decades of literature. Frontend had components/, utils/, and a folder called helpers that everyone was quietly afraid to open. The general vibe, even among good engineers, was that the frontend didn't need real architecture — it was just where you rendered the thing the backend actually built.

Feature-Sliced Design showed up into that vacuum, and I think that context matters more than the methodology's specific rules. FSD didn't just propose a folder convention. It made an argument: the frontend is complex enough, and grows unpredictably enough, that it deserves the same seriousness backend systems get. That argument is the actual contribution, and it's the reason FSD spread as fast as it did — not the specific layer names.

FSD's real achievement wasn't a folder structure. It was convincing an entire ecosystem that the frontend was worth architecting at all.

I've written before about how following FSD's rules literally, without thinking, produced one of the worse codebases I've built — a single button smeared across ten folders, each one "correctly" following the layer it was assigned to. That article was about the failure mode. This one is about something narrower: which problem FSD was actually solving, and where the credit for "clean architecture" quietly gets misattributed to a folder convention that never promised it.

What FSD actually fixed

Three things, and they're real.

It gave teams a shared vocabulary for growth. Before FSD, "where does this go" was answered differently by every senior engineer on the team, usually based on whatever they'd seen at their last job. FSD gives you pages, features, entities, shared — not perfect names, but names everyone on a team can agree mean the same thing. That alone kills a huge amount of bikeshedding.

It made import direction a rule instead of a convention. The layer hierarchy — shared can't import from entities, entities can't import from features, and so on — is enforceable with a lint rule. That's the part I actually think is underrated. Most frontend codebases don't have architectural violations because someone made a bad call; they have them because nothing stopped the bad call from compiling. FSD turned "please don't import upward" from a code review comment into a build failure.

It gave "the frontend has architecture" a name people could put on a job posting. This sounds cynical, but I mean it as a genuine point: before FSD (and a handful of contemporaries), it was hard to even have the conversation about frontend architecture in a hiring or planning context, because there wasn't a shared reference point. Now there is. That's a real, if unglamorous, win.

What it never promised to fix

Here's where I think people — including a version of me, a few years back — get it wrong. FSD tells you where a piece of code should live relative to other pieces. It does not tell you whether the pieces you're splitting apart should have been split apart in the first place.

That distinction matters because the smeared-component failure isn't a bug in FSD. It's what happens when you use a layering convention to answer a question it was never designed to answer: how much of a single feature's logic should live together versus be distributed. FSD's layers describe horizontal concerns — what kind of thing is this (a hook, a type, an API call) — and following them faithfully will happily scatter every piece of a single vertical concern (one feature, one component, one story) across the whole hierarchy, because each piece genuinely does belong to a different layer by FSD's own rules.

// "Correct" FSD placement for one payment-method component src/ features/payment-method/model.ts // the hook and state entities/payment-method/types.ts // the type shared/hooks/useDebounce.ts // a helper it needs shared/api/paymentMethods.ts // the fetch call features/analytics/paymentTracking.ts // its analytics event

Every one of those placements passes an FSD lint rule. None of them help the next engineer who needs to change how the payment method selector behaves, because understanding it now requires reconstructing it from five files across three layers, in a codebase that will tell you, correctly, that nothing is architecturally wrong.

FSD also doesn't fix cohesion at the page level, and it doesn't fix team discipline. A team that argues about everything else will still argue about whether a given piece of logic is a feature or an entity — FSD gives you a vocabulary for the argument, not an answer to it. And a team with no code review discipline will produce a messy FSD codebase exactly as fast as it would have produced a messy flat one; the folders will just be tidier while it happens.

The credit-misattribution problem

I think this is the actual thing worth naming: teams that adopt FSD often experience a real improvement in their codebase, and then attribute all of it to the layering. Some of the improvement is the layering. A meaningful chunk of it is something else entirely — the simple fact that adopting any deliberate structure, discussed and agreed on as a team, forces the conversations that were previously being skipped. "Where does this go" gets asked out loud instead of guessed at. That conversation is valuable regardless of which methodology prompted it.

Which means the honest version of "we adopted FSD and our codebase got better" is often "we adopted a shared convention, discussed it as a team, and enforced it with tooling — and FSD happened to be the convention we picked." That's still a win. It's just a different, more general win than "FSD solved our architecture problem," and conflating the two is how teams end up surprised when FSD alone doesn't prevent a smeared component, a bloated shared/ folder, or a pages/ layer that's turned into a dumping ground.

What I actually reach for now

I still use FSD's structure as a starting point — the version I use in practice keeps the layer names but treats them as a coarse sort, not a mandate to split every feature into its smallest constituent parts. The rule I actually enforce is closer to: a feature's pieces get distributed across layers only when another feature genuinely needs to share them. Until then, they stay together, even if that means a features/payment-method/ folder that internally holds its hook, its types, and its API call in one place, technically "violating" the purist reading of the layer boundaries.

That's not a rejection of FSD. It's treating it as what it actually is: a good answer to "how do we agree on where things go as this project grows," and a tool that says nothing useful about "how much should stay together." Knowing which question you're asking is the part that doesn't come in the docs.

If your team adopted FSD and it didn't fix the thing you hoped it would, I'd genuinely ask which of the two problems you actually had — because they need different fixes, and only one of them is solved by a folder structure.

Telegram

More than a blog post

I share frontend news and the reasoning behind it throughout the day. Pick the language that feels natural to you.

Need to discuss your project? Get in touch.