Project Structure
A well-organized project structure improves scalability, readability, onboarding speed, and consistency across team members. It also reduces cognitive load and makes large projects easier to maintain and scale over time.
General principles
Group by domain when possible (
features/
)Separate UI, state, API, and validation logic
Use clear names (see Naming)
Prefer flat and descriptive over deep and vague
Keep entry files (
index.ts
) minimal and purposefulCo-locate tests, styles, and types near the source (optional)
Structure: Fullstack-Ready (Monorepo-Friendly)
1root/
2│
3├── src/ # App source code
4│ ├── api/ # API route handlers (backend or frontend API routes)
5│ ├── components/ # Reusable UI components
6│ ├── features/ # Domain-specific features (each with UI, logic, API)
7│ ├── pages/ # App routes (Next.js, React-Router, etc.)
8│ ├── hooks/ # Reusable React/TS logic
9│ ├── lib/ # Global utilities, constants, adapters, etc.
10│ ├── stores/ # Global state management (Redux, Zustand, etc.)
11│ ├── styles/ # CSS/SASS/global styles
12│ ├── services/ # Business logic / API clients / external systems
13│ ├── types/ # Global TypeScript types & interfaces
14│ └── index.tsx # App entry point
15│
16├── public/ # Static files (images, fonts, etc.)
17├── tests/ # Unit and integration tests (if separated)
18├── scripts/ # CLI or dev-time scripts (e.g. seeding, linting)
19├── .env # Environment variables
20├── tsconfig.json # TS config
21├── package.json # Project manifest
22└── README.md
Structure: Frontend-Ready (React.js)
1frontend/
2├── public/ # Static assets (images, favicon, etc.)
3├── src/
4│ ├── assets/ # SVGs, icons, fonts
5│ ├── components/ # Reusable UI components (pure/presentational)
6│ ├── features/ # Domain-level logic and UI slices
7│ ├── hooks/ # Reusable logic (custom React hooks)
8│ ├── lib/ # Utilities, formatters, validators
9│ ├── pages/ # Route components (e.g., for Next.js or React Router)
10│ ├── routes/ # Route configuration (if not file-based)
11│ ├── stores/ # State management (Zustand, Redux, Jotai)
12│ ├── styles/ # Global styles, SASS files, themes
13│ ├── types/ # Global TypeScript types/interfaces
14│ ├── App.tsx
15│ └── index.tsx # Entry point
16├── tests/ # (Optional) central test folder
17├── .env
18├── tsconfig.json
19├── webpack.config.js / vite.config.ts
20└── package.json
Structure: Backend-Ready (Node.js)
1backend/
2├── src/
3│ ├── config/ # DB config, environment variables
4│ ├── controllers/ # Route handlers (thin)
5│ ├── routes/ # Express routes (or routers per feature)
6│ ├── services/ # Business logic, orchestrates repositories
7│ ├── models/ # DB models / schemas (e.g., Sequelize, Prisma, Mongoose)
8│ ├── middlewares/ # Express middleware (auth, logging, etc.)
9│ ├── validators/ # Joi/Zod/schemas or custom validation
10│ ├── utils/ # General-purpose helpers
11│ ├── jobs/ # Background tasks, cron, queues
12│ ├── types/ # Global/shared TypeScript types
13│ ├── index.ts # App entrypoint (creates server)
14│ └── server.ts # Express app instance
15├── tests/ # Unit/integration tests
16├── .env
17├── ormconfig.js
18├── tsconfig.json
19└── package.json
Feature-First principles
Each domain or feature has its own folder containing all related code.
UI components, logic, services, and tests live together per feature.
Promotes high cohesion and encapsulation of business logic.
Makes onboarding easier by reducing cross-folder navigation.
Encourages modular thinking and separation of concerns by feature.
Reduces risk of tight coupling between unrelated features.
Easier to scale in large teams working on different product areas.
Structure: Feature-First Frontend
1src/
2└── features/
3 ├── auth/
4 │ ├── AuthForm.tsx
5 │ ├── useAuth.ts
6 │ ├── authService.ts
7 │ ├── auth.types.ts
8 │ └── auth.test.ts
9 ├── user/
10 └── cart/
Structure: Feature-First Backend
1src/
2└── features/
3 ├── auth/
4 ├── user/
5 │ ├── user.controller.ts
6 │ ├── user.routes.ts
7 │ ├── user.service.ts
8 │ ├── user.model.ts
9 │ ├── user.validator.ts
10 │ └── user.types.ts
11 └── product/
Layered principles
Code is grouped by technical role: components, services, models, etc.
Easier to start with for small or early-stage projects.
Encourages reuse of shared logic and components across the app.
Can lead to scattered logic and reduced clarity at scale.
Often results in long import chains and tightly coupled layers.
Testing often requires mocking multiple layers.
Difficult to isolate feature boundaries as the app grows.
Structure: Layered Frontend
1src/
2├── components/
3├── hooks/
4├── pages/
5├── services/
6├── utils/
Structure: Layered Backend
1src/
2├── controllers/
3├── routes/
4├── services/
5├── models/
6├── validators/
7├── middlewares/
Hybrid principles
Combines feature-based organization with shared technical layers.
Features hold their own logic, while shared code is centralized.
Allows reuse of components, hooks, and utilities across features.
Balances modularity with maintainability.
Encourages co-located business logic without duplicating base tools.
Scales well for mid- to large-size apps with a strong shared foundation.
Provides flexibility for teams working across both domain and technical lines.
1src/
2├── features/
3│ └── user/
4├── shared/
5│ ├── components/
6│ ├── hooks/
7│ └── utils/
Anti-Pattern: everything-in-utils hell
All utility code dumped into a single utils/
folder — validation, math, DOM, dates, etc.
Problem:
Lacks categorization (dates, strings, DOM, validation — all mixed up).
Hard to discover or refactor logic.
1src/
2├── utils/
3│ ├── helpers.ts
4│ ├── dateUtils.ts
5│ ├── formatters.ts
6│ ├── validate.ts
Fix: split into focused folders: lib/
, validators/
, formatters/
, etc.
1src/
2├── lib/
3│ ├── formatters/
4│ ├── validators/
5│ ├── math/
6│ └── dom/
Anti-Pattern: one big 'components' folder
All UI components mixed into one folder with no grouping or naming strategy.
Problem:
No separation by domain, type, or feature.
Naming collisions and long lists.
1src/
2└── components/
3 ├── Button.tsx
4 ├── Header.tsx
5 ├── UserForm.tsx
6 ├── ProductList.tsx
Fix: group by role (ui/
, layout/
, form/
) or domain (features/user/components/
).
1src/
2└── components/
3 ├── ui/
4 │ ├── Button.tsx
5 │ ├── Input.tsx
6 └── layout/
7 ├── Header.tsx
8 ├── Sidebar.tsx
Or (if domain-driven):
1src/
2└── features/
3 ├── user/
4 │ └── UserForm.tsx
5 └── product/
6 └── ProductList.tsx
Anti-Pattern: deeply nested folders
Folders within folders for the sake of organizing — often overly abstracted or premature. Files buried under 6+ levels of folders.
Problem:
Hard to navigate and maintain.
Signals over-engineering or improper abstraction.
1src/
2└── core/
3 └── base/
4 └── shared/
5 └── utils/
6 └── validation/
7 └── email/
8 └── index.ts
Fix: Keep folder depth shallow, structure reflects actual domains.
1src/
2└── validators/
3 └── validateEmail.ts
Anti-Pattern: mixing 'feature' and 'technical' folders
Having both auth/
and services/
on the same level.
Problem: Leads to mental friction: do you look in services/
or in user/
?
1src/
2├── auth/
3├── user/
4├── services/
5├── controllers/
Fix: Choose either feature-based or layered — or isolate one inside the other.
Anti-Pattern: generic folder names
Overused and meaningless folder names. common/
, core/
, or shared/
folders that contain everything from fonts to logic to entire services.
Problem: Becomes a dumping ground for everything that "doesn’t fit."
1src/
2├── common/
3│ ├── Button.tsx
4│ ├── helpers.ts
5│ ├── constants.ts
6│ ├── authService.ts
Fix: Use precise names (shared/components/
, shared/hooks/
).
1src/
2├── shared/
3│ ├── components/
4│ ├── lib/
5│ └── constants/
Anti-Pattern: duplicate logic across features
Copy-pasted functions between features due to poor centralization.
Problem: Leads to inconsistencies and bugs.
1# Same utility code re-written in multiple places because there's no clear shared location
2features/user/formatName.ts
3features/profile/formatName.ts
4features/dashboard/formatName.ts
Fix: Move shared logic to lib/
, shared/
, or services/
with clear names.
lib/formatters/formatName.ts
Anti-Pattern: spaghetti routing files
A single huge routes.js
or app.ts
file with all routes.
Problem: Impossible to scan or reason about at scale.
1// routes.ts
2app.get('/user')
3app.post('/user')
4app.get('/product')
5app.post('/product')
6app.delete('/order/:id')
Fix: Split routes by feature or use dynamic loading per module.
1// routes/user.ts
2// routes/product.ts
3// routes/order.ts
4
5// Then combined in routes/index.ts if needed
Anti-Pattern: index files overuse
Every folder exports everything through index
files — everywhere.
Problem:
Hides file boundaries and source origins.
Can cause circular dependencies.
Fix: Use index
selectively, not blindly.
Anti-Pattern: 'God' modules
A single file that handles rendering, fetching, parsing, validation, formatting, and more. One file (e.g., app.js
, api.ts
, dashboard.tsx
) grows to 500+ lines.
Problem:
Violates single-responsibility.
Hard to test, refactor, or navigate.
// UserDashboard.tsx — 700 lines
Fix: Split into smaller modules by intent and responsibility.
1features/user/
2├── UserDashboard.tsx
3├── useUserData.ts
4├── formatUserStats.ts
5├── user.test.ts
Anti-Pattern: poor test co-location
Tests are scattered far away or in flat (without structure) __tests__/
folder.
Problem: Disconnects tests from source logic.
1src/
2├── features/
3│ ├── user/
4│ │ ├── UserCard.tsx
5│ │ └── userService.ts
6└── __tests__/
7 ├── userCard.test.tsx
8 └── userService.test.ts
Fix: Co-locate tests next to the code they verify, or use mirrored structure.
1src/
2└── features/
3 └── user/
4 ├── UserCard.tsx
5 ├── userService.ts
6 └── __tests__/
7 ├── UserCard.test.tsx
8 └── userService.test.ts
Or next to the file:
1userService.ts
2userService.test.ts
Anti-Pattern: ambiguous boundaries in fullstack
In fullstack projects: shared types, logic, or files live in unclear places.
Problem: Leads to duplication or incorrect imports.
1src/
2├── api/
3├── pages/
4├── types/
5├── services/
Fix: Use a shared/
package or common/
module with clear separation of platform concerns.
1apps/
2├── frontend/
3│ └── src/
4├── backend/
5│ └── src/
6└── shared/
7 ├── types/
8 ├── utils/
9 └── config/
1src/
2├── client/
3├── server/
4└── shared/