July 14 2025
Boundaries Between Feature, Shared, and Design System
What actually helps is being clear about what is feature-local code, what is shared, and what has become a stable contract.
Andrews Ribeiro
Founder & Engineer
4 min Intermediate Frontend
The problem
A lot of frontend structure becomes organizational theater.
You open the tree and see:
features/shared/design-system/
Everything looks serious.
But in real use:
sharedreceives anything nobody knows where to putdesign-systembecomes the home for product exceptions- one feature imports from another for convenience
In the end, the folders look better and the boundaries are still bad.
Mental model
Those names should not represent aesthetics.
They should represent different levels of contract.
A practical way to think about it:
- feature = code that exists to solve one specific product flow or case
- shared = reusable code across features, but not yet part of the central visual or system contract
- design system = tokens, patterns, and components with a stable contract and clear consistency responsibility
Short version:
the question is not “which folder can hold this?” The question is “what is the real scope of this code, and how stable does it need to be?”
Breaking the boundaries down
What usually stays in feature
Feature is where code stays when it is still tightly tied to the context of that flow.
Examples:
- checkout screen components
- filter-combination rules for that listing
- onboarding-specific copy
- state and composition from that domain
Even if the code looks clean.
Even if it might become reusable in the future.
If its value still depends on that context, keeping it local is usually healthier.
What usually goes to shared
Shared is the middle ground.
It serves things that:
- appear across several features
- have cross-cutting utility
- are still not part of the central UI contract
Examples:
- date helpers
- integration wrappers
- small reusable screen blocks
- cross-cutting hooks that do not belong to the design system
The risk here is turning shared into a dump.
If everything goes there, nothing has a clear owner.
What really deserves to be in the design system
The design system should carry what needs to be consistent, predictable, and stable.
Examples:
- tokens
- primitives
- base form components
- standardized visual feedback
- recurring accessibility and interaction rules
The classic mistake is promoting a product exception too early.
When that happens, the design system becomes a catalog of special cases with infinite props.
Simple example
Imagine a status badge.
Scenario 1:
- it only appears in one billing flow
- it follows rules specific to that context
- its meaning is still changing
That is probably feature.
Scenario 2:
- it appears across several internal screens
- it reuses simple behavior
- it still does not define central product language
That may be shared.
Scenario 3:
- it became a recurring visual pattern
- it needs to follow tokens and accessibility consistently
- it already has clear semantics and stable states
Now it starts to look like design system.
The test that usually helps
Before moving code, it helps to ask:
- is this specific to one flow?
- is this useful across several places without depending on the original domain?
- did this become a stable contract of interface and consistency?
Those answers usually point to a better home than any folder convention.
Common mistakes
- Sending to
sharedeverything that created local discomfort. - Pushing a premature component into the design system.
- Creating boundaries by cargo cult, with no decision rule.
- Letting one feature depend on another and calling that pragmatism.
How a senior thinks
People who look well at frontend architecture tend to be conservative about promoting code.
The healthy reasoning usually is:
- keep it local first
- then watch for real repetition
- only then promote to shared
- and only promote to design system when the contract is already stable
That avoids two bad costs:
- abstraction too early
- fake consistency based on an overly generic component
Interview angle
This topic shows up a lot in:
- frontend architecture
- system design focused on web product
- large codebase review
Weak answer:
I would separate things into feature, shared, and design system.
Strong answer:
- explains the promotion criteria
- shows that stability matters
- separates cross-cutting reuse from central contract
That is what communicates maturity.
Closing thought
Feature, shared, and design system only help when they represent real boundaries.
If they become only folder decoration, the team gets a prettier tree and an equally confusing codebase.
Good structure is not the one that looks organized. It is the one that makes clear what is still local, what already became cross-cutting utility, and what truly deserves a stable contract.
Quick summary
What to keep in your head
- Feature, shared, and design system represent different levels of scope and stability.
- When shared becomes a generic drawer, that is a symptom of weak boundaries, not a missing folder.
- A design system should carry stable contracts, not product-specific exceptions disguised as base components.
- When there is real doubt, the safer pattern is usually to keep code local first and promote it later.
Practice checklist
Use this when you answer
- Does this code exist for one specific feature, or for several flows with the same intent?
- Am I pushing a product exception into the design system too early?
- Is shared receiving clear utility, or only code with no owner?
- If I move this code, does the boundary get clearer or only prettier?
You finished this article
Share this page
Copy the link manually from the field below.