codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
EDU›Monorepo · SSOT · layer separation thinking›Step 3

Step 3

Folders as contracts

0 views

Folders as contracts

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-pryzeet/.

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

← Step 2

SSOT — where to put it

Step 4 →

SQL = SSOT