How do you organise a codebase?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
The goal is that someone can find where a change belongs without reading the whole project. Two structures dominate.
By type — components/, hooks/, services/, utils/. Simple, and fine for small projects. It stops scaling once the folders get large, because one feature ends up scattered across five directories and you touch all of them for a single change.
By feature — everything a feature needs lives together:
src/
features/
checkout/
components/
api.ts
types.ts
shared/ ← only what several features actually use
lib/
Deleting a feature becomes deleting a folder, and related code sits next to related code. The main risk is shared/ turning into a dumping ground, so move something there only when a second feature genuinely needs it — not in anticipation.
Principles that matter more than the folder names
- Colocate. Keep tests, styles and types next to the code they describe.
- Keep dependencies one-directional. Features may import from shared; shared must never import from a feature. A cycle here is the clearest sign the boundary is wrong.
- Separate server and client code explicitly. A directory name is the cheapest way to make an accidental import obvious, and it prevents leaking server-only modules into the browser bundle.
- One export per file, named after the file. Search and rename then work reliably.
- Let the structure follow the domain, not the framework.
checkoutsays more thancontainers.
Signals it has gone wrong
A utils.ts that keeps growing, a change that requires editing six folders, imports with four levels of ../, or a components/ folder where nobody can tell what is shared and what belongs to one screen.
Restructure when you feel that friction, not in advance — the right shape becomes obvious only once the code exists.
