Skip to main content

Step 3

Folders as contracts

7 views

Table of contents

Freeze "files dropped here automatically do X" as a contract.

1. URL = folder (Next.js App Router)

src/app/
├── posts/
│   ├── page.tsx          → /posts
│   └── [id]/
│       └── page.tsx      → /posts/:id

Path → URL and back, with grep.

2. No hyphenated composites in path

Express hierarchy via depth, not by admin-catalog/.

3. Naming

kebab-case.ts          → modules
camelCase.ts           → hooks (useX)
PascalCase.tsx         → React components
__tests__/*.test.ts    → tests
*.spec.ts              → Playwright E2E

Pick one set per repo.

4. Barrel exports — stable entry points

src/shared/lib/
├── index.ts             ← public API
├── db.ts
├── auth/
│   ├── index.ts
│   ├── session.ts
│   └── oauth.ts

External code imports "@/shared/lib", internals stay refactor-free.

5. Shared vs domains

src/shared/lib/    — domain-agnostic
src/shared/ui/     — visual elements
src/shared/types/  — types only
src/domains/*/     — domain-specific

6. Tests — two schools

  • Next to code: src/lib/foo.ts + src/lib/foo.test.ts
  • Separate: src/lib/foo.ts + tests/lib/foo.test.ts

Next-to-code wins for grepability and moves.

7. Docs folder contract (example)

docs/
├── RULES.md            — global rules
├── shared/*.md         — tech standards
├── agent/{svc}/        — AI instructions
└── service/{svc}/      — product/ops docs

8. _ prefix for private

src/
├── _legacy/        ← forbidden to import
├── _scripts/       ← one-off migrations

Python uses _ by convention; in TS, pair with eslint rules.

9. Gotchas

  • Over-nested paths — keep to 3–4 levels
  • utils/ as kitchen sink
  • Import cycles
  • Mixed naming conventions

10. Enforce with ESLint

import boundaries from "eslint-plugin-boundaries";
export default {
  plugins: { boundaries },
  rules: {
    "boundaries/element-types": ["error", {
      rules: [
        { from: "frontend", disallow: ["backend"] },
        { from: "shared", disallow: ["domains"] },
      ],
    }],
  },
};

Closing

Folder structure is a "makes it findable for others" exercise. A name that depends on your memory fails after three months.

Next

  • 04-sql-as-ssot