Clean Code: A Place for Everything

How you organize your code is one of the first things a new developer on your project will notice. A logical, intuitive structure can help them get up to speed quickly. A chaotic one can leave them feeling lost and confused.

There are many ways to organize a project, but the key is to be consistent and to choose a structure that scales with your application. One of the most common debates is whether to organize files by their type (e.g., components, services) or by their feature.

Structure by Feature, Not by Type

Organizing by type might seem logical at first, but as your application grows, it can become hard to find all the related pieces of a feature. Grouping by feature keeps everything related to a specific part of your application together.

// Bad: Organized by type. When you work on "users", you have to jump between folders.
src/
  components/
    UserList.tsx
    ProductList.tsx
    OrderList.tsx
  services/
    userService.ts
    productService.ts
    orderService.ts
  types/
    user.ts
    product.ts
    order.ts

// Good: Organized by feature. Everything related to "users" is in one place.
src/
  users/
    components/
      UserList.tsx
      UserCard.tsx
    services/
      userService.ts
    types/
      user.ts
    index.ts
  products/
    components/
      ProductList.tsx
      ProductCard.tsx
    services/
      productService.ts
    types/
      product.ts
    index.ts

This feature-based approach makes your codebase much more modular. If you want to change something about the user feature, you know exactly where to look. If you want to remove it, you can just delete the folder (in theory!).

Use Barrel Exports to Simplify Imports

A "barrel" is an index.ts (or index.js) file that re-exports modules from the same folder. This allows you to import multiple things from a single location, which can really clean up your import statements.

// In src/users/index.ts, we export everything from the feature folder.
export { UserList } from './components/UserList';
export { UserCard } from './components/UserCard';
export { userService } from './services/userService';
export type { User } from './types/user';

// Now, other parts of the app can import from a single, clean path.
import { UserList, userService, type User } from '@/users';

Quick Tips

  • Be consistent. Whatever structure you choose, stick with it across the project.
  • Keep related files close. The "locality of reference" principle applies to code too. Files that are changed together should live together.
  • Don't over-engineer. For a small project, a simple structure is fine. You can always refactor as it grows.

A well-organized codebase is a sign of a professional developer. It shows that you care about maintainability and the experience of your fellow developers.