Project Layout

code specs/code/project-layout.kmd

Layout intra-módulo per linguagem: source root, tests location, fixtures/testdata, resources/assets, build artifacts (gitignored), generated code, config files, docs, examples. Tabela canônica per linguagem. Distinto de RFC-006 que cobre layout inter-módulo (sector → backend/app/engine/landing).

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Project Layout

Facet Code do Koder Design.

Cobre layout intra-módulo (dentro de um Sector). Layout inter-módulo (<domain>/<area>/<sector>/) é RFC-006.

Princípio cross-cutting

Cada linguagem tem o seu idiom estabelecido (Go cmd/+internal/, Rust src/+tests/, Dart lib/+test/, etc.). A Koder respeita o idiom — não inventa layout próprio que confunde tooling oficial.

Onde uniformizar entre todas:

  • README.kmd na raiz do módulo (per readmes/products.kmd)
  • koder.toml na raiz (per koder-toml/format.kmd)
  • .editorconfig na raiz (per code/indentation.kmd)
  • LICENSE na raiz
  • docs/ pra documentação intra-módulo (KMD/MD)
  • backlog/ pra tickets do módulo (per policies/backlog.kmd)
  • dist/, build/, target/, out/ sempre gitignored

Tabela canônica per linguagem

Go

<module>/
├── README.kmd
├── koder.toml
├── .editorconfig
├── LICENSE
├── go.mod
├── go.sum
├── cmd/
│   └── <bin-name>/main.go    ← um dir por binário
├── internal/                 ← código não-exportável
│   ├── <pkg>/
│   └── ...
├── pkg/                      ← código exportável (se library)
├── api/                      ← spec proto/openapi (se relevante)
├── testdata/                 ← fixtures pra _test.go
├── docs/
├── backlog/
├── examples/                 ← se library
├── scripts/                  ← bash auxiliares
└── dist/                     ← gitignored: build outputs
  • Tests: arquivo _test.go next-to-source (idiom Go)
  • Fixtures: testdata/ (Go ignora por convenção)

Rust

<module>/
├── README.kmd
├── koder.toml
├── .editorconfig
├── LICENSE
├── Cargo.toml
├── Cargo.lock              ← commit em binary; gitignore em library
├── src/
│   ├── lib.rs              ← biblioteca
│   ├── main.rs             ← binário
│   └── bin/<name>.rs       ← múltiplos binários
├── tests/                  ← integration tests (cada arquivo = crate)
├── benches/                ← benchmarks
├── examples/               ← idiom Cargo
├── docs/
├── backlog/
└── target/                 ← gitignored
  • Unit tests: #[cfg(test)] mod tests no mesmo arquivo (idiom Rust)
  • Integration: tests/

Dart / Flutter

<module>/
├── README.kmd
├── koder.toml
├── .editorconfig
├── LICENSE
├── pubspec.yaml
├── pubspec.lock            ← commit em app; gitignore em library
├── lib/
│   ├── <module>.dart       ← entry público
│   └── src/                ← implementação (não exportada)
├── test/
├── integration_test/       ← Flutter integration tests
├── example/                ← se library
├── android/, ios/, ...     ← se Flutter app
├── assets/                 ← se Flutter app
├── docs/
├── backlog/
└── build/                  ← gitignored

Python

<module>/
├── README.kmd
├── koder.toml
├── .editorconfig
├── LICENSE
├── pyproject.toml          ← PEP 518; preferred sobre setup.py
├── <package_name>/         ← snake_case; src layout opcional (recomendado pra evitar import shadowing)
│   ├── __init__.py
│   └── ...
├── tests/                  ← preferred sobre next-to-source
│   ├── conftest.py
│   └── test_*.py
├── docs/
├── examples/
├── backlog/
└── dist/, build/, .venv/   ← gitignored
  • src layout (src/<package_name>/) recomendado pra projetos publicados — evita import shadowing.

Koda

<module>/
├── README.kmd
├── koder.toml
├── .editorconfig
├── LICENSE
├── lib/                    ← código fonte
│   ├── <module>.kd
│   └── ...
├── test/
│   └── *_test.kd
├── docs/
├── examples/
├── backlog/
└── out/                    ← gitignored

JS/TS

