Skip to main content

Feature-first organization

Organize code by feature, the business capability it serves, not by technical type. Each feature owns everything it needs: its layers on the backend, its components, hooks, API calls, and types on the frontend.

Why: a change request is almost always about a feature ("let users reset a password"), not about a technical layer. When a feature lives in one place, you can understand, test, and change it without hunting across the whole codebase.

Feature-first versus type-first​

# Type-first: one change touches every folder
routers/ users.py orders.py
services/ users.py orders.py
repositories/ users.py orders.py
models/ users.py orders.py

# Feature-first: one change stays in one folder
features/
users/ domain/ application/ infrastructure/ presentation/
orders/ domain/ application/ infrastructure/ presentation/

Type-first layouts look tidy at first, but they spread each feature across the tree, and unrelated features end up sharing files. Feature-first keeps the Clean Architecture layers, but inside each feature rather than across the whole application.

Concrete layouts: FastAPI structure and React + Vite + Ant Design. This page covers the reasoning and the boundaries, not the folder trees.

Sizing a feature​

A feature is one business capability whose parts change together. These tests are heuristics, not standards.

TestHealthyWarning sign
Describe it in one sentence"Manage user accounts"The sentence needs "and" to connect unrelated things
Change frequencyIts files change togetherHalf of its files change for reasons unrelated to the other half
Dependencies on other featuresA few, through public interfacesIt reaches into many other features
NameA domain noun such as ordersA technical word such as utils, services, or common

Split a feature when it fails the first two tests. Merge two features when neither can change without the other.

Dependencies between features​

  • Frontend (standard): features import from other features only through their index.ts (React + Vite + Ant Design).
  • Backend: the rule for one feature calling another is TBD. As guidance, not a standard, avoid importing another feature's infrastructure or ORM models.
  • Shared code and features: whether shared/ may import from a feature is TBD. Guidance, not a standard: shared code exists to serve several features, so depending on one of them defeats its purpose.

Moving code to shared​

Code starts in the feature that needs it and moves to shared/ only after real reuse. The frontend moves code once it is reused in two or three places (React + Vite + Ant Design); the backend sets no numeric threshold. This is DRY applied to folders: a premature shared/ couples features to a guess about what they have in common.

Backend shared/ holds cross-feature ports, persistence base, logging, and pure helpers (FastAPI structure).

Common mistakes​

MistakeWhy it hurtsFix
Top-level type folders (services/, utils/) beside features/Features leak back into a type-first layoutPut the code inside the feature that uses it
common/ or utils/ as a dumping groundNothing owns it, so everything depends on itMove code to its feature, or to shared/ with a clear purpose
One feature imports another's internalsHidden coupling; changing one breaks the otherUse the other feature's public interface
Moving code to shared/ after one useCouples features to a guessWait for real reuse
A feature named after a layer, such as repositoriesThe layout is type-first againName features after business capabilities