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