Files
dev-conventions/common/language.md
T
av 22c6855968 common: добавлен язык записи конвенций
- правило = номер + модальность (ДОЛЖЕН / СЛЕДУЕТ / ДОПУСКАЕТСЯ) плюс
  обязательный блок «Почему»; из OpenSpec взяты идентификаторы правил, но
  не SHALL и не GIVEN/WHEN/THEN
- файловый статус отменён: обязательность живёт на правиле, а в шапке ещё
  не переведённых конвенций ключ остаётся меткой «это проза»
2026-07-25 18:56:29 +03:00

12 KiB
Raw Blame History

Язык конвенций

Как записываются правила в этом каноне. Документ описывает форму, а не содержание: что такое правило, чем оно отличается от прозы вокруг и как на него сослаться.

Форма подсмотрена у OpenSpec, но взято оттуда не всё — см. «Чего мы не берём».

Зачем формализовать

Не ради строгости. Три конкретные вещи, которые без адресуемых правил не работают:

  • Механизация. Регион механизировано должен говорить «правило R4 проверяет archrules», а не «AUTOINCREMENT в новых миграциях — archrules»: во втором случае читатель сам догадывается, к какому утверждению это относится, и догадывается по-разному.
  • Отступления. «У нас не так» бесполезно, пока не сказано, что именно «не так». Со ссылкой на правило отступления становятся счётными: видно, сколько правил конвенции репозиторий реально не соблюдает.
  • Промоут находки. Путь «находка → конвенция → правило линтера → удаление прозы» требует ручки, за которую берут конкретное правило.

Общий знаменатель — идентификатор. Модальные слова и таблицы полезны, но вторичны.

Единица — правило

### R5. Разбор внешнего идентификатора на границе

**ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса
к базе.

**Почему.** Разбор валидирует формат и нормализует регистр. Сравнение строк
в базе побайтовое, поэтому без нормализации запрос молча не находит
существующую запись — отладка такого случая стоит дороже, чем сам разбор.

Четыре обязательные части: номер, заголовок, модальность с нормой, почему. Норма — одна фраза; если в неё не влезает, это два правила.

Правило без «почему» не принимается

Это жёсткое требование к форме, а не пожелание. Причины:

  • «Почему» — единственный способ понять, когда правило перестало действовать. Норма стареет молча; обоснование стареет заметно. Когда причина отпала, видно, что правило пора убрать, а не соблюдать по инерции.
  • Правило без обоснования не переживает спор. Через год ни автор, ни агент не восстановят мотив, и правило будет либо отменено первым же возражением, либо соблюдено там, где вредит.
  • Формулировка «почему» — проверка на то, что это вообще правило. Если причина не формулируется, перед нами привычка или вкусовщина; ей место в черновиках, а не в конвенции.

«Почему» отвечает на «что сломается, если сделать иначе», а не пересказывает норму другими словами. «Потому что так принято» — не обоснование.

Модальные слова

Пишутся капсом — это ключевые слова, а не обычный текст.

Слово Значение Отступление
ДОЛЖЕН нарушение считается ошибкой только с записью в регион отступления
НЕ ДОЛЖЕН запрет то же
СЛЕДУЕТ сильная рекомендация; новый код пишем так допустимо, причину записываем
НЕ СЛЕДУЕТ обратное к СЛЕДУЕТ то же
ДОПУСКАЕТСЯ явное разрешение не требуется — правило ничего не запрещает

ДОПУСКАЕТСЯ нужно не для симметрии: оно снимает вопрос «а так можно?» там, где соседнее правило звучит строго и его легко перечитать шире, чем задумано.

Модальность живёт на правиле, а не на файле. Прежний файловый статус (status: рекомендуемая / обязательная в шапке) отменён: он неизбежно врал, потому что один файл смешивает жёсткие требования с советами. В ещё не переведённых конвенциях ключ остаётся как метка «этот файл — проза», без нормативного значения, и исчезает при переводе.

Мы не используем SHALL и прочие английские ключевые слова. Они заняты спецификациями (OpenSpec), и общий словарь стирал бы границу «конвенция — не capability». Разный словарь эту границу держит бесплатно.

Идентификаторы

  • Формат — R<номер>, сквозная нумерация внутри файла, начиная с R1.
  • Строка таблицы, если на неё нужно ссылаться отдельно, — R5.1, R5.2.
  • Номера стабильны и не переиспользуются. Удалённое правило оставляет дыру в нумерации; занимать её новым правилом нельзя — иначе ссылка из чужого репозитория начнёт указывать на другое утверждение.
  • Глобальный адрес — путь файла плюс номер: arch/db-identifiers.md R5. В пределах одного файла достаточно R5.

Порядок правил в файле выбирается по читаемости, не по номерам: номер — это идентификатор, а не позиция.

Таблицы вместо сценариев

Часть правил классифицирует ситуации: какой уровень лога, какая категория директории, что делать с невалидным вводом в зависимости от его источника. Для них каноническая форма — таблица «ситуация → вердикт», строки которой при необходимости нумеруются.

Таблица плотнее прозы и не даёт пропустить ветку: пустая клетка видна, а неупомянутый случай в абзаце — нет.

Чего мы не берём из 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> в локальных регионах копии указывают на правила, которые в каноне ещё существуют;
  • модальные слова не встречаются вне правил.

Порядок перевода

Конвенции переводятся на этот язык по мере касания, а не кампанией. Смешение форм в каноне допустимо: пока файл не тронут, он остаётся прозой со статусом в шапке.

Сейчас на формальном языке записаны arch/db-identifiers.md и stack/ansible/app-directories.md.