Files and comments

Last updated: 2026-08-18Ten file roles, domain-shaped directories instead of layers, and the five tags a comment is allowed to carry.

Files and directories

The role lives in the filename

<domain>.<role>.ts — the role is visible in a directory listing, so the file does not have to be opened.

RoleContentsPromise
*.entry.tsProcess entry pointThe only place where the dependency graph is assembled
*.route.tsHTTP endpointsRequest parsing and a call into the domain, nothing else
*.rpc.tsgRPC methodsThe same, for RPC
*.store.tsDatabase accessThe only place that contains SQL or an ORM
*.wire.tsA client for someone else's APIThe only place a foreign format lives
*.policy.tsAccess rules and invariantsPure, no I/O — rules are testable without a database
*.shape.tsTypes and validation schemasNothing executable
*.event.tsEvent contracts, producers and consumersSubject names appear only here
*.job.tsBackground work, cronIdempotency is mandatory
*.pure.tsPure domain computationZero I/O. The legal home for what used to be dumped in utils

*.tsx is a component by definition and needs no role. Framework-mandated names (page, layout, route, middleware, mod) stay as they are. A package's public entry is public.ts.

Banned filenames

utils util helpers helper common shared misc lib
main types constants service manager handler index

Every one of them promises a dumping ground: within six months a file with that name holds two unrelated things, and nobody can delete it because nobody knows who uses it.

Directories are vertical slices

Layers: you see the framework
src/controllers/track.ts
src/services/track.ts
src/types/track.ts
src/utils/track.ts
Domains: you see the product
src/track/track.route.ts
src/track/track.store.ts
src/track/track.shape.ts
src/track/track.pure.ts

A directory is named after a domain, not after a technical nature. Directories named utils helpers common shared misc lib core services controllers managers handlers are banned. A listing of src/ should show what the product does, not which framework it is written in.


Comments

A comment answers "why", never "what". The "what" is already written on the line below, and unlike the comment it does not lie.

Five tags are allowed:

ts
// why: Stripe sends the webhook before the object is readable through the API
// perf: 40k rows — a Map beats a linear scan by two orders of magnitude
// safety: constant-time comparison, the value comes from outside
// spec: RFC 8628 §3.5
// ref: incident 2026-08-04, production rollback

A continuation line of a multi-line comment does not repeat the tag. Block comments are JSDoc above an export only; /* ... */ around code means the code should be deleted, not commented out.

Banned

The usual way
// increment the counter
count += 1

// ============================
// Step 1: validate the input
// TODO: rewrite this
GRAIN
count += 1

// why: the counter moves before the write — otherwise it races the worker
// grain:allow unit-suffix — field of an external Stripe contract

Comments that restate code, TODO / FIXME / HACK, numbered steps, ASCII dividers, emoji — in comments and in logs alike: a log is read by grep, not by a person.

A deliberate exception to any rule is written down, with a reason:

ts
// grain:allow unit-suffix — field of an external Stripe contract, cannot be renamed

The reason is mandatory — without it the checker complains about the escape itself. That turns a violation from carelessness into a decision someone can argue with.

Files and comments | AS Docs