plugins/frontend-toolkit/skills/architecture-improvement/SKILL.md
Reorganize a project into a feature-based folder structure with unidirectional imports. Use when adding a new feature conflicts with existing structure, when teammates can't find files, when circular dependencies appear in build logs, or at the start of a new quarter. Not for changes within a single file (use code-refactoring) or component extraction (use component-quality).
npx skillsauth add jaykim88/claude-ai-engineering architecture-improvementInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Establish clear module boundaries by feature, enforce unidirectional imports, and prevent circular dependencies. The folder structure should answer "where does this code live?" in under 10 seconds.
Universal — feature-based folder organization, unidirectional import rules, circular-dependency detection, and barrel-file discipline apply to any modular codebase. Server/Client component boundary is React-specific; other frameworks have analogous concerns.
Precondition — is the migration worth it? Feature-based organization pays off at scale, not in small apps. If the project is small (roughly < 20-30 components, 1-2 features) and none of the Triggers apply, keep the simpler layer-based layout — empty features/x/{api,hooks,...} shells add navigation cost with no benefit. Layer-based isn't obsolete; it's the right default until a Trigger appears (can't find files / one change spans many folders / cross-feature coupling causes bugs).
Reorganize from layer-based to feature-based
components/, hooks/, utils/, types/features/auth/{api,components,hooks,stores,types,utils}/shared/ (a hybrid layout is normal and expected)Define the import rule (unidirectional)
shared/ → features/ → app/
shared/ can be imported by features/ and app/features/ can be imported by app/ only — never by another featureapp/ (pages, routes) is the top — imported by nothingDetect and enforce the boundary (don't rely on discipline)
shared/, OR establish a documented dependency (rare, requires ADR)shared/ generic, not a junk drawer — the real failure mode of feature-based architecture. Promote only code with no domain knowledge (a Button, formatDate, an HTTP client); "used by 2 features" alone is not the bar. Domain-specific code shared by two features belongs to one feature, imported by the other via a documented exception. Sub-organize shared/ (ui / lib / api / config) — never a flat dump.Detect and resolve circular imports (validation loop)
madge for JS/TS — see Implementation)import type in TS) are erased at compile time and don't break at runtime — fix real (value) cycles first; de-prioritize type-only onesAudit barrel files (index.ts re-exports)
shared/ public-API layer (consumed by many features)features/auth/index.ts) — direct subpath imports instead: from '@/features/auth/api/login'Reposition server/client rendering boundaries (frameworks with a server/client split)
render-strategy-decision'use client' audit in React/Next.js)Document the structure
docs/architecture.md with directory diagrammadge --circular reports 0shared/ — verify it's actually used by 2+ features and won't be specialized laterdocs/adr/ADR-NNN-feature-based-architecture.md documenting the decision (Context / Options / Decision / Consequences)eslint-plugin-import with no-restricted-paths.zones enforcementrefactor(arch): move <subsystem> to feature-based layoutapp/, features/, shared/, lib/eslint-plugin-import no-restricted-paths.zones in .eslintrc (CI-blocking):
"import/no-restricted-paths": ["error", {
"zones": [
{ "target": "src/features/*/!(index.ts)", "from": "src/features/*", "except": ["./"] },
{ "target": "src/shared", "from": "src/features" },
{ "target": "src/shared", "from": "src/app" }
]
}]
Quick manual scan: grep -rE "from ['\"]@/features/[a-z]+/" src/features/npx madge --circular src/grep -rn "'use client'" src/ — remove the directive from any file that uses no hooks (useState/useEffect/useReducer), no browser APIs (window/document/localStorage), and no React event handlers (onClick etc.)shared/ public APIcomposables/, components/, utils/ — feature boundary requires explicit configuration; nuxt.config.ts has imports.dirs for fine-grained controlsrc/lib/; routes in src/routes/ ARE the app layer; barrel discouraged because Vite tree-shakes individual exports welleslint-plugin-import + @nx/eslint-plugin for cross-feature enforcement; lazy-loaded routes via loadChildrenmadge works for any JS/TS project; feature-based organization (vertical slices) is a universal pattern from Domain-Driven Design; circular dependencies are a smell in any languagecode-refactoring — for changes within a single filerender-strategy-decision — when reorganizing Server/Client component boundariescomponent-quality — for component extraction during the restructureshared/, never from sibling features. When two features need to share, the right move is to promote the shared piece up to shared/, not to barrel-export across siblings. Barrel files are convenient but cost tree-shaking and create circular dep risk; use them only where there's a stable 3+-consumer public API.development
Audit and optimize third-party scripts — analytics, tag managers, chat widgets, embeds — with the right loading strategy, performance budget, facades, and CSP/consent controls. Use when adding a script, when TBT/INP regress, when a GDPR/CCPA consent requirement arises, or before shipping. Not for first-party bundle size (use bundle-optimization) or broad Core Web Vitals diagnosis (use rendering-performance).
development
Apply the Testing Trophy (mostly integration tests with RTL + MSW, sparing E2E with Playwright) and set coverage thresholds. Use before new feature work, after bug fixes, when CI coverage falls below target, or when tests are flaky or break on every refactor. Not for wiring coverage gates + Playwright into the GitHub Actions matrix (use cicd-pipeline) or auditing WCAG a11y compliance (use accessibility-audit).
development
Inventory and prioritize technical debt — TODO/FIXME/HACK, any usage, deprecated APIs, untested logic — with impact × effort matrix. Use at quarter start, before a refactoring sprint, when a new teammate joins, or when feature velocity slows. Not for actually paying down debt (use code-refactoring) or recording a migration approach (use decision-records) — this only inventories and prioritizes.
development
Decision framework for choosing the right state location — URL, server cache, local component, or shared/global store. Use when state-sync bugs appear, prop drilling gets deep (3+ levels), filters/tabs lose state on reload, or quarterly review. Not for form state specifically (use form-ux) or when the state is actually server data (use api-caching-optimization).