- сами конвенции переехали в conventions/, описательное — в корень: LANGUAGE.md (язык записи) и GUIDE.md (как ведут конвенции) - conv синхронизирует только conventions/, пути в origin даются относительно неё — раскладка копий в репозиториях не меняется
14 KiB
Язык конвенций
Как записываются правила в этом каноне. Документ описывает форму, а не содержание: что такое правило, чем оно отличается от прозы вокруг и как на него сослаться.
Форма подсмотрена у OpenSpec, но взято оттуда не всё — см. «Чего мы не берём».
Зачем формализовать
Не ради строгости. Три конкретные вещи, которые без адресуемых правил не работают:
- Механизация. Регион
механизированодолжен говорить «правило R4 проверяетarchrules», а не «AUTOINCREMENTв новых миграциях —archrules»: во втором случае читатель сам догадывается, к какому утверждению это относится, и догадывается по-разному. - Отступления. «У нас не так» бесполезно, пока не сказано, что именно «не так». Со ссылкой на правило отступления становятся счётными: видно, сколько правил конвенции репозиторий реально не соблюдает.
- Промоут находки. Путь «находка → конвенция → правило линтера → удаление прозы» требует ручки, за которую берут конкретное правило.
Общий знаменатель — идентификатор. Модальные слова и таблицы полезны, но вторичны.
Единица — правило
### R5. Разбор внешнего идентификатора на границе
**ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса
к базе.
**Почему.** Разбор валидирует формат и нормализует регистр. Сравнение строк
в базе побайтовое, поэтому без нормализации запрос молча не находит
существующую запись — отладка такого случая стоит дороже, чем сам разбор.
Четыре обязательные части: номер, заголовок, модальность с нормой, почему. Норма — одна фраза; если в неё не влезает, это два правила.
Правило без «почему» не принимается
Это жёсткое требование к форме, а не пожелание. Причины:
- «Почему» — единственный способ понять, когда правило перестало действовать. Норма стареет молча; обоснование стареет заметно. Когда причина отпала, видно, что правило пора убрать, а не соблюдать по инерции.
- Правило без обоснования не переживает спор. Через год ни автор, ни агент не восстановят мотив, и правило будет либо отменено первым же возражением, либо соблюдено там, где вредит.
- Формулировка «почему» — проверка на то, что это вообще правило. Если причина не формулируется, перед нами привычка или вкусовщина; ей место в черновиках, а не в конвенции.
«Почему» отвечает на «что сломается, если сделать иначе», а не пересказывает норму другими словами. «Потому что так принято» — не обоснование.
Модальные слова
Пишутся капсом — это ключевые слова, а не обычный текст.
| Слово | Значение | Отступление |
|---|---|---|
| ДОЛЖЕН | нарушение считается ошибкой | только с записью в регион отступления |
| НЕ ДОЛЖЕН | запрет | то же |
| СЛЕДУЕТ | сильная рекомендация; новый код пишем так | допустимо, причину записываем |
| НЕ СЛЕДУЕТ | обратное к СЛЕДУЕТ | то же |
| ДОПУСКАЕТСЯ | явное разрешение | не требуется — правило ничего не запрещает |
ДОПУСКАЕТСЯ нужно не для симметрии: оно снимает вопрос «а так можно?» там, где соседнее правило звучит строго и его легко перечитать шире, чем задумано.
Модальность живёт на правиле, а не на файле. Прежний файловый статус
(status: рекомендуемая / обязательная в шапке) отменён: он неизбежно
врал, потому что один файл смешивает жёсткие требования с советами. В шапке
остаются только extends и служебные ключи копии.
Мы не используем SHALL и прочие английские ключевые слова. Они заняты спецификациями (OpenSpec), и общий словарь стирал бы границу «конвенция — не capability». Разный словарь эту границу держит бесплатно.
Идентификаторы
- Формат —
R<номер>, сквозная нумерация внутри файла, начиная сR1. - Строка таблицы, если на неё нужно ссылаться отдельно, —
R5.1,R5.2. - Номера стабильны и не переиспользуются. Удалённое правило оставляет дыру в нумерации; занимать её новым правилом нельзя — иначе ссылка из чужого репозитория начнёт указывать на другое утверждение.
- Глобальный адрес — путь файла плюс номер:
arch/db-identifiers.md R5. В пределах одного файла достаточноR5.
Порядок правил в файле выбирается по читаемости, не по номерам: номер — это идентификатор, а не позиция.
Правило, чья норма уехала в линтер
Когда правило механизировано у всех потребителей, его норма из канона удаляется, а обоснование — нет. Остаётся правило без модальности, и чтобы оно не выглядело недописанным, место нормы занимает отметка:
### R6. Дефолтов времени в схеме БД нет
**МЕХАНИЗИРОВАНО.** Проверяется общим правилом линтера; формулировка
удалена, потому что дублировала работающую проверку.
**Почему.** Дефолт превращает забытую вставку в тихо работающий код…
- Номер и заголовок сохраняются: ссылки из репозиториев продолжают указывать на то же утверждение.
- «Почему» остаётся навсегда — линтер сообщает, что нарушено, но не сообщает, зачем правило существует, и без обоснования нельзя понять, когда проверку пора отменять.
- МЕХАНИЗИРОВАНО — не шестое модальное слово: оно не задаёт обязательность, а сообщает, что обязательность теперь обеспечена машиной. В остальном такое правило равно ДОЛЖЕН.
Факт «механизировано у всех» устанавливается вручную: канон по построению не знает списка подписчиков, и обойти репозитории перед удалением нормы — часть работы, а не то, что можно проверить автоматически.
Таблицы вместо сценариев
Часть правил классифицирует ситуации: какой уровень лога, какая категория директории, что делать с невалидным вводом в зависимости от его источника. Для них каноническая форма — таблица «ситуация → вердикт», строки которой при необходимости нумеруются.
Таблица плотнее прозы и не даёт пропустить ветку: пустая клетка видна, а неупомянутый случай в абзаце — нет.
Чего мы не берём из OpenSpec
GIVEN/WHEN/THEN. У спецификации субъект — система, и её поведение разворачивается во времени: состояние, событие, исход. У конвенции субъект — автор кода, и разворачивать нечего: есть ситуация выбора и вердикт. Это таблица, а не траектория.
SHALL. См. выше про словарь.
Сценарии как общая форма. Прозаический сценарий остаётся точечным инструментом — для стыка правил, когда два правила вместе дают неочевидный результат:
WHEN зависимость недоступна и ретраи вызова исчерпаны → ext-запись ERROR
AND тик фонового цикла упал по той же причине → доменная запись WARN
Такой блок ставится после обоих правил и ссылается на их номера. Если стыков нет — сценариев в файле нет.
Что правилом не является
Модальные слова в этих частях не употребляются — иначе перестанет быть понятно, что адресуемо, а что нет:
- Область действия — на что конвенция распространяется во времени («новые таблицы; существующие не переписываются»). Это рамка для всех правил файла, а не правило.
- Связано — ссылки на смежные конвенции, ADR, код.
- Локальные регионы — содержимое принадлежит репозиторию.
- Вводная проза, объясняющая предмет конвенции.
Как на правила ссылаются копии
В репозитории:
<!-- local:механизировано -->
R2, R4 — `internal/archrules` (проверяются в новых миграциях).
<!-- /local -->
<!-- local:отступления -->
R6 — не соблюдается в легаси-таблицах `show_history`, `queue`: составные
ключи там появились до конвенции, переписывание требует миграции данных.
<!-- /local -->
Отсюда видно и то, чего раньше не было видно: конвенция из восьми правил, из которых два механизированы и одно не соблюдается.
Что стоит проверять машиной
Сейчас не реализовано; список — на будущее для conv:
- номера уникальны внутри файла и не имеют пропусков вниз (новое правило берёт следующий свободный, а не первый освободившийся);
- у каждого
### R<n>есть модальное слово (или отметка МЕХАНИЗИРОВАНО) и блок «Почему»; - ссылки вида
R<n>в локальных регионах копии указывают на правила, которые в каноне ещё существуют; - модальные слова не встречаются вне правил.
Порядок перевода
Все конвенции канона записаны на этом языке. Новая конвенция пишется на нём сразу; смешение форм в каноне больше не предполагается.