Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Coding Standards|Rules, conventions, and best practices I follow and recommend to teams

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 purposeful

  • Co-locate tests, styles, and types near the source (optional)

Structure: Fullstack-Ready (Monorepo-Friendly)

File
Copied!
1root/
23├── 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
1516├── 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)

File
Copied!
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)

File
Copied!
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

File
Copied!
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

File
Copied!
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

File
Copied!
1src/
2├── components/
3├── hooks/
4├── pages/
5├── services/
6├── utils/

Structure: Layered Backend

File
Copied!
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.

File
Copied!
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.

File
Copied!
1src/
2├── utils/
3│   ├── helpers.ts
4│   ├── dateUtils.ts
5│   ├── formatters.ts
6│   ├── validate.ts

Fix: split into focused folders: lib/, validators/, formatters/, etc.

File
Copied!
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.

File
Copied!
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/).

File
Copied!
1src/
2└── components/
3    ├── ui/
4    │   ├── Button.tsx
5    │   ├── Input.tsx
6    └── layout/
7        ├── Header.tsx
8        ├── Sidebar.tsx

Or (if domain-driven):

File
Copied!
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.

File
Copied!
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.

File
Copied!
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/?

File
Copied!
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."

File
Copied!
1src/
2├── common/
3│   ├── Button.tsx
4│   ├── helpers.ts
5│   ├── constants.ts
6│   ├── authService.ts

Fix: Use precise names (shared/components/, shared/hooks/).

File
Copied!
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.

File
Copied!
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.

File
Copied!
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.

TypeScript
Copied!
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.

TypeScript
Copied!
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.

TypeScript
Copied!
// UserDashboard.tsx — 700 lines

Fix: Split into smaller modules by intent and responsibility.

File
Copied!
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.

File
Copied!
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.

File
Copied!
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:

File
Copied!
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.

File
Copied!
1src/
2├── api/
3├── pages/
4├── types/
5├── services/

Fix: Use a shared/ package or common/ module with clear separation of platform concerns.

File
Copied!
1apps/
2├── frontend/
3│   └── src/
4├── backend/
5│   └── src/
6└── shared/
7    ├── types/
8    ├── utils/
9    └── config/
File
Copied!
1src/
2├── client/
3├── server/
4└── shared/
Styleguide: Project Structure | Yevhen Klymentiev