common: добавлен язык записи конвенций
- правило = номер + модальность (ДОЛЖЕН / СЛЕДУЕТ / ДОПУСКАЕТСЯ) плюс обязательный блок «Почему»; из OpenSpec взяты идентификаторы правил, но не SHALL и не GIVEN/WHEN/THEN - файловый статус отменён: обязательность живёт на правиле, а в шапке ещё не переведённых конвенций ключ остаётся меткой «это проза»
This commit is contained in:
+25
-24
@@ -1,16 +1,12 @@
|
||||
---
|
||||
status: обязательная
|
||||
---
|
||||
|
||||
# Как мы ведём конвенции
|
||||
|
||||
Конвенция описывает повторяющийся выбор: как называть директории, как
|
||||
раскладывать данные, как оформлять ошибки. Она отвечает на вопрос «как
|
||||
принято», а не «что здесь происходит». Одна конвенция — один файл.
|
||||
|
||||
Механической проверки у самой этой конвенции нет — осознанное исключение:
|
||||
проверять «правильно ли написана конвенция» нечем, а обязательный статус
|
||||
нужен, чтобы правила ниже не обсуждались заново в каждом репозитории.
|
||||
Как записывается сама конвенция — правила, модальность, обоснования — в
|
||||
[language.md](language.md). Здесь — про то, зачем они заводятся, где живут
|
||||
и как соотносятся с соседними видами документов.
|
||||
|
||||
## Канон и копии
|
||||
|
||||
@@ -54,17 +50,16 @@ status: обязательная
|
||||
текущем состоянии репозитория. «Так сделано у нас» — это регион
|
||||
отступлений; норма пишется в настоящем предписывающем времени.
|
||||
|
||||
## Статус
|
||||
## Насколько правило обязательно
|
||||
|
||||
Каждая конвенция объявляет статус в шапке:
|
||||
Обязательность живёт **на правиле**, а не на файле: один документ почти
|
||||
всегда смешивает жёсткие требования с советами, и общая пометка на нём
|
||||
неизбежно врёт про часть содержимого. Шкала модальных слов — в
|
||||
[language.md](language.md).
|
||||
|
||||
- **рекомендуемая** — так стоит делать в новом коде; существующий переезжает
|
||||
по мере касания, отдельной кампанией не переписывается;
|
||||
- **обязательная** — нарушение считается ошибкой; по возможности проверяется
|
||||
линтером или хуком, а не вниманием.
|
||||
|
||||
Конвенция без механической проверки держится только на внимании — это
|
||||
нормально для рекомендуемой и плохо для обязательной.
|
||||
Правило без механической проверки держится только на внимании. Для
|
||||
**СЛЕДУЕТ** это нормально, для **ДОЛЖЕН** — плохо: такое правило либо
|
||||
механизируется, либо честно понижается.
|
||||
|
||||
## Когда заводить
|
||||
|
||||
@@ -89,14 +84,19 @@ status: обязательная
|
||||
- **из канона формулировка не удаляется**, пока правило не механизировано
|
||||
у всех потребителей: иначе те, у кого линтера нет, останутся без правила;
|
||||
- **факт механизации** фиксируется в локальном регионе `механизировано` —
|
||||
со ссылкой на конкретное правило;
|
||||
со ссылкой на номер правила и на конкретную проверку;
|
||||
- когда механизация стала общей (правило уехало в общий конфиг линтера или
|
||||
в общую роль), формулировка удаляется из канона одним `push`.
|
||||
|
||||
Обоснование правила («Почему») не удаляется никогда, даже когда сама норма
|
||||
уехала в линтер: линтер сообщает, что нарушено, но не сообщает, зачем
|
||||
правило существует, — а именно это нужно, чтобы понять, когда его пора
|
||||
отменить.
|
||||
|
||||
## Трудноизменяемые слои
|
||||
|
||||
У схемы БД, формата хранения и раскладки директорий шкала
|
||||
«рекомендуемая → переезжает по мере касания» не работает: таблица не
|
||||
У схемы БД, формата хранения и раскладки директорий не работает привычное
|
||||
«новое пишем правильно, старое переезжает по мере касания»: таблица не
|
||||
переезжает от того, что её потрогали. Для таких конвенций:
|
||||
|
||||
- **область действия пишется явно** — «применяется к новым таблицам и
|
||||
@@ -108,10 +108,11 @@ status: обязательная
|
||||
|
||||
## Честный список отступлений
|
||||
|
||||
В локальном регионе перечисляем отступления, которые уже есть в коде, —
|
||||
иначе репозиторий делает вид, что правилу следует. У рекомендуемой
|
||||
конвенции пустой список отступлений почти всегда означает, что их просто не
|
||||
искали.
|
||||
В локальном регионе перечисляем отступления, которые уже есть в коде, — со
|
||||
ссылкой на номера правил. Иначе репозиторий делает вид, что конвенции
|
||||
следует, а проверить это можно только чтением всего кода.
|
||||
|
||||
Пустой список отступлений почти всегда означает, что их не искали.
|
||||
|
||||
Отступление — это «правилу не следуем здесь и вот почему». Если регион
|
||||
разросся до «мы это правило вообще не применяем», значит либо у правила
|
||||
@@ -129,7 +130,7 @@ status: обязательная
|
||||
- **Короткие инварианты дублируются туда, что агент читает безусловно**
|
||||
(`AGENTS.md` / `CLAUDE.md`): сама по себе конвенция агенту не видна, он
|
||||
дойдёт до неё, только если его туда отправили. Детали остаются здесь,
|
||||
в файл-точку-входа едет одна строка на правило.
|
||||
в файл-точку-входа едет одна строка на правило с его номером.
|
||||
|
||||
<!-- local:точки-входа -->
|
||||
<!-- /local -->
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
# Язык конвенций
|
||||
|
||||
Как записываются правила в этом каноне. Документ описывает форму, а не
|
||||
содержание: что такое правило, чем оно отличается от прозы вокруг и как на
|
||||
него сослаться.
|
||||
|
||||
Форма подсмотрена у OpenSpec, но взято оттуда не всё — см. «Чего мы не
|
||||
берём».
|
||||
|
||||
## Зачем формализовать
|
||||
|
||||
Не ради строгости. Три конкретные вещи, которые без адресуемых правил не
|
||||
работают:
|
||||
|
||||
- **Механизация.** Регион `механизировано` должен говорить «правило R4
|
||||
проверяет `archrules`», а не «`AUTOINCREMENT` в новых миграциях —
|
||||
`archrules`»: во втором случае читатель сам догадывается, к какому
|
||||
утверждению это относится, и догадывается по-разному.
|
||||
- **Отступления.** «У нас не так» бесполезно, пока не сказано, что именно
|
||||
«не так». Со ссылкой на правило отступления становятся счётными: видно,
|
||||
сколько правил конвенции репозиторий реально не соблюдает.
|
||||
- **Промоут находки.** Путь «находка → конвенция → правило линтера →
|
||||
удаление прозы» требует ручки, за которую берут конкретное правило.
|
||||
|
||||
Общий знаменатель — **идентификатор**. Модальные слова и таблицы полезны,
|
||||
но вторичны.
|
||||
|
||||
## Единица — правило
|
||||
|
||||
```markdown
|
||||
### 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, код.
|
||||
- **Локальные регионы** — содержимое принадлежит репозиторию.
|
||||
- Вводная проза, объясняющая предмет конвенции.
|
||||
|
||||
## Как на правила ссылаются копии
|
||||
|
||||
В репозитории:
|
||||
|
||||
```markdown
|
||||
<!-- 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`.
|
||||
Reference in New Issue
Block a user