- LANGUAGE.md: раздел «Идентификаторы» переписан под префиксы, в список машинных проверок добавлена сверка с реестром - GUIDE.md перенумерован под префикс META, «номер правила» заменён на «идентификатор»
260 lines
20 KiB
Markdown
260 lines
20 KiB
Markdown
---
|
||
prefix: META
|
||
---
|
||
|
||
# Как мы ведём конвенции
|
||
|
||
Конвенция описывает повторяющийся выбор: как называть директории, как
|
||
раскладывать данные, как оформлять ошибки. Она отвечает на вопрос «как
|
||
принято», а не «что здесь происходит».
|
||
|
||
Как записывается сама конвенция — правила, модальность, обоснования — в
|
||
[LANGUAGE.md](LANGUAGE.md). Здесь — про то, зачем конвенции заводятся, где
|
||
живут и как соотносятся с соседними видами документов.
|
||
|
||
## Область действия
|
||
|
||
Правила ниже адресованы автору конвенции — тому, кто заводит новый файл или
|
||
правит существующий, в каноне и в копиях репозиториев. Обязательность живёт
|
||
на отдельном правиле, а не на файле; шкала модальных слов — в
|
||
[LANGUAGE.md](LANGUAGE.md).
|
||
|
||
## Отличие от соседей
|
||
|
||
- `docs/adr/` — **решение**, принятое однажды и постфактум («почему выбрали
|
||
Authelia, а не Keycloak»). Запись неизменяема.
|
||
- `docs/specs/` и OpenSpec, где они есть, — **что** система делает,
|
||
наблюдаемое поведение как контракт. Конвенция — **как** написан код;
|
||
в спеки она не переносится, это не capability.
|
||
- `docs/drafts/` — оперативная хроника и черновики, «что собираюсь сделать».
|
||
- `docs/conventions/` — **правило на будущее**, применяемое многократно.
|
||
Живой документ: правится, когда договорённость меняется.
|
||
|
||
## Оформление
|
||
|
||
Имя файла — kebab-case по теме: `app-directories.md`. Правилом это не
|
||
записано: обоснование сводится к «чтобы имя файла в реестре префиксов
|
||
писалось одним способом», а проверить нарушение всё равно проще глазом, чем
|
||
сформулировать норму. Номер META-16, под которым это правило существовало,
|
||
оставлен свободным и не переиспользуется.
|
||
|
||
## Канон и копии
|
||
|
||
Файлы в `docs/conventions/` с шапкой `origin:` — копии из общего канона
|
||
`dev-conventions`, а не собственные документы репозитория. Репозиторное
|
||
живёт только внутри локальных регионов `<!-- local:имя --> … <!-- /local -->`:
|
||
они исключены из сравнения с каноном, и расхождение по ним — норма, а не
|
||
дрейф. Правка вне регионов означает одно из двух: улучшение, которое
|
||
возвращают в канон, или сознательное расхождение, записанное в ключ `local:`
|
||
шапки. Состояние копий показывает `conv status`, различия — `conv diff`,
|
||
обновление из канона — `conv pull`; всё через раннер репозитория.
|
||
|
||
Имя региона обязательно и стабильно: перенос содержимого при обновлении
|
||
идёт по именам, и переименование осиротит содержимое во всех копиях.
|
||
|
||
## Правила
|
||
|
||
### META-1. Одна конвенция — один файл
|
||
|
||
**ДОЛЖЕН.** Файл описывает ровно один повторяющийся выбор.
|
||
|
||
**Почему.** Подписка — это набор лежащих в репозитории файлов, и берут файл
|
||
целиком. Файл, собравший две темы, вынуждает репозиторий взять правила,
|
||
которые ему не нужны, и получать шум в `diff` по чужой половине. Разрезать
|
||
позже дорого: путь файла — часть адреса правила, и после разреза внешние
|
||
ссылки указывают не туда.
|
||
|
||
### META-2. Конвенция заводится, когда решение принимается третий раз
|
||
|
||
**СЛЕДУЕТ.** Поводом служит одно и то же решение, принятое третий раз и
|
||
каждый раз чуть по-другому.
|
||
|
||
**Почему.** По одному-двум случаям не видно, что в решении повторяется, а
|
||
что было частностью места: правило, выведенное из первого случая, кодирует
|
||
частность и дальше мешает больше, чем помогает. Единичный выбор, к тому же
|
||
спорный, читателю нужен вместе с мотивом — это ADR, где мотив и есть
|
||
содержание записи.
|
||
|
||
### META-3. Новая конвенция пишется там, где заболело
|
||
|
||
**СЛЕДУЕТ.** Первые шаги пути «находка → конвенция → правило линтера →
|
||
удаление прозы» делаются в репозитории, где случилась находка; в канон
|
||
продвигается общая часть.
|
||
|
||
**Почему.** Правило, написанное сразу в общем виде, не проверено ни одним
|
||
применением, и условие применимости у него придумано, а не найдено, —
|
||
платят за это все потребители сразу. Формулировка, обкатанная на одном
|
||
репозитории, приезжает в канон уже с известной границей.
|
||
|
||
### META-4. В тексте конвенции нет утверждений о состоянии репозитория
|
||
|
||
**НЕ ДОЛЖЕН.** Норма пишется в настоящем предписывающем времени, без
|
||
описаний того, как сейчас устроен конкретный репозиторий.
|
||
|
||
**Почему.** Такое утверждение устаревает молча и подменяет норму описанием:
|
||
читатель перестаёт понимать, что от него требуется, а что просто
|
||
констатировано. В копиях у остальных потребителей чужой факт вдобавок ложен
|
||
с первого дня. «Так сделано у нас» — содержимое региона отступлений, где оно
|
||
и локально, и проверяемо.
|
||
|
||
### META-5. Расхождение кода с правилом — отступление, а не повод переписать правило
|
||
|
||
**ДОЛЖЕН.** Правило правится, только когда неверно по существу: содержит
|
||
фактическую ошибку, внутреннее противоречие или условие применимости,
|
||
которое не даёт ответа.
|
||
|
||
**Почему.** Правило, подогнанное под текущий код, перестаёт что-либо
|
||
требовать — оно описывает то, что и так происходит, и первое же расхождение
|
||
переписывает его снова. Направление «конвенция → код» держится ровно тем,
|
||
что факт не считается аргументом.
|
||
|
||
### META-6. `ДОЛЖЕН` без механической проверки либо механизируется, либо понижается
|
||
|
||
**ДОЛЖЕН.** Правило, нарушение которого объявлено ошибкой, получает
|
||
машинную проверку или переводится в СЛЕДУЕТ.
|
||
|
||
**Почему.** Без проверки правило держится на внимании: нарушения копятся
|
||
молча и всплывают выборочно — на том ревью, куда дошли руки. Для СЛЕДУЕТ
|
||
это честно, а ДОЛЖЕН при этом обещает то, чего не делает, и через несколько
|
||
таких случаев обесценивает остальные ДОЛЖЕН в файле.
|
||
|
||
Отсюда следствие: правило, машинная проверка которого невозможна в принципе
|
||
(вкус формулировки, выбор границы, суждение о ситуации), не может быть
|
||
ДОЛЖЕН — его модальность СЛЕДУЕТ по построению, а не по слабости.
|
||
|
||
### META-7. Факт механизации фиксируется в локальном регионе со ссылкой на правило
|
||
|
||
**ДОЛЖЕН.** Регион `механизировано` называет идентификатор правила и
|
||
конкретную проверку.
|
||
|
||
**Почему.** Механизация — состояние конкретного репозитория, канон о ней не
|
||
знает, а без записи следующий автор либо заведёт вторую проверку того же,
|
||
либо будет вычитывать глазами уже проверенное машиной. Без идентификатора
|
||
читатель догадывается сам, к какому утверждению относится проверка, — и
|
||
догадывается по-разному.
|
||
|
||
### META-8. Формулировка не удаляется из канона, пока механизирована не у всех
|
||
|
||
**НЕ ДОЛЖЕН.** Норма остаётся в каноне, пока хотя бы у одного потребителя
|
||
машинной проверки нет.
|
||
|
||
**Почему.** У кого линтера нет, тот после удаления остаётся без правила
|
||
вообще: ни проверки, ни текста. Механизация у одного потребителя ничего не
|
||
говорит о прочих, поэтому удалять по факту «у нас уже проверяется» —
|
||
значит чинить свой файл за чужой счёт.
|
||
|
||
Списка подписчиков канон по построению не знает, поэтому факт «механизировано
|
||
у всех» устанавливается обходом репозиториев вручную — это часть работы по
|
||
удалению нормы, а не то, что можно проверить машиной (см. `LANGUAGE.md`,
|
||
состояние МЕХАНИЗИРОВАНО).
|
||
|
||
### META-9. Общая механизация разрешает удалить норму из канона
|
||
|
||
**ДОПУСКАЕТСЯ.** Правило, уехавшее в общий конфиг линтера или в общую роль,
|
||
удаляется из канона одним `push`.
|
||
|
||
**Почему.** Формулировка, дублирующая работающую у всех проверку,
|
||
размазывает внимание: файл на несколько сотен строк заставляет человека и
|
||
агента добросовестно вычитывать тривиальное именование и не доходить до
|
||
формы решения. Явное разрешение нужно, чтобы META-8 не читался как запрет
|
||
удалять вообще.
|
||
|
||
### META-10. Обоснование не удаляется никогда
|
||
|
||
**НЕ ДОЛЖЕН.** Блок «Почему» остаётся и после того, как норма уехала в
|
||
линтер.
|
||
|
||
**Почему.** Линтер сообщает, что нарушено, но не сообщает, зачем правило
|
||
существует. Без обоснования не видно, когда причина отпала, — проверка
|
||
продолжает работать по инерции, и возразить ей нечем, кроме как отключив.
|
||
|
||
### META-11. У трудноизменяемого слоя область действия пишется явно
|
||
|
||
**ДОЛЖЕН.** Конвенция о схеме БД, формате хранения или раскладке директорий
|
||
называет, к чему применяется: к новым таблицам и миграциям, а не к
|
||
состоянию схемы.
|
||
|
||
**Почему.** Здесь не работает привычное «новое пишем правильно, старое
|
||
переезжает по мере касания»: таблица не переезжает от того, что её
|
||
потрогали. Без явной рамки правило читается как требование к текущему
|
||
состоянию, и оба исхода плохи — миграция живых данных ради опрятности либо
|
||
молчаливый вывод, что конвенция не соблюдается совсем.
|
||
|
||
### META-12. Механизируется граница изменения, а не состояние
|
||
|
||
**ДОЛЖЕН.** Проверка запрещает нарушение в новых миграциях, а не в уже
|
||
существующей схеме.
|
||
|
||
**Почему.** Проверка состояния краснеет на легаси с первого дня: её
|
||
отключают или обвешивают вечным списком исключений — и она перестаёт ловить
|
||
новое, ради чего заводилась. Проверка границы оставляет старое в покое и
|
||
делает новую ошибку невозможной.
|
||
|
||
### META-13. Список отступлений трудноизменяемого слоя — постоянный
|
||
|
||
**ДОЛЖЕН.** Перечисленные старые таблицы и раскладки живут как есть, а не
|
||
как задачи на дочистку.
|
||
|
||
**Почему.** Список, записанный долгом, требует либо мигрировать живые данные
|
||
без выгоды, либо год за годом объяснять невыполненный план. Второе кончается
|
||
тем, что список перестают вести, — и пропадает единственное место, где видно,
|
||
где именно правило не действует.
|
||
|
||
### META-14. Отступления перечисляются поимённо, со ссылкой на правила
|
||
|
||
**ДОЛЖЕН.** В локальном регионе перечислены отступления, которые уже есть в
|
||
коде, с идентификатором правила и причиной.
|
||
|
||
**Почему.** Иначе репозиторий выглядит соблюдающим конвенцию, а проверить
|
||
это можно только чтением всего кода. Со ссылками отступления счётны: видно,
|
||
сколько правил конвенции репозиторий реально не соблюдает. Пустой регион при
|
||
этом почти всегда означает не отсутствие отступлений, а то, что их не искали.
|
||
|
||
### META-15. Запись в регионе отступлений разбирается по масштабу
|
||
|
||
**ДОЛЖЕН.** Судьба записи зависит от того, что в ней сказано:
|
||
|
||
| № | Что записано | Куда идёт |
|
||
|---|---|---|
|
||
| META-15.1 | правилу не следуем в перечисленных местах, причина названа | остаётся отступлением |
|
||
| META-15.2 | правило не применяется в репозитории целиком | чинится условие применимости — в каноне |
|
||
| META-15.3 | конвенция репозиторию не нужна | копия удаляется, подписки нет |
|
||
|
||
**Почему.** Отступление описывает исключение, и по нему видно, какая часть
|
||
правила нарушена. Запись «мы это правило вообще не применяем» такой
|
||
информации не несёт и маскирует одну из двух чинимых причин: неверную рамку
|
||
правила в каноне, которую чинят один раз для всех, или лишнюю подписку,
|
||
где файл просто не нужен. Оставленная отступлением, она прячет обе.
|
||
|
||
### META-17. Репо-специфичная часть «Связано» — в локальном регионе
|
||
|
||
**ДОЛЖЕН.** Раздел «Связано» стоит в конце файла; канонические ссылки — в
|
||
общем тексте, ссылки на ADR, код и файлы конкретного репозитория — в
|
||
локальном регионе.
|
||
|
||
**Почему.** Общий текст уезжает `push`-ем ко всем потребителям, и ссылка на
|
||
чужой файл у них битая с первого дня. Регион исключён из сравнения, поэтому
|
||
та же ссылка внутри него никого не задевает и не даёт вечного шума в `diff`.
|
||
|
||
### META-18. README директории перечисляет конвенции с однострочным описанием
|
||
|
||
**СЛЕДУЕТ.** Одна плоская таблица: файл и строка о том, про что он.
|
||
|
||
**Почему.** Подписка — это набор лежащих файлов, и без описаний вопрос
|
||
«какая из них про мой случай» решается открыванием каждой. Ценой в десяток
|
||
файлов это означает, что не открывают ни одной.
|
||
|
||
### META-19. Короткие инварианты дублируются в точку входа агента
|
||
|
||
**ДОЛЖЕН.** В `AGENTS.md` / `CLAUDE.md` едет одна строка на правило с его
|
||
идентификатором; детали остаются в конвенции.
|
||
|
||
**Почему.** Сама по себе конвенция агенту не видна: он дойдёт до неё, только
|
||
если его туда отправили, — а безусловно он читает точку входа. Строка с
|
||
идентификатором служит и напоминанием, и адресом, по которому за
|
||
подробностями идут; перенос деталей туда же вернул бы задачу поддержки двух
|
||
текстов.
|
||
|
||
<!-- local:точки-входа -->
|
||
<!-- /local -->
|