Skip to content

refactor(web,docs): adopt Feature-Sliced Design in both frontend apps - #33

Merged
suiramdev merged 1 commit into
devfrom
refactor/feature-sliced-design
Sep 19, 2026
Merged

suiramdev merged 1 commit into
devfrom
refactor/feature-sliced-design

Conversation

@suiramdev

Copy link
Copy Markdown
Owner

Summary

apps/web/src and apps/fumadocs/src are now Feature-Sliced Design roots: app, pages, features, entities, shared, one public API per slice, and imports that only ever point down a layer. Route files hold routing alone — apps/web goes from 1620 lines of route file to 302, with each screen's composition, queries and handlers in the page slice that owns them. Steiger enforces the result as a new Architecture step in CI. Start with apps/web/src/pages/budget/ and apps/web/AGENTS.md.

Motivation

  • components/, hooks/, lib/ and utils/ grouped code by what it is, so a screen's pieces sat in four folders and nothing said which of them could import which.
  • Route files carried composition, queries and handlers, so a screen could not be read or moved without its router entry.
  • Nothing mechanical stopped a shared helper from importing a page, and two such edges already existed.
  • Doing it now costs one rebase; doing it after the next feature costs that feature too.

Drawbacks

  • One commit moves 248 files, so it conflicts with anything in flight and makes git log on a moved file need --follow.
  • A slice's index.ts adds a hop between a call site and the symbol, which is the cost of having a public API at all.
  • bun run lint:fsd must run from the app directory — Steiger reads steiger.config.ts only when that file's directory is the working directory, and silently uses its own defaults otherwise.
  • Only apps/web and apps/fumadocs are roots; the packages keep their own layout, so the repository now has two conventions rather than one.

Prior art

  • FSD v2.1 as documented at fsd.how, with Steiger as the linter its authors ship.
  • The page-slice split follows the methodology's own framework guidance for TanStack Router: routes stay thin, screens live in pages.
  • Extraction is earned, never anticipated, which is why features/ holds two slices and entities/ one — the alternative, an entity per domain noun, is the failure mode the methodology warns about.

Notes

  • Visual change: none. Every page was walked in a browser against a real stack — home, both budget tabs, settings, analysis, portfolio, goals, /dev-avatar, and the docs site.
  • Tests: no new tests; 134 existing tests pass. The gate this change adds is bun run check:fsd, proved by a violation fixture (exit 1) and by toggling a rule off and back on to prove the config is read.
  • Docs updated: contributing/architecture.mdx (new Feature-Sliced Design section and the gate), contributing/index.mdx, contributing/writing-docs.mdx, contributing/categorisation.mdx, contributing/bank-provider.mdx, plus AGENTS.md at the root and in apps/web, apps/fumadocs, packages/api, packages/env, packages/ui.
  • docs/engineering/ is repointed at the new paths and carries the facts the route comments held before no-comments took them.
  • countries moved to shared/lib and category-menu-icon to entities/category: features/bank-connection and pages/settings each became a second consumer.
  • Merge order: this rebases onto dev at 1303541 and touches almost every file under both src/ trees, so it should land before any other frontend branch.

apps/web/src and apps/fumadocs/src are now Feature-Sliced Design roots:
app, pages, features, entities and shared, one public API per slice, and
imports that only ever point down a layer. Route files hold routing alone
(1620 lines to 302 in apps/web); their composition, queries and handlers
moved into the page slices they belong to.

Steiger enforces it. Each app declares `steiger src --fail-on-warnings`
as `lint:fsd`, `bun run check:fsd` runs both through turbo, and CI runs
it as the Architecture step. `no-reexport-only-modules` already allowed
`index.ts`; apps/fumadocs/.oxlintrc.json now says so too, because a
slice's public API is exactly that deliberate entrypoint.

Only the two applications are roots: FSD orders an application, and the
packages are libraries the apps consume. features/ holds two slices and
entities/ one, because extraction is earned by a second consumer rather
than anticipated — which is what moved `countries` into shared/lib and
`category-menu-icon` into entities/category.

docs/engineering/ is repointed at the new paths, and carries the facts
the route comments held before `no-comments` took them.
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 81d95fed-838c-4e8b-9510-accbf238d9b4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@suiramdev
suiramdev merged commit e76872a into dev Sep 19, 2026
3 checks passed
@suiramdev
suiramdev deleted the refactor/feature-sliced-design branch September 19, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant