{"post":{"slug":"how-to-structure-a-project-2026","title":"How to structure a project in 2026: a practical guide for every stack","excerpt":"Good project structure is invisible when right and painful when wrong. This 2026 guide gives concrete layouts for monorepos, services, and solo apps.","category":"code","date":"February 27, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">G</span>ood project structure is the kind of thing you only notice when it is wrong. When it is right, you open a file and know immediately where the thing you need lives. When it is wrong, every change requires a safari through four directories and three mental context-switches. In 2026 the tooling has never been better for enforcing layout — but you still have to pick the right one first.\n\n## What changed in 2026\n\n- **Monorepo tooling matured.** Turborepo, Nx, and Bazel now handle caching and task orchestration reliably enough that large teams default to them. But small teams still over-adopt them too early.\n- **Language servers reward explicitness.** Path aliases (`@/features/auth`) are now first-class in TypeScript, Rust workspaces, and Python's `src/` layout — IDEs and LSPs resolve them natively.\n- **AI-assisted navigation raised the bar.** Tools like Copilot Workspace index your structure; a flat, coherent layout gives you better suggestions than a tangled one.\n- **`src/` layout won in Python.** `src/<package>/` is now the recommended default for new Python projects, eliminating the accidental-import-of-the-root bug.\n\n## The core principle: colocation\n\nFiles that change together should live together. The opposite — splitting by technical layer — creates \"shotgun surgery,\" where one feature change requires edits in six folders.\n\n```\n# Layer-split (fragile)\ncontrollers/\n  user.js\n  order.js\nmodels/\n  user.js\n  order.js\nservices/\n  user.js\n  order.js\n\n# Feature-first (scales)\nfeatures/\n  user/\n    controller.js\n    model.js\n    service.js\n    user.test.js\n  order/\n    controller.js\n    model.js\n    service.js\n    order.test.js\n```\n\n## Reference layouts by stack\n\n### Node / TypeScript API\n\n```\nsrc/\n  features/\n    auth/\n    orders/\n    users/\n  shared/          # pure utilities, no feature imports\n    db.ts\n    logger.ts\n    errors.ts\n  app.ts           # express/fastify setup\n  server.ts        # entrypoint\ntests/\n  integration/\n.env.example\n```\n\n### Python service\n\n```\nsrc/\n  myapp/\n    features/\n      auth/\n      billing/\n    shared/\n      db.py\n      settings.py\n    main.py\ntests/\npyproject.toml\n```\n\n### React / Next.js frontend\n\n```\nsrc/\n  features/\n    dashboard/\n      components/\n      hooks/\n      api.ts\n    onboarding/\n  shared/\n    components/   # truly global UI only\n    hooks/\n    lib/\n  app/            # Next.js app-router pages\n```\n\n## Monorepo vs polyrepo in 2026\n\n| Factor | Monorepo | Polyrepo |\n| --- | --- | --- |\n| Code sharing | Easy — import directly | Requires publishing packages |\n| CI complexity | Higher — scoped pipelines needed | Lower per repo |\n| Team autonomy | Lower | Higher |\n| Dependency sync | Trivial | Manual, error-prone |\n| Best for | >3 related services sharing code | Unrelated or independent services |\n\nStart with a single repo. Add monorepo tooling only when you have two or more services that share non-trivial code and you feel the pain of duplication.\n\n## How to structure shared code\n\n- **`shared/` or `common/`** — only true cross-feature primitives (errors, loggers, config loaders).\n- **No circular imports.** `features/auth` may import `shared/db`, but `shared/` must not import any feature.\n- **Barrel files with care.** One `index.ts` per feature for the public interface; avoid re-exporting everything or you lose tree-shaking.\n\n```typescript\n// features/auth/index.ts — explicit public surface\nexport { login, logout } from './service';\nexport type { AuthUser } from './types';\n// Do NOT export internal helpers\n```\n\n## Enforcing structure with tooling\n\nTribal knowledge decays. Automate it:\n\n```json\n// .eslintrc — forbid cross-feature imports\n{\n  \"rules\": {\n    \"import/no-restricted-paths\": [\n      \"error\",\n      {\n        \"zones\": [\n          {\n            \"target\": \"./src/features/auth\",\n            \"from\": \"./src/features/orders\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nAdd path aliases so refactoring a folder does not break fifty relative imports:\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\n## How to pick a structure\n\n1. **Solo project / prototype** — flat `src/` with feature folders once you have more than 3 features.\n2. **Small team API** — feature folders inside `src/`, one `shared/`, strict import rules.\n3. **Multiple frontend + backend** — monorepo with Turborepo, packages for shared types and UI kit.\n4. **Enterprise multi-team** — Nx or Bazel with explicit module boundaries and ownership in `CODEOWNERS`.\n\n## Common mistakes\n\n**Going feature-first too late.** The layer split feels fine at 5 routes. At 30 it is a disaster. Switch early.\n\n**Putting everything in `shared/`.** If 40% of your code is \"shared,\" you have a hidden monolith. Shared should be small.\n\n**No `index` surface contract.** Importing `../../features/auth/internals/token` from another feature means any internal rename breaks callers.\n\n**Inconsistent casing.** `UserService.ts` vs `user-service.ts` in the same project is a tooling and git headache. Pick one convention and lint it.\n\n## What to skip\n\n- **Monorepo on day one** — the tooling overhead is real. Earn it.\n- **Clean Architecture layer names** (`domain/`, `application/`, `infrastructure/`) unless your team knows DDD well. They often produce more ceremony than value.\n- **Deep nesting past three levels** — `src/features/billing/stripe/webhooks/handlers/` is a warning sign.\n\n## FAQ\n\n**Does folder structure matter if I have good tests?**\nYes. Tests do not help you find where to add a feature. Structure determines navigation speed and the cognitive load of change.\n\n**How do I migrate an existing messy project?**\nMove one feature at a time. Create the new structure in parallel, migrate one module, run tests, commit. Never big-bang refactor structure.\n\n**Should test files live next to source files or in a separate `tests/` folder?**\nUnit tests colocated (`auth.test.ts` next to `auth.ts`). Integration and end-to-end tests in a top-level `tests/` folder — they test boundaries, not internals.\n\n**What about generated code?**\nAlways in a `generated/` or `__generated__/` directory, never edited by hand, and typically git-ignored or clearly marked.\n\n## Where to go next\n\n- [How to refactor legacy code in 2026](/blog/how-to-refactor-legacy-code-2026)\n- [How to review code effectively in 2026](/blog/how-to-review-code-effectively-2026)\n- [How to write good commit messages in 2026](/blog/how-to-write-good-commit-messages-2026)","hue":321,"metaTitle":"How to Structure a Project in 2026: Layouts That Scale","metaDescription":"Practical 2026 guide to project structure. Feature folders, monorepo vs polyrepo, path aliases, and tooling to enforce your layout across any language","keywords":["how to structure a project 2026","project folder structure 2026","feature folder structure","monorepo structure 2026","software project layout","colocation principle","project organisation best practices","scalable codebase structure","project structure javascript","clean architecture folder layout"],"tldr":["**Colocation beats separation** — keep files that change together close together, not scattered by type.","**Feature folders scale; layer folders don't** — controllers/, models/, services/ breaks down past ~10 features.","**One README per service** — every runnable unit needs a 60-second \"how to run this\" at its root.","**Enforce structure with tooling** — lint rules and path aliases beat tribal knowledge every time.","**Skip:** creating a monorepo on day one. Start with a single well-structured repo; split when friction is measurable."],"readTime":"4 min read","wordCount":919},"related":[{"slug":"how-to-write-clean-functions-2026","title":"How to write clean functions in 2026","excerpt":"Clean functions are the single highest-leverage thing you can do for code quality. Here is the 2026 playbook with concrete rules and real examples.","category":"code","date":"March 18, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":318},{"slug":"how-to-set-up-a-monorepo-2026","title":"How to set up a monorepo in 2026","excerpt":"Monorepos let teams share code and coordinate releases without publishing packages. This guide covers tooling, workspace setup, and CI caching strategies for 2026.","category":"code","date":"March 16, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":309},{"slug":"refactoring-legacy-code-guide-2026","title":"Refactoring Legacy Code: A Safe 2026 Process","excerpt":"Refactoring legacy code without tests is how outages happen. This guide covers the safe sequence, characterization tests, small steps, and feature flags, for touching code nobody fully understands.","category":"code","date":"August 8, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":320}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-stretch-daily-2026","title":"How to stretch daily in 2026: a simple routine that actually sticks","excerpt":"Daily stretching reduces stiffness and improves movement quality, but most routines get abandoned. Here is a realistic, science-backed approach for 2026.","category":"guides","date":"February 27, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":155},"older":{"slug":"how-to-study-for-an-exam-2026","title":"How to study for an exam in 2026: proven methods that actually work","excerpt":"Most studying is wasted effort — rereading notes and highlighting are nearly useless. Here is the 2026 evidence-based system that actually moves the needle.","category":"guides","date":"February 27, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":155}}