<module>/
├── README.kmd
├── koder.toml
├── .editorconfig
├── LICENSE
├── package.json
├── package-lock.json       ← commit (npm) ou pnpm-lock.yaml (pnpm)
├── tsconfig.json           ← se TS
├── src/                    ← preferred
│   ├── index.ts
│   └── ...
├── test/                   ← Jest/Vitest setup
│   └── *.test.ts
├── docs/
├── examples/
├── public/                 ← se web app
├── backlog/
└── dist/, node_modules/    ← gitignored

Shell scripts (módulo standalone)

<module>/
├── README.kmd
├── koder.toml
├── LICENSE
├── bin/                    ← scripts executáveis (chmod +x)
│   └── <script>
├── lib/                    ← funções source-able
│   └── <lib>.sh
├── test/                   ← bats-core ou similar
├── docs/
└── backlog/

R1 — Tests location

Linguagem Convenção
Go next-to-source (foo_test.go ao lado de foo.go)
Rust unit: inline mod tests; integration: tests/
Dart test/ separado
Python tests/ separado (preferred Koder) ou next-to (PEP 396 OK)
Koda test/ separado
JS/TS test/ ou src/__tests__/ (config Jest/Vitest)
Shell test/ separado

Princípio: seguir idiom da linguagem. A Koder não força uniformidade contra o tooling oficial.

R2 — Fixtures / testdata

Linguagem Convenção
Go testdata/ (ignorado pelo go build)
Rust tests/fixtures/
Dart test/fixtures/
Python tests/fixtures/ ou tests/data/
Koda test/fixtures/
JS/TS test/fixtures/

R3 — Resources / assets

  • Static assets (imagens, fonts): assets/ (Flutter), static/ (Go web), public/ (JS web)
  • Templates: templates/ (Python/Go) ou views/ (Rails-like)
  • Migrations DB: migrations/ na raiz do módulo

R4 — Build artifacts (sempre gitignored)

Diretório Linguagem
dist/ JS/TS, Python, Dart
build/ Dart, Java, generic
target/ Rust, Java/Maven
out/ Koda, generic
bin/ Go (output de go build) — exceção: scripts source-controlled
*.pyc, __pycache__/ Python
node_modules/ JS/TS
.venv/, venv/, env/ Python

.gitignore template canônico em meta/docs/stack/specs/code/gitignore.template.

R5 — Generated code

Quando código é gerado por tooling (protoc, openapi-generator, codegen Koder):

  • Marker comment no header: // Code generated by <tool>. DO NOT EDIT.
  • Diretório gen/ ou per-tool (protoc/)
  • Gitignore quando reproduzível trivialmente (build step rápido)
  • Commit quando custoso/lento ou quando o gerador não é parte do build padrão

R6 — Config files

  • Project config: koder.toml na raiz (sempre)
  • Language config: .golangci.yml, pyproject.toml, analysis_options.yaml, tsconfig.json — na raiz
  • Editor: .editorconfig na raiz (sempre)
  • Env: .env.example na raiz; .env gitignored
  • CI: .gitea/workflows/ (Gitea Actions)

R7 — Docs intra-módulo

  • README.kmd na raiz (overview, install, usage curto)
  • docs/ pra docs maiores (docs/getting-started.kmd, docs/api.kmd, docs/architecture.kmd)
  • ADRs em docs/decisions/NNNN-<title>.kmd (per policies/content-location.kmd)
  • RFCs intra-módulo em docs/rfcs/<scope>-RFC-<NN>-<title>.kmd

R8 — Examples

Quando o módulo é library, examples/ com 1+ exemplos completos runnable. Cada exemplo standalone (não compartilha state com outros).

Audit deterministic

project-layout-audit.sh:

  1. README.kmd, koder.toml, .editorconfig, LICENSE na raiz
  2. Diretório de build na lista de gitignored
  3. Generated code com marker comment
  4. Tests em location idiomática (warning se inconsistente)
  • RFC-006 (sector layout inter-módulo)
  • code/imports.kmd (import paths refletem layout)
  • readmes/products.kmd (README format)
  • koder-toml/format.kmd (config canônica)
  • policies/backlog.kmd (backlog/ directory)
  • policies/content-location.kmd (where to put doc types)

References