конвенции: перенести механизируемое в golangci-lint и internal/archrules
Правило, которое проверяет машина, не должно оставаться прозой: файл конвенций на сотни строк размазывает внимание по тривиальному — модель добросовестно проверит именование полей лога и не дойдёт до формы решения. Включены sloglint (константный msg, стиль ключ-значение), forbidigo (fmt.Print*, os.Getenv, time.Now мимо store.Now), errorlint (сравнение ошибок), depguard (сторонние пакеты ошибок). internal/archrules — сканеры на то, что линтером не выражается: направление зависимостей ядро↔транспорты, AUTOINCREMENT и серверное время в новых миграциях, матчинг ошибки по тексту. Код приведён к правилам: logging.StartCall как единая точка отсчёта длительности внешних вызовов, store.Now вместо time.Now в httpapi и часах воркера, slog.DiscardHandler в тестах. Перенесённое вычеркнуто из docs/conventions/* и openspec/config.yaml — прозой осталось только то, что правилом не выражается. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,11 +4,13 @@
|
||||
[../specs/database.md](../specs/database.md); обоснование выбора ULID —
|
||||
`openspec/changes/ulid-identity/design.md` (после архивации — в истории git).
|
||||
|
||||
**Механизировано:** `AUTOINCREMENT` и `DEFAULT (datetime('now'))` в новых
|
||||
миграциях (`internal/archrules`), время мимо `store.Now()` (`forbidigo`).
|
||||
|
||||
## Первичные ключи — ULID, не автоинкремент
|
||||
|
||||
- **PK сущности — TEXT ULID** (26 символов Crockford base32), генерируется
|
||||
**приложением** в момент создания записи. `INTEGER PRIMARY KEY
|
||||
AUTOINCREMENT` в новых таблицах не используем.
|
||||
**приложением** в момент создания записи.
|
||||
- Почему ULID: сортируем по времени создания (`ORDER BY id` = хронология),
|
||||
компактен и удобен в URL/логах (без дефисов — grep и двойной клик берут id
|
||||
целиком), глобально уникален across таблиц — поиск по голому id находит
|
||||
@@ -43,9 +45,10 @@
|
||||
`2006-01-02T15:04:05Z` (секундная точность). Фиксированная ширина сохраняет
|
||||
лексикографическую сортировку TEXT = хронологию (`ORDER BY created_at`).
|
||||
Единая точка генерации — приложение: `store.Now()` + `store.FormatTime`/
|
||||
`ParseTime` (аналогично `ident.NewID` для id); `DEFAULT (datetime('now'))` на
|
||||
колонках **не используется** (fail-loud при забытой вставке: `NOT NULL` без
|
||||
дефолта). Зона хранения всегда UTC; таймзона отображения в UI — конфиг
|
||||
`ParseTime` (аналогично `ident.NewID` для id), а не дефолт в схеме — так
|
||||
забытая вставка падает громко (`NOT NULL` без дефолта). Измерение
|
||||
длительности — не метка времени: для внешних вызовов его засекает
|
||||
`logging.StartCall`. Зона хранения всегда UTC; таймзона отображения в UI — конфиг
|
||||
`[general].timezone`.
|
||||
- Миграции — goose (`internal/store/migrations`): SQL-файлы для DDL;
|
||||
Go-миграции (`goose.AddMigrationContext`) — когда нужен код (генерация
|
||||
|
||||
Reference in New Issue
Block a user