Файлы и каталоги
Роль в имени файла
<домен>.<роль>.ts — роль видна в листинге каталога, файл не нужно открывать.
| Роль | Содержимое | Обещание |
|---|---|---|
*.entry.ts | Точка входа процесса | Единственное место, где собирается граф зависимостей |
*.route.ts | HTTP-эндпоинты | Только разбор запроса и вызов домена |
*.rpc.ts | gRPC-методы | То же для RPC |
*.store.ts | Доступ к БД | Единственное место, где есть SQL или ORM |
*.wire.ts | Клиент к чужому API | Единственное место, где живёт чужой формат |
*.policy.ts | Правила доступа и инварианты | Чисто, без I/O — правила тестируются без БД |
*.shape.ts | Типы и схемы валидации | Ничего исполняемого |
*.event.ts | Контракты событий, продюсеры и консьюмеры | Имена сабджектов только здесь |
*.job.ts | Фоновые задачи, крон | Идемпотентность обязательна |
*.pure.ts | Чистые вычисления домена | Ноль I/O. Легальное место для того, что раньше сваливали в utils |
*.tsx — компонент по определению, роль не нужна. Имена, продиктованные фреймворком (page, layout, route, middleware, mod), остаются как есть. Публичный вход пакета — public.ts.
Запрещённые имена файлов
utils util helpers helper common shared misc lib
main types constants service manager handler indexКаждое из них — обещание свалки: файл с таким именем через полгода содержит две несвязанные вещи, и его никто не может удалить, потому что непонятно, кто им пользуется.
Каталоги — вертикальные срезы
src/controllers/track.ts
src/services/track.ts
src/types/track.ts
src/utils/track.tssrc/track/track.route.ts
src/track/track.store.ts
src/track/track.shape.ts
src/track/track.pure.tsКаталог называется по домену, не по технической природе. Запрещены каталоги utils helpers common shared misc lib core services controllers managers handlers. По листингу src/ должно быть видно, что делает продукт, а не на каком фреймворке он написан.
Комментарии
Комментарий отвечает на «почему», никогда на «что». «Что» уже написано в коде строкой ниже, и оно не врёт — в отличие от комментария.
Разрешены пять тегов:
// why: Stripe шлёт webhook раньше, чем отдаёт объект в API
// perf: 40k строк — Map быстрее линейного поиска на два порядка
// safety: сравнение constant-time, значение приходит извне
// spec: RFC 8628 §3.5
// ref: инцидент 2026-08-04, откат продаПродолжение многострочного комментария тег не повторяет. Блочный комментарий — только JSDoc над экспортом; /* ... */ вокруг кода означает, что код надо удалить, а не закомментировать.
Запрещено
// increment the counter
count += 1
// ============================
// Step 1: validate the input
// TODO: переписатьcount += 1
// why: счётчик обновляется до записи — иначе гонка с воркером
// grain:allow unit-suffix — поле внешнего контракта StripeКомментарий-пересказ кода, TODO / FIXME / HACK, нумерация шагов, ASCII-разделители, эмодзи — в комментариях и в логах: лог читает grep, а не человек.
Осознанное исключение из любого правила оформляется явно и с причиной:
// grain:allow unit-suffix — поле внешнего контракта Stripe, переименовать нельзяПричина обязательна — без неё чекер ругается на сам escape. Это превращает нарушение из небрежности в решение, которое можно обсудить.