- META-37: тема называется решением и адресатом, а не ролью части проекта; логи сервера и браузера — logging и client-logging, а не суффиксная пара - META-38: ось слоя объявляется ключами lang/stack в шапке, а не выводится из пути — переезд файла между директориями иначе молча менял состав копии у каждого потребителя; шапки двенадцати конвенций приведены к правилу - в список проверок добавлены объявление оси, единственность базового слоя и совпадение объявленного с директорией
646 lines
53 KiB
Markdown
646 lines
53 KiB
Markdown
---
|
||
version: 1
|
||
---
|
||
|
||
# Язык конвенций
|
||
|
||
Формальный язык, на котором записаны правила этого канона: что считается
|
||
правилом, чем оно отличается от прозы вокруг, какими словами задаётся
|
||
обязательность и как на правило сослаться извне.
|
||
|
||
Версия языка — **1**. Номер называется в каждой конвенции: словарь может
|
||
пополниться, и текст, написанный по предыдущей версии, должен читаться по
|
||
той, по которой написан.
|
||
|
||
Документ адресован автору набора и в репозиторий-потребитель не едет. К
|
||
читателю копии едет короткое `READING.md`: словарь со значениями, форма
|
||
правила и её граница, ссылки, локальная часть — без разделов о ведении набора.
|
||
Словарь в двух документах обязан совпадать (META-30), и это единственное
|
||
место, где между ними возможен дрейф.
|
||
|
||
Описание языка ни на один набор конвенций не опирается, поэтому все примеры
|
||
здесь — вымышленные правила с префиксами на `X` (`XKEY`, `XMIG`, `XLOG`).
|
||
Такие префиксы канон не занимает никогда, в манифесте набора их нет — значит
|
||
пример не спутать с настоящим правилом, а перенумерация конвенций описание
|
||
языка не задевает.
|
||
|
||
## Опора на стандарты
|
||
|
||
Язык не выводится из вкуса автора. Каждое решение о форме взято из
|
||
документа, где эта задача уже решена и обкатана, и отклонения от источника
|
||
названы явно.
|
||
|
||
| Источник | Что взято | Что отклонено |
|
||
|---|---|---|
|
||
| **BCP 14** (RFC 2119 + RFC 8174) | шкала модальности; правило «нормативно только заглавное»; сдержанность в высшей модальности; англоязычный словарь как готовый | синонимы `REQUIRED`, `RECOMMENDED`, `OPTIONAL`; `SHALL` как форма требования |
|
||
| **ISO/IEC Directives, Part 2** | разделение «требование — рекомендация — разрешение — возможность»; запрет модальных глаголов в утверждениях о факте | списки равнозначных словесных форм («is to», «is required to», «only … is permitted») |
|
||
| **ISO/IEC/IEEE 29148** | характеристики хорошего требования — единичность и проверяемость; обоснование и метод верификации как отдельные атрибуты требования | остальной аппарат требований: приоритеты, источники, матрицы трассируемости |
|
||
| **DMN** | таблица решений с объявленной политикой совпадения | исполняемая семантика и всё, что предполагает движок решений |
|
||
| **EARS** | паттерн «нежелательное поведение» — в виде таблицы | шаблоны как форма записи правила: `WHEN`, `WHILE`, `WHERE` |
|
||
| **OpenSpec** | четырёхчастная форма правила; адресуемость правила идентификатором; форма «условие → следствие» для стыка правил | `SHALL`; `GIVEN/WHEN/THEN` как общая форма записи |
|
||
|
||
Три отклонения стоят объяснения, потому что выглядят как произвол.
|
||
|
||
**Синонимов нет.** BCP 14 держит `REQUIRED` рядом с `MUST` и `OPTIONAL`
|
||
рядом с `MAY` ради читаемости английской прозы. Одна форма записи на ступень
|
||
означает, что проверка «модальное слово употреблено вне правила» становится
|
||
перечислением, а не разбором синонимических рядов.
|
||
|
||
**`SHALL` не используется ни в каком словаре этого языка.** Слово занято
|
||
спецификациями (OpenSpec), и общая с ними форма стирала бы границу между
|
||
конвенцией и описанием поведения системы: `SHALL` в конвенции читался бы как
|
||
контракт, которого конвенция не даёт. Для англоязычного словаря это означает
|
||
выбор в пользу `MUST` из BCP 14, а не `shall` из ISO/IEC Directives.
|
||
|
||
**`GIVEN/WHEN/THEN` не берётся как общая форма.** У спецификации субъект —
|
||
система, и её поведение разворачивается во времени: состояние, событие,
|
||
исход. У конвенции субъект — автор кода, и разворачивать нечего: есть
|
||
ситуация выбора и вердикт. Это таблица, а не траектория. Тем же рассуждением
|
||
отклонены шаблоны EARS как форма записи правила, а взято из EARS другое —
|
||
сообщённое снижение числа дефектов после введения шаблонов. Вывод, что дело в
|
||
самой обязательности формы, а не в её конкретном виде, наш; он ниже, среди
|
||
усилений.
|
||
|
||
Исключение — стык правил, где субъект действительно система: там форма
|
||
«условие → следствие» берётся сознательно, вместе со служебными словами под
|
||
неё. Это единственное место, и оно описано в «Таблицах решений».
|
||
|
||
## Где источник усилен
|
||
|
||
Три решения идут дальше источника, и это наши решения, а не его требования.
|
||
Названы они отдельно, чтобы довод не подменялся ссылкой: спорить с ними нужно
|
||
по существу, а не со стандартом.
|
||
|
||
- **Обоснование обязательно.** В 29148 rationale — из списка рекомендуемых
|
||
атрибутов требования; обязательный костяк там другой, это характеристики
|
||
самого требования. Здесь правило без блока ПОЧЕМУ не принимается, потому что
|
||
конвенция живёт годами и переживает автора: норма без причины через год либо
|
||
отменяется первым возражением, либо соблюдается там, где вредит.
|
||
- **Полнота таблицы решений.** DMN даёт политику совпадения как именованный
|
||
атрибут, а полноты не требует: индикатор полноты был в первой версии
|
||
спецификации и из последующих убран, полноту проверяют валидаторы
|
||
инструментов. Здесь она требуется, потому что таблицу и заводят ради
|
||
видимости пропуска: неперечисленный случай в прозе не виден, а пустая
|
||
клетка видна.
|
||
- **Вывод про обязательность шаблона.** В EARS сообщается о снижении числа
|
||
дефектов в требованиях после введения шаблонов. Вывод, что выигрыш даёт сама
|
||
обязательность формы, а не её конкретный вид, — наш: он объясняет, почему мы
|
||
берём из EARS результат, но не берём сами шаблоны.
|
||
|
||
## Что даёт формализация
|
||
|
||
Адресуемое правило — не украшение формы, а условие работы трёх механизмов:
|
||
|
||
- **Механизация.** Запись о ней должна говорить «правило `XMIG-4` проверяет
|
||
`archrules`», а не «`AUTOINCREMENT` в новых миграциях — `archrules`»: во
|
||
втором случае читатель сам догадывается, к какому утверждению это
|
||
относится, и догадывается по-разному.
|
||
- **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно
|
||
«не так». Со ссылкой на правило отступления становятся счётными: видно,
|
||
сколько правил конвенции репозиторий реально не соблюдает.
|
||
- **Промоут находки.** Путь «находка → конвенция → правило линтера» требует
|
||
ручки, за которую берут конкретное правило.
|
||
|
||
Общий знаменатель — **идентификатор**. Модальные слова и таблицы полезны,
|
||
но вторичны.
|
||
|
||
## Единица — правило
|
||
|
||
```markdown
|
||
### XKEY-5. Разбор внешнего идентификатора на границе
|
||
|
||
**ДОЛЖЕН.** Идентификатор, пришедший снаружи, проходит разбор до запроса
|
||
к базе.
|
||
|
||
**ПОЧЕМУ.** Разбор валидирует формат и нормализует регистр. Сравнение строк
|
||
в базе побайтовое, поэтому без нормализации запрос молча не находит
|
||
существующую запись — отладка такого случая стоит дороже, чем сам разбор.
|
||
```
|
||
|
||
Четыре обязательные части: **идентификатор**, **заголовок**, **модальность
|
||
с нормой**, **обоснование под меткой ПОЧЕМУ**. Пятый блок, ПРИМЕРЫ,
|
||
необязателен — о нём ниже. Норма — одна фраза; если в неё не влезает, это два
|
||
правила. Требование единичности взято из ISO/IEC/IEEE 29148: составная норма
|
||
не проверяема целиком, и нарушение одной её половины нечем адресовать.
|
||
|
||
Метки правила — модальное слово, ПОЧЕМУ, ПРИМЕРЫ — пишутся заглавными и
|
||
принадлежат словарю набора: скелет правила читается одинаково в любом языке,
|
||
на который канон переведён.
|
||
|
||
**Правило кончается перед следующим заголовком.** Область правила — от его
|
||
заголовка до следующего заголовка любого уровня. Внутри области текст
|
||
принадлежит последнему открытому блоку: метка блок открывает, и блок длится
|
||
до следующей метки или до конца области.
|
||
|
||
```markdown
|
||
### XKEY-3. Заголовок правила
|
||
|
||
**ДОЛЖЕН.** Норма одной фразой.
|
||
|
||
| № | ситуация | вердикт | ← блок нормы: таблица уточняет её
|
||
|
||
**ПОЧЕМУ.** Причина.
|
||
|
||
Продолжение причины, пример, ← блок обоснования продолжается
|
||
ссылка на внешнюю практику.
|
||
|
||
### XKEY-4. Следующее правило ← здесь область кончилась
|
||
```
|
||
|
||
Отсюда три следствия:
|
||
|
||
- **Хвост после ПОЧЕМУ — обоснование** до следующей метки или до конца
|
||
области, а не безымянная часть правила и не проза вокруг. Требований в нём
|
||
не живёт: то, что подлежит исполнению, стоит в блоке нормы, где у него есть
|
||
модальность и адрес. Требование, оставленное
|
||
в хвосте, требованием не является — сослаться на него нельзя и отступление
|
||
от него записать нельзя.
|
||
- **Таблица и список после модальной метки — часть нормы.** Правило,
|
||
классифицирующее ситуации, ровно так и записывается («Таблицы решений»), а
|
||
вердикт из такой таблицы адресуется номером строки.
|
||
- **Проза — это то, что лежит вне областей правил.** Тем самым проверка
|
||
«заглавных модальных слов вне правил нет» становится реализуемой: границу
|
||
считает разметка, а не читательское суждение о том, где правило кончилось.
|
||
|
||
Заглавное модальное слово внутри области правила законно, когда это
|
||
упоминание ступени в обосновании («для СЛЕДУЕТ это честно»). Метку от
|
||
упоминания отличает положение: метка стоит первой в своём абзаце, полужирным
|
||
и с точкой.
|
||
|
||
## Примеры к правилу
|
||
|
||
Пятый блок правила — необязательный, под меткой ПРИМЕРЫ. В нём код,
|
||
показывающий норму в деле, обычно парой «плохо → хорошо». Стоит он после
|
||
обоснования: сначала требование, потом причина, потом иллюстрация.
|
||
|
||
````markdown
|
||
### XKEY-5. Внешний идентификатор разбирается до обращения к базе
|
||
|
||
**ДОЛЖЕН.** Значение, пришедшее снаружи, проходит разбор и нормализацию
|
||
раньше, чем по нему делается запрос.
|
||
|
||
**ПОЧЕМУ.** Синтаксически невалидное значение не может соответствовать
|
||
записи, поэтому поход в базу за ним — заведомо холостой.
|
||
|
||
**ПРИМЕРЫ.**
|
||
|
||
Плохо — строка уходит в запрос как пришла:
|
||
|
||
```go
|
||
row := db.QueryRow("select … where id = ?", r.PathValue("id"))
|
||
```
|
||
|
||
Хорошо — разбор на границе, запроса при неудаче нет:
|
||
|
||
```go
|
||
id, err := ident.Parse(r.PathValue("id"))
|
||
if err != nil {
|
||
return notFound(w)
|
||
}
|
||
row := db.QueryRow("select … where id = ?", id)
|
||
```
|
||
````
|
||
|
||
**Пример иллюстрирует норму, а не задаёт её.** Три следствия, ради которых
|
||
это сказано:
|
||
|
||
- **требований в блоке нет.** Всё, что подлежит исполнению, стоит в блоке
|
||
нормы; деталь примера — имя переменной, конкретная функция, форма ответа —
|
||
требованием не становится. Разошёлся пример с нормой — действует норма, а
|
||
пример правят;
|
||
- **это не готовый сниппет.** Код в примере сокращён до того, что показывает
|
||
правило: обработка ошибок, контекст, импорты в нём условны, и копировать его
|
||
дословно не нужно;
|
||
- **пример стареет быстрее нормы.** Он привязан к сегодняшнему API, поэтому
|
||
расхождение примера с текущим кодом — повод поправить пример, а не отменять
|
||
правило.
|
||
|
||
Блок необязателен: он окупается там, где норму словами описать дороже, чем
|
||
показать, — форма вызова, структура записи в логе, раскладка файла. У правила
|
||
про выбор границы или про уровень лога иллюстрировать нечего.
|
||
|
||
## Обоснование обязательно
|
||
|
||
Правило без блока ПОЧЕМУ не принимается. Это требование к форме, а не
|
||
пожелание. В 29148 обоснование — отдельный атрибут требования, но из
|
||
рекомендуемых; здесь оно обязательно, и вот почему:
|
||
|
||
- **Обоснование — единственный способ увидеть, что правило устарело.**
|
||
Норма стареет молча; причина стареет заметно. Когда причина отпала, видно,
|
||
что правило пора убрать, а не соблюдать по инерции.
|
||
- **Правило без обоснования не переживает спор.** Через год ни автор, ни
|
||
агент не восстановят мотив, и правило будет либо отменено первым же
|
||
возражением, либо соблюдено там, где вредит.
|
||
- **Формулировка обоснования — проверка на то, что это вообще правило.**
|
||
Если причина не формулируется, перед нами привычка или вкусовщина; ей
|
||
место в черновиках, а не в конвенции.
|
||
|
||
Обоснование отвечает на «что сломается, если сделать иначе», а не
|
||
пересказывает норму другими словами. «Потому что так принято» — не
|
||
обоснование.
|
||
|
||
Форма обоснования при этом ничем не ограничена: рамки здесь только
|
||
смысловые. Абзац может быть длинным, вести рассуждение, приводить пример,
|
||
ссылаться на стандарты, внешние практики и чужие проекты — на устройство
|
||
OpenTelemetry, на умолчания библиотек логирования, на процедуру миграции из
|
||
документации СУБД. Запрещённых слов и обязательной
|
||
структуры у обоснования нет, и заводить их не нужно: обязательность несёт
|
||
норма, а обоснование её объясняет — путаницу между этими двумя ролями
|
||
исключает правило о заглавных.
|
||
|
||
## Модальные слова
|
||
|
||
Инвариант языка — **шкала**: пять ступеней в четырёх категориях ISO/IEC
|
||
Directives, Part 2, по одной форме записи на ступень, заглавными. Какими
|
||
словами ступени названы — параметр естественного языка набора, а не часть
|
||
языка конвенций. Этот канон написан по-русски и несёт русский словарь.
|
||
|
||
Пишутся заглавными — это ключевые слова, а не обычный текст.
|
||
|
||
| Слово | Категория | Значение | Отступление |
|
||
|---|---|---|---|
|
||
| **ДОЛЖЕН** | требование | нарушение считается ошибкой | только с записью в отступления |
|
||
| **НЕ ДОЛЖЕН** | требование | запрет | то же |
|
||
| **СЛЕДУЕТ** | рекомендация | сильная рекомендация; новый код пишем так | допустимо, причину записываем |
|
||
| **НЕ СЛЕДУЕТ** | рекомендация | обратное к СЛЕДУЕТ | то же |
|
||
| **ДОПУСКАЕТСЯ** | разрешение | выбор за автором кода; возражение на ревью не принимается | не требуется — правило ничего не запрещает |
|
||
|
||
**Нормативно только заглавное написание.** Это правило RFC 8174, и оно
|
||
здесь по той же причине, по которой понадобилось там: без него каждое
|
||
строчное «должен» во вводной прозе становится предметом спора о том, норма
|
||
это или речь. Строчное слово нормой не является никогда, поэтому проза
|
||
свободна, а проверка «модальное слово вне правила» сводится к поиску
|
||
заглавных форм.
|
||
|
||
**Четвёртая категория ISO — возможность — ключевого слова не имеет.**
|
||
Утверждения о том, что бывает и что технически осуществимо, пишутся обычной
|
||
прозой и модальных слов не несут. Модальное слово в таком утверждении
|
||
превращает описание в норму, которую никто не собирался вводить.
|
||
|
||
**ДОПУСКАЕТСЯ адресовано рецензенту.** В BCP 14 у `MAY` есть вторая
|
||
половина, которую обычно не замечают: сторона, не выбравшая опцию, обязана
|
||
работать с той, что выбрала. В конвенции этому соответствует запрет
|
||
возражать: выбор, помеченный ДОПУСКАЕТСЯ, на ревью не обсуждается. Без этой
|
||
половины слово было бы удобством читателя, а не нормой, и не работало бы в
|
||
единственной точке, где у конвенции есть принуждение.
|
||
|
||
**ДОЛЖЕН требует двух условий сразу:**
|
||
|
||
1. нарушение причиняет названный вред, а не расходится со вкусом — META-25,
|
||
он же критерий BCP 14, где высшая модальность резервируется под то, что
|
||
действительно ломается, и не употребляется для навязывания метода;
|
||
2. вердикт о нарушении воспроизводим — META-6: по тексту правила двое
|
||
проверяющих приходят к одному ответу, иначе обязательность держится на
|
||
том, кто читал.
|
||
|
||
Не выполнено первое — правилу место в СЛЕДУЕТ или нигде. Не выполнено
|
||
второе — в СЛЕДУЕТ. Воспроизводимость сама по себе не повышает правило до
|
||
ДОЛЖЕН: проверяемых мелочей больше, чем важных вещей, и безразборное
|
||
повышение обесценивает шкалу быстрее, чем её отсутствие.
|
||
|
||
Модальность живёт на **правиле**, а не на файле. Файловый статус
|
||
(`status: рекомендуемая` / `обязательная` в шапке) не используется: он
|
||
неизбежно врёт, потому что один файл смешивает жёсткие требования с
|
||
советами. В шапке остаются только `topic`, `prefix` и `extends`.
|
||
|
||
## Словарь другого языка
|
||
|
||
Словарь набора — три перечня, и требования к ним одни и те же.
|
||
|
||
**Шкала обязательности.** Для английского готовый словарь даёт BCP 14; для
|
||
любого другого языка слова берут из перевода стандарта, если он есть, или
|
||
переводят сами. Шкала и семантика ступеней при этом не меняются — меняется
|
||
только запись.
|
||
|
||
| Ступень | Русский | Английский (BCP 14) |
|
||
|---|---|---|
|
||
| требование | ДОЛЖЕН | MUST |
|
||
| запрет | НЕ ДОЛЖЕН | MUST NOT |
|
||
| рекомендация | СЛЕДУЕТ | SHOULD |
|
||
| рекомендация против | НЕ СЛЕДУЕТ | SHOULD NOT |
|
||
| разрешение | ДОПУСКАЕТСЯ | MAY |
|
||
|
||
**Метки.** Обязательности не задают, а размечают: ПОЧЕМУ — обоснование,
|
||
ПРИМЕРЫ — иллюстрации к норме, МЕХАНИЗИРОВАНО — запись о проверке в копии,
|
||
СНЯТО — заглушку на месте убранного правила. Стандартом не даются ни в одном
|
||
языке: в BCP 14 таких понятий нет, слова подбираются под язык так же, как
|
||
остальные.
|
||
|
||
| Метка | Русский | Английский |
|
||
|---|---|---|
|
||
| обоснование | ПОЧЕМУ | WHY |
|
||
| иллюстрации | ПРИМЕРЫ | EXAMPLES |
|
||
| способ проверки | МЕХАНИЗИРОВАНО | MECHANIZED |
|
||
| снятое правило | СНЯТО | RETIRED |
|
||
|
||
**Служебные слова сценарного блока** — `КОГДА`, `ТОГДА`, `И`, `ИЛИ`; таблица
|
||
и объяснение в разделе «Таблицы решений».
|
||
|
||
Что требуется от любого словаря:
|
||
|
||
- **одна форма на ступень и на метку.** Синонимы отклонены не из аскетизма:
|
||
проверка «модальное слово вне правила» перечисляет формы, и синонимический
|
||
ряд превращает перечисление в разбор.
|
||
- **слово заглавными не встречается в обычной прозе этого языка.** Иначе
|
||
правило «нормативно только заглавное» перестаёт спасать: проверка ловит
|
||
оформление, а не модальность.
|
||
- **модальные слова и метки перечислены в строке о версии языка.** Читателю
|
||
копии они известны из самого файла, без обращения к этому документу, —
|
||
иначе конвенция в чужом репозитории теряет ключ к собственному тексту.
|
||
Служебные слова сценария в строку не входят: структура блока читается из
|
||
самого блока, и в файле без стыков правил их нет вовсе.
|
||
- **словарь один на канон.** Два словаря параллельно дают две формы записи
|
||
одного требования и удваивают каждую проверку; выбор языка — свойство
|
||
набора, а не отдельного файла.
|
||
|
||
## Ссылка на язык из конвенции
|
||
|
||
Каждая конвенция называет язык одной строкой во вводной прозе:
|
||
|
||
> Ключевые слова ДОЛЖЕН, НЕ ДОЛЖЕН, СЛЕДУЕТ, НЕ СЛЕДУЕТ, ДОПУСКАЕТСЯ и метки
|
||
> ПОЧЕМУ, ПРИМЕРЫ, МЕХАНИЗИРОВАНО и СНЯТО толкуются как описано в языке
|
||
> конвенций версии 1 — тогда и только тогда, когда написаны заглавными.
|
||
|
||
Слова в строке — из словаря того языка, на котором написан набор. Для
|
||
англоязычного набора та же строка выглядит так:
|
||
|
||
> The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and the marks WHY,
|
||
> EXAMPLES, MECHANIZED and RETIRED are to be interpreted as described in the
|
||
> conventions language, version 1, and only when written in capitals.
|
||
|
||
Форма скопирована у BCP 14, где та же задача решается тем же способом:
|
||
спецификация не прикладывает к себе словарь и не указывает путь к нему, а
|
||
называет документ и версию. Пути в этой строке нет намеренно — конвенция
|
||
уезжает в чужой репозиторий, где путей канона не существует, а норму
|
||
исполнить всё равно можно: строка сама перечисляет ключевые слова набора и
|
||
сама несёт правило заглавных.
|
||
|
||
## Обязательность и способ проверки — разные атрибуты
|
||
|
||
Механизация не входит в шкалу модальности: она говорит не о том, насколько
|
||
правило обязательно, а о том, чем эта обязательность обеспечена. В 29148 это
|
||
два разных атрибута требования, и здесь тоже два.
|
||
|
||
**Проверяющий по умолчанию — читатель правила**, человек или агент. Канон
|
||
пишется прежде всего под агента: он читает конвенцию и по ней смотрит код,
|
||
то есть проверка есть у каждого правила с первого дня, и её инструмент —
|
||
формулировка нормы. Поэтому вторым условием ДОЛЖЕН стоит воспроизводимость
|
||
вердикта (META-6), а не наличие скрипта: ступень говорит о важности нормы и о
|
||
том, сколько внимания она получает при проверке, а не о состоянии
|
||
инструментов. Линтер сильнее чтения — он не забывает, ничего не стоит на
|
||
каждом прогоне и краснеет до ревью, — поэтому механизация желательна везде,
|
||
где проверка пишется (META-27), и остаётся концом пути «находка → конвенция →
|
||
проверка». Но обязательным условием высшей ступени она не является: иначе весь
|
||
канон стоял бы в СЛЕДУЕТ до появления скриптов, которых пока нет ни одного.
|
||
|
||
**Механизация нормы не заменяет и не сокращает.** Норма остаётся в правиле
|
||
навсегда — как и обоснование (META-8, META-10), — сколько бы проверок её ни
|
||
подпирало. Причин три:
|
||
|
||
- **линтер сообщает, что нарушено, но не сообщает, что требуется.** Без нормы
|
||
правило нечем исполнить и не с чем сверить вердикт проверки, а проверяющий
|
||
по умолчанию читает именно норму;
|
||
- **подписчики появляются позже.** Репозиторий, подключившийся через год,
|
||
получил бы правило без нормы и без линтера — ни текста, ни проверки;
|
||
- **«механизировано у всех» набору не проверить:** списка подписчиков у него
|
||
нет по построению.
|
||
|
||
**Отметка — свойство репозитория, а не набора.** Механизирована норма или нет,
|
||
зависит от того, чей это репозиторий, поэтому в тексте конвенции отметки нет:
|
||
её место — запись о механизации в локальной части копии, со ссылкой на
|
||
идентификатор правила (META-7).
|
||
|
||
```markdown
|
||
<!-- conv:local -->
|
||
|
||
XMIG-2, XMIG-4 — МЕХАНИЗИРОВАНО: `internal/archrules`, в новых миграциях.
|
||
```
|
||
|
||
Так у правила остаются оба атрибута сразу: обязательность — в норме, которая
|
||
приезжает из набора и одинакова у всех, способ проверки — в записи, которая
|
||
принадлежит репозиторию и у каждого своя.
|
||
|
||
## Таблицы решений
|
||
|
||
Часть правил **классифицирует ситуации**: какой уровень лога, какая
|
||
категория директории, что делать с невалидным вводом в зависимости от его
|
||
источника. Каноническая форма для них — таблица «ситуация → вердикт», строки
|
||
которой нумеруются как подпункты правила (`XLOG-8.1`).
|
||
|
||
Таблица плотнее прозы и не даёт пропустить ветку: пустая клетка видна, а
|
||
неупомянутый случай в абзаце — нет. От такой таблицы требуются два свойства —
|
||
первое названо в DMN, второе мы добавили сами («Где источник усилен»):
|
||
|
||
- **Политика совпадения.** По умолчанию строки взаимоисключающи: любой
|
||
ситуации соответствует ровно одна. Если это не так, таблица объявляет
|
||
порядок строкой над собой — «применяется первое совпадение». Молчание об
|
||
этом означает, что при двух подходящих строках читатель выбирает сам, и
|
||
два автора выберут по-разному.
|
||
- **Полнота.** Перечислены все случаи, попадающие в область действия. Если
|
||
возможен случай вне перечисленных, он назван отдельной строкой, а не
|
||
оставлен на догадку.
|
||
|
||
Сценарный блок остаётся точечным инструментом — для **стыка правил**, когда
|
||
два правила вместе дают неочевидный результат:
|
||
|
||
```
|
||
КОГДА зависимость недоступна И ретраи вызова исчерпаны
|
||
ТОГДА внешний вызов даёт запись ERROR,
|
||
И тик фонового цикла, упавший по той же причине, — запись WARN
|
||
```
|
||
|
||
Такой блок ставится после обоих правил и ссылается на их идентификаторы.
|
||
Если стыков нет — сценариев в файле нет.
|
||
|
||
Форма «условие → следствие» здесь взята намеренно, хотя как **общая** форма
|
||
записи она отклонена: на стыке правил субъект действительно система, и
|
||
результат разворачивается во времени — то самое, для чего эта форма и
|
||
придумана. Служебные слова блока перечислены ниже и подчиняются тем же
|
||
требованиям, что модальные: одна форма на роль, заглавными, набор один на
|
||
канон.
|
||
|
||
| Роль | Русский | Английский |
|
||
|---|---|---|
|
||
| условие | КОГДА | WHEN |
|
||
| следствие | ТОГДА | THEN |
|
||
| соединение | И | AND |
|
||
| выбор | ИЛИ | OR |
|
||
|
||
Модальными словами они не являются: обязательности не задают, только
|
||
структуру. Поэтому в строку о версии языка они не попадают — там
|
||
перечисляется то, чему нужно определение, а логическая связка читается сама,
|
||
— и под проверку «модальные слова вне правил» не подпадают.
|
||
|
||
## Идентификаторы
|
||
|
||
Идентификаторов в языке два: **правило** адресуется префиксом с номером,
|
||
**конвенция целиком** — именем темы. Ссылаться путём к файлу нельзя ни на то,
|
||
ни на другое.
|
||
|
||
**Правило.**
|
||
|
||
- Формат — `<ПРЕФИКС>-<номер>`: `XKEY-5`, `XLOG-27`. Префикс принадлежит
|
||
файлу, нумерация внутри файла сквозная и начинается с единицы.
|
||
- Строка таблицы, если на неё нужно ссылаться отдельно, — `XKEY-5.1`,
|
||
`XKEY-5.2`.
|
||
- **Идентификатор глобален.** Префикс уникален по всему канону, поэтому
|
||
путь файла в ссылке не нужен: `XKEY-5` адресует правило одинаково изнутри
|
||
файла, из соседней конвенции и из чужого репозитория. В собранной копии
|
||
слои разных осей лежат в одном документе, так что ссылка на базовый слой
|
||
из языкового вообще никуда не ведёт — правило рядом.
|
||
- **Идентификаторы стабильны и не переиспользуются.** Занять номер снятого
|
||
правила новым нельзя — иначе ссылка из чужого репозитория начнёт указывать
|
||
на другое утверждение. То же относится к префиксам: выбывшие хранит манифест
|
||
набора.
|
||
- **Снятое правило остаётся заглушкой.** Заголовок и номер сохраняются, норму
|
||
с обоснованием заменяет блок СНЯТО с датой и причиной. Поэтому нумерация в
|
||
файле сплошная, а любая ссылка разрешается — либо в правило, либо в
|
||
объяснение, почему его сняли (META-31, META-32). Отдельного реестра снятых
|
||
номеров нет: он был бы вторым источником правды рядом с файлом.
|
||
- Префикс **выбирается под файл, а не выводится по формуле**: он нужен,
|
||
чтобы по нему искать, а не чтобы его разбирать. Выводимый префикс вдобавок
|
||
привязал бы идентификатор к таксономии, которую канон перестраивает, и
|
||
упёрся бы в потолок из числа букв алфавита.
|
||
- Префиксы на букву `X` каноном не занимаются: они принадлежат локальным
|
||
правилам репозиториев-потребителей.
|
||
- Перенос правила в другой файл — смысловое изменение, а не переименование:
|
||
новый файл означает новый префикс и новую нумерацию. Переезд самого файла
|
||
между осями идентификаторы не трогает.
|
||
|
||
Порядок правил в файле выбирается по читаемости, не по номерам: номер — это
|
||
идентификатор, а не позиция.
|
||
|
||
**Тема.**
|
||
|
||
- **Тема — набор правил об одном фокусе разработки:** время, конфигурация,
|
||
схема БД. Она же — единица подписки: потребитель берёт тему целиком, а не
|
||
отдельные правила.
|
||
- Имя темы записывается латиницей. Рекомендуется нижний kebab-case
|
||
(`db-identifiers`), но годится любой идентификатор, пригодный для имени
|
||
файла: имя попадает и в файловую систему потребителя, и в его манифест.
|
||
- **Тему объявляет файл, а не файловая система.** Имя стоит в шапке
|
||
(`topic:`) и зарегистрировано в манифесте набора. Слои одной темы несут
|
||
одно и то же имя — по нему они и собираются в один документ, как бы ни
|
||
назывались их файлы.
|
||
- **Имя темы не переиспользуется** — как и префикс: оно живёт в шапке
|
||
`origin:` каждой копии, в подписке манифеста и в тексте ссылок, поэтому
|
||
снятое имя уходит в раздел выбывших манифеста, а не достаётся другой теме.
|
||
- Ссылка на конвенцию — имя темы (конвенция `logging`), ссылка на правило —
|
||
идентификатор (`XLOG-27`). Путь файла не употребляется ни там, ни там: в
|
||
собранной копии путей канона не существует.
|
||
|
||
## Что правилом не является
|
||
|
||
Заглавные модальные слова в этих частях **не употребляются** — иначе
|
||
перестанет быть понятно, что адресуемо, а что нет:
|
||
|
||
- **Область действия** — на что конвенция распространяется во времени
|
||
(«новые таблицы; существующие не переписываются»). Это рамка для всех
|
||
правил файла, а не правило.
|
||
- **Связано** — ссылки на смежные конвенции, ADR, код.
|
||
- **Локальная часть копии** — содержимое принадлежит репозиторию.
|
||
- Вводная проза, объясняющая предмет конвенции.
|
||
|
||
Все четыре части лежат вне областей правил: до первого заголовка правила или
|
||
после заголовка, которым область закрылась. Хвост обоснования сюда не
|
||
относится — он внутри правила, и модальные слова в нём законны как упоминания.
|
||
|
||
## Как на правила ссылаются копии
|
||
|
||
Ниже маркера локальной части, в репозитории:
|
||
|
||
```markdown
|
||
<!-- conv:local -->
|
||
|
||
XMIG-2, XMIG-4 — МЕХАНИЗИРОВАНО: `internal/archrules`, в новых миграциях.
|
||
|
||
XMIG-6 не соблюдается в легаси-таблицах `show_history`, `queue`: составные
|
||
ключи там появились до конвенции, переписывание требует миграции данных.
|
||
```
|
||
|
||
Отсюда видно и то, чего раньше не было видно: конвенция из восьми правил,
|
||
из которых два механизированы и одно не соблюдается.
|
||
|
||
## Что стоит проверять машиной
|
||
|
||
Проверке подлежит всё, что язык **употребляет**: файлы конвенций и документ,
|
||
которым канон сам себя ведёт (в этом каноне — `GUIDE.md`, префикс META). Файл,
|
||
который язык **цитирует**, — это описание вроде текущего: ключевые слова стоят
|
||
в нём как предмет разговора, а не как норма, и проверки к нему не применяются.
|
||
Различает не расположение файла, а роль слова в нём.
|
||
|
||
Ни одна проверка пока не реализована, поэтому при ревью их выполняют чтением.
|
||
|
||
**Форма правила** — разбором текста, в любом файле, который язык употребляет:
|
||
|
||
- модальные и служебные слова принадлежат объявленному словарю канона, а не
|
||
смеси словарей;
|
||
- префикс в шапке файла совпадает с манифестом набора, состоит из четырёх
|
||
заглавных латинских букв, не начинается на `X` и не значится в списке
|
||
выбывших;
|
||
- заголовки правил файла используют только его собственный префикс;
|
||
- нумерация внутри файла сплошная: от единицы до наибольшего номера без
|
||
пропусков, номера не повторяются, новое правило берёт следующий за
|
||
наибольшим (META-31);
|
||
- у каждого `### <ПРЕФИКС>-<n>` есть либо модальное слово с нормой и блок
|
||
ПОЧЕМУ — ни норма, ни обоснование не удаляются никогда (META-8, META-10), —
|
||
либо блок СНЯТО с датой и причиной;
|
||
- блок ПРИМЕРЫ, если он есть, стоит после обоснования и не открывает правило:
|
||
порядок блоков — норма, ПОЧЕМУ, ПРИМЕРЫ;
|
||
- вводная проза содержит строку о версии языка;
|
||
- ссылки вида `<ПРЕФИКС>-<n>` — хоть в тексте набора, хоть в локальной части
|
||
копии — разрешаются: неразрешённый идентификатор всегда ошибка (META-32);
|
||
- заглавные модальные слова не встречаются вне областей правил (область —
|
||
от заголовка правила до следующего заголовка) — кроме строки о версии
|
||
языка, которая их перечисляет по назначению;
|
||
- модальная метка стоит первой в своём абзаце: заглавное слово в середине
|
||
фразы — упоминание ступени, а не вторая норма правила.
|
||
|
||
**Распространение** — разбором текста, только в файлах конвенций: эти проверки
|
||
о том, что документ уезжает к потребителю, а документ, которым канон ведёт
|
||
себя, не уезжает никуда.
|
||
|
||
- шапка файла несёт имя темы, и это имя стоит в манифесте набора — среди
|
||
живых, а не среди выбывших;
|
||
- ось слоя объявлена в шапке, а не выведена из пути; у одной темы не больше
|
||
одного слоя без ключей оси — базовый слой единственный;
|
||
- если директории осей используются, объявленное в шапке совпадает с путём:
|
||
расхождение означает переезд файла без правки шапки;
|
||
- имя темы, названное в ссылке или в подписке потребителя, тоже разрешается по
|
||
манифесту: ссылка на снятую тему не проходит молча;
|
||
- префиксы локальных правил копии начинаются на `X`;
|
||
- отметки МЕХАНИЗИРОВАНО в тексте конвенции нет: её место — запись о
|
||
механизации в локальной части копии (META-7);
|
||
- словарь в коротком описании языка совпадает с этим: те же ступени, те же
|
||
метки, те же значения (META-30);
|
||
- префикс **чужой темы** не встречается в абзаце с модальностью (META-20);
|
||
префикс арх-слоя своей темы там допустим (META-24), префикс другого языка
|
||
или стека — нет;
|
||
- путь файла канона не встречается в тексте конвенции (META-21).
|
||
|
||
**Чтением**, потому что машине не даётся:
|
||
|
||
- строки таблицы взаимоисключающи либо политика совпадения объявлена;
|
||
- перечисленные в таблице случаи покрывают область действия;
|
||
- норма исполнима без обращения к другим файлам (в файлах конвенций: они
|
||
уезжают по одной, а обвязка ссылается на соседей свободно);
|
||
- обоснование отвечает на «что сломается», а не пересказывает норму;
|
||
- хвост обоснования не вводит требований, которых нет в блоке нормы;
|
||
- примеры иллюстрируют норму и не расширяют её: деталь примера не читается
|
||
как требование.
|
||
|
||
Проверки выше — про запись правила. Граница самой темы (не собрала ли она
|
||
два решения сразу, нужна ли потребителю целиком) языку не принадлежит: её
|
||
вопросы стоят в документе, которым набор ведёт себя.
|
||
|
||
## Версия языка
|
||
|
||
Номер версии называется в каждой конвенции, поэтому он двигается, когда
|
||
изменение формы способно изменить чтение **уже разданной** копии: копия
|
||
ссылается на номер, а не на текст, и обязана читаться по той версии, по
|
||
которой написана. Правки формы до того, как копии разошлись, номер не двигают
|
||
— читать по ним пока нечего.
|
||
|
||
Смена словаря под другой естественный язык версию не двигает никогда: версия
|
||
принадлежит шкале, меткам и правилам формы, а не буквам.
|