diff --git a/conventions/arch/config.md b/conventions/arch/config.md index ff72197..0c80d57 100644 --- a/conventions/arch/config.md +++ b/conventions/arch/config.md @@ -293,9 +293,9 @@ prefix: CONF ## Связано -- `arch/time.md` — формат времени; зона отображения — единственный +- конвенция `time` — формат времени; зона отображения — единственный конфигурируемый параметр времени, семантика описана там. -- `arch/app-directories.md` — конфиг лежит в категории «конфигурация» и +- конвенция `app-directories` — конфиг лежит в категории «конфигурация» и доступен приложению только на чтение. diff --git a/conventions/arch/db-identifiers.md b/conventions/arch/db-identifiers.md index a008cd2..559298e 100644 --- a/conventions/arch/db-identifiers.md +++ b/conventions/arch/db-identifiers.md @@ -136,7 +136,7 @@ KEYS-1 требует **сортируемый** строковый иденти ## Связано -- `arch/time.md` — метки времени тоже генерирует приложение, а не схема. +- конвенция `time` — метки времени тоже генерирует приложение, а не схема. diff --git a/conventions/arch/time.md b/conventions/arch/time.md index 7bee098..e4feb02 100644 --- a/conventions/arch/time.md +++ b/conventions/arch/time.md @@ -154,8 +154,8 @@ prefix: TIME ### TIME-11. Зона отображения берётся из конфигурации, по умолчанию `UTC` -**ДОЛЖЕН.** Значение приходит из конфигурации (`arch/config.md`), значение -по умолчанию — `UTC`. +**ДОЛЖЕН.** Значение приходит из конфигурации, значение по умолчанию — +`UTC`. **Почему.** Зашитая в код зона превращает переезд или второго пользователя в другом поясе в правку кода и релиз. Значение по умолчанию `UTC` выбрано @@ -184,8 +184,9 @@ prefix: TIME ## Связано -- `arch/config.md` — где задаётся зона отображения. -- `arch/db-identifiers.md` — то же правило «генерирует приложение» для id. +- конвенция `config` — где задаётся зона отображения. +- конвенция `db-identifiers` — то же правило «генерирует приложение» для + идентификаторов. diff --git a/conventions/lang/go/config.md b/conventions/lang/go/config.md index aa73d76..d6a812d 100644 --- a/conventions/lang/go/config.md +++ b/conventions/lang/go/config.md @@ -5,7 +5,7 @@ extends: arch/config.md # Конфигурация: реализация на Go -Как `arch/config.md` выглядит в Go-приложении: формат, загрузчик, границы +Как базовый слой выглядит в Go-приложении: формат, загрузчик, границы запрета на окружение. Форма записи — `LANGUAGE.md`. Секретов Go-специфика не касается: они приходят из деплоя уже в файле, а @@ -224,8 +224,8 @@ zoneinfo, а сообщение указывает не на ту причину ## Связано -- `lang/go/time.md` — зона отображения и формат времени. -- `lang/go/logging.md` — `slog`, которым падает невалидный конфиг. +- конвенция `time` — зона отображения и формат времени. +- конвенция `logging` — `slog`, которым падает невалидный конфиг. diff --git a/conventions/lang/go/db-identifiers.md b/conventions/lang/go/db-identifiers.md index 6568aef..956eed5 100644 --- a/conventions/lang/go/db-identifiers.md +++ b/conventions/lang/go/db-identifiers.md @@ -5,7 +5,7 @@ extends: arch/db-identifiers.md # Идентификаторы: реализация на Go -Как `arch/db-identifiers.md` выглядит в Go-приложении. Форма записи — +Как базовый слой выглядит в Go-приложении. Форма записи — `LANGUAGE.md`. Единая точка из `KEYS-3` — пакет `internal/ident`: он @@ -118,8 +118,8 @@ HTTP-кодов. В случае GKEY-8.1 снаружи это неотличи **НЕ ДОЛЖЕН.** Обработчик не конструирует доменный sentinel (например `ErrNotFound`), чтобы тут же сопоставить его со своим ответом. -**Почему.** Инверсия правила «трансляция у источника» из -`lang/go/errors.md`. Sentinel — сообщение от слоя, который знает факт: +**Почему.** Инверсия правила «трансляция у источника» из конвенции +`errors`. Sentinel — сообщение от слоя, который знает факт: строка не найдена, потому что store её искал. Сфабрикованный транспортом, он утверждает непроверенное, и по типу ошибки перестаёт быть видно, был ли вообще поход в хранилище — а на этом держится вся диагностика по ошибкам. diff --git a/conventions/lang/go/db-schema.md b/conventions/lang/go/db-schema.md index 4683c97..d525242 100644 --- a/conventions/lang/go/db-schema.md +++ b/conventions/lang/go/db-schema.md @@ -119,14 +119,15 @@ down останавливает сразу и заставляет пересо таблицы соответствия, которую пришлось бы держать в голове для числового кода. -### MIGR-8. Метки времени — `TEXT` в формате из `arch/time.md` +### MIGR-8. Метки времени — `TEXT` в каноническом формате **ДОЛЖЕН.** Колонка с меткой времени объявляется как `TEXT`, значения -пишутся в формате из `arch/time.md`. +пишутся как RFC 3339 в UTC с суффиксом `Z` и фиксированной шириной. **Почему.** Типа даты в SQLite нет, поэтому единственное, что делает -значения сравнимыми, — договорённость о формате. Текст в формате из -`arch/time.md` сортируется лексикографически в том же порядке, что и +значения сравнимыми, — договорённость о формате; сам формат выбран не +здесь, а конвенцией `time` (`TIME-1`, `TIME-2`). Текст в нём сортируется +лексикографически в том же порядке, что и хронологически: `ORDER BY` и диапазонные условия работают без функций преобразования, а значит и без потери индекса. Соседство двух форматов в одной колонке ломает и сравнение, и разбор на стороне Go. @@ -159,12 +160,12 @@ down останавливает сразу и заставляет пересо ### MIGR-11. Первичный ключ новой таблицы — TEXT ULID **ДОЛЖЕН.** Колонка ключа объявляется как `TEXT`, значение приходит из -приложения (`KEYS-1`, `KEYS-2`). +приложения. **Почему.** Здесь конвенция схемы ничего не решает — она реализует решение, -принятое в `arch/db-identifiers.md`. Повторить там ветвление или условие -значило бы завести второй источник правды, и соседние таблицы разъехались бы -по разным ответам на один вопрос. +принятое конвенцией `db-identifiers` (`KEYS-1`, `KEYS-2`). Повторить там +ветвление или условие значило бы завести второй источник правды, и соседние +таблицы разъехались бы по разным ответам на один вопрос. `AUTOINCREMENT` в такой таблице невозможен: SQLite разрешает его только на `INTEGER PRIMARY KEY`. То есть для новых таблиц запрещать нечего. @@ -192,9 +193,9 @@ down останавливает сразу и заставляет пересо ## Связано -- `arch/time.md` — формат меток времени. -- `arch/db-identifiers.md` — выбор первичных ключей. -- `lang/go/errors.md` — граничные ошибки `database/sql` транслируются в +- конвенция `time` — формат меток времени. +- конвенция `db-identifiers` — выбор первичных ключей. +- конвенция `errors` — граничные ошибки `database/sql` транслируются в доменные у источника, в слое store. diff --git a/conventions/lang/go/errors.md b/conventions/lang/go/errors.md index 3847b4c..0708352 100644 --- a/conventions/lang/go/errors.md +++ b/conventions/lang/go/errors.md @@ -5,8 +5,8 @@ prefix: GERR # Ошибки Как ошибки строятся, оборачиваются и проверяются. Форма записи — -`LANGUAGE.md`. Где и когда ошибку **логировать** — в -`lang/go/logging.md` (коротко: лог один раз на доменной границе). +`LANGUAGE.md`. Где и когда ошибку **логировать** — в конвенции `logging` +(коротко: лог один раз на доменной границе). Две границы, о которых говорят правила ниже: @@ -169,7 +169,7 @@ HTTP-клиентов, файловой системы, внешних SDK. ### GERR-12. Полная ошибка идёт в приватный канал **ДОЛЖЕН.** В лог уходит вся цепочка `%w` с контекстом; где и когда именно -— `lang/go/logging.md`. +— конвенция `logging`. **Почему.** Цепочка — единственный носитель диагностики (GERR-1), и единственный канал, где её можно показать целиком, — тот, который видит @@ -231,7 +231,7 @@ HTTP-клиентов, файловой системы, внешних SDK. **Почему.** Непокрытая ошибка — не класс отказа, а дефект: ветвь забыли завести вопреки GERR-16. Адресат у неё владелец в смысле «надо чинить», отсюда -`ERROR` — уровень выбирается по адресату (`lang/go/logging.md`). Статус +`ERROR` — уровень выбирается по адресату (конвенция `logging`). Статус тоже не выбирается: известное пользовательское состояние лежало бы в маппинге, а про неизвестное сказать пользователю нечего, поэтому 4xx отпадает. @@ -385,9 +385,8 @@ HTTP-клиентов, файловой системы, внешних SDK. ## Связано -- `lang/go/logging.md` — где и когда ошибка попадает в лог. -- `KEYS-7` (`arch/db-identifiers.md`) — формат корреляционного ключа - из `GERR-14`. +- конвенция `logging` — где и когда ошибка попадает в лог. +- `KEYS-7` — формат корреляционного ключа из `GERR-14`. diff --git a/conventions/lang/go/logging.md b/conventions/lang/go/logging.md index 420c891..9075c06 100644 --- a/conventions/lang/go/logging.md +++ b/conventions/lang/go/logging.md @@ -44,7 +44,7 @@ dev-выводом перестаёшь ежедневно гонять собс ### SLOG-3. Время записи — UTC **ДОЛЖЕН.** `time` приводится к UTC через `ReplaceAttr` по `slog.TimeKey` -(см. `lang/go/time.md`). +(см. конвенцию `time`). **Почему.** По умолчанию UTC не получится: встроенные хендлеры пишут время в зоне самого `time.Time`, то есть в локальной зоне процесса. Записи одного @@ -54,8 +54,8 @@ dev-выводом перестаёшь ежедневно гонять собс событий. Точность `JSONHandler` — миллисекунды фиксированной ширины; это другая -точность, чем в БД, и по `arch/time.md` так и должно быть: ширина -фиксируется на носитель. +точность, чем в БД, и по `TIME-2` так и должно быть: ширина фиксируется +на носитель. ## Сообщение @@ -243,7 +243,7 @@ dev-выводом перестаёшь ежедневно гонять собс **НЕ СЛЕДУЕТ.** Отдельный случайный `trace_id` не заводится, если у сущностей есть стабильные уникальные идентификаторы. (Как их выбирают — -`arch/db-identifiers.md`, если конвенция взята.) +конвенция `db-identifiers`, если взята.) **Почему.** Идентификатор сущности уже существует, стабилен между процессами и во времени — по нему собираются записи не одного прохода, а @@ -347,7 +347,7 @@ ctx = logctx.With(ctx, log) // достаём логгер из ctx в кажд Нарушение инварианта в собственном коде — паника, недостижимая ветка — в таблицу не входит: это не доменный отказ, и логирует его recover-граница -вместе со стеком (`lang/go/errors.md`). Искать его класс здесь не нужно. +вместе со стеком (конвенция `errors`). Искать его класс здесь не нужно. Мимо таблицы идёт и доменная ошибка, которой нет в маппинге: класса у неё нет, потому что её просто забыли завести. Она логируется `ERROR` с @@ -505,7 +505,7 @@ API-ключи и токены, `Authorization`-заголовки, аутент ### SLOG-37. `*url.Error` санитизируется на границе клиента **ДОЛЖЕН.** Ошибка разворачивается в первопричину **до** лога и до -обёртки — раньше трансляции в доменную (`lang/go/errors.md`). +обёртки — раньше трансляции в доменную (конвенция `errors`). **Почему.** `*url.Error` встраивает полный URL запроса, а секрет живёт прямо в нём: токен в пути, `api_key` в query. Go редактирует только пароль @@ -553,11 +553,11 @@ API-ключи и токены, `Authorization`-заголовки, аутент ## Связано -- `arch/time.md` — точность и зона меток времени фиксируются на носитель. -- `lang/go/time.md` — как ставится UTC в `ReplaceAttr` (SLOG-3). -- `lang/go/errors.md` — трансляция ошибки в доменную, порядок относительно +- конвенция `time` — точность и зона меток времени фиксируются на носитель; + как ставится UTC в `ReplaceAttr` (SLOG-3). +- конвенция `errors` — трансляция ошибки в доменную, порядок относительно санитизации (SLOG-37). -- `arch/db-identifiers.md` — откуда берутся стабильные идентификаторы, +- конвенция `db-identifiers` — откуда берутся стабильные идентификаторы, на которых держится корреляция (SLOG-18). diff --git a/conventions/lang/go/time.md b/conventions/lang/go/time.md index bdb9bb7..c1734ea 100644 --- a/conventions/lang/go/time.md +++ b/conventions/lang/go/time.md @@ -5,7 +5,7 @@ extends: arch/time.md # Время: реализация на Go -Как требования `arch/time.md` выполняются в Go-коде: откуда берётся +Как требования базового слоя выполняются в Go-коде: откуда берётся «сейчас», в каком виде время попадает в базу и в логи, что делать с зонами. Форма записи — `LANGUAGE.md`. @@ -178,13 +178,13 @@ func utcTime(_ []string, a slog.Attr) slog.Attr { суток у того, что давно посчитано и сохранено. Календарные вычисления бизнес-логики берут зону явно — как описано в -`arch/time.md`. +базовом слое. ## Связано -- `arch/time.md` — базовая конвенция: UTC как формат хранения, нормализация - чужого входа, явная зона в календарных вычислениях. -- `lang/go/config.md` — валидация зоны отображения загрузчиком конфига. +- базовый слой — UTC как формат хранения, нормализация чужого входа, явная + зона в календарных вычислениях. +- конвенция `config` — валидация зоны отображения загрузчиком конфига. diff --git a/conventions/stack/ansible/app-directories.md b/conventions/stack/ansible/app-directories.md index 03cc9be..b6cbc02 100644 --- a/conventions/stack/ansible/app-directories.md +++ b/conventions/stack/ansible/app-directories.md @@ -5,7 +5,7 @@ extends: arch/app-directories.md # Категории директорий: реализация в Ansible -Как категории из `arch/app-directories.md` раскладываются на сервере +Как категории из базового слоя раскладываются на сервере плейбуком. Форма записи — `LANGUAGE.md`. ## Область действия diff --git a/conventions/stack/htmx/web-ui.md b/conventions/stack/htmx/web-ui.md index 90cffb0..bfe36f9 100644 --- a/conventions/stack/htmx/web-ui.md +++ b/conventions/stack/htmx/web-ui.md @@ -9,8 +9,8 @@ prefix: HTMX показывает и какие действия поддерживает — в спеках, не здесь. Форма записи — `LANGUAGE.md`. -Логирование запросов — `lang/go/logging.md` (HTTP-поля, рутинно-частое на -`DEBUG`). Трансляция доменных ошибок наружу — `lang/go/errors.md` +Логирование запросов — конвенция `logging` (HTTP-поля, рутинно-частое на +`DEBUG`). Трансляция доменных ошибок наружу — конвенция `errors` (приватный канал = логи, публичный = сообщение плюс корреляционный ключ). Здесь — только специфика htmx-транспорта, без дублирования. @@ -215,8 +215,8 @@ s.render(w, "source_block", view) // фрагмент = тот же шаб `?err=` статус по-прежнему используется: там его кто-то читает. Цена решения: в логе доступа провалившееся действие выглядит как `200`. -Искать его надо по доменной записи об исходе операции (`lang/go/logging.md`), -а не по коду ответа. +Искать его надо по доменной записи об исходе операции (конвенция +`logging`), а не по коду ответа. ### HTMX-34. Сбой без ответа-фрагмента показывается глобальным слушателем @@ -238,8 +238,8 @@ s.render(w, "source_block", view) // фрагмент = тот же шаб ### HTMX-15. Наружу идёт сообщение публичного канала -**ДОЛЖЕН.** Во фрагмент попадает нейтральный текст по правилам -`lang/go/errors.md`; `err.Error()` в разметку не рендерится. +**ДОЛЖЕН.** Во фрагмент попадает нейтральный текст публичного канала; +`err.Error()` в разметку не рендерится. **Почему.** htmx-фрагмент выглядит внутренней деталью приложения, и на нём легче всего забыть, что это тот же публичный канал, что и страница: