Files
dev-conventions/lang/go/config.md
T
av 4a59c71737 заведён канон общих конвенций для личных проектов
- 13 конвенций по осям arch / lang / stack / common; репозитории берут
  оттуда копии в свой docs/conventions/ и коммитят их у себя
- conv — синхронизация копий: add / status / diff / pull / push, локальные
  регионы исключены из сравнения, поэтому расхождение не даёт шума
2026-07-25 18:18:18 +03:00

90 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
status: рекомендуемая
extends: arch/config.md
---
# Конфигурация: реализация на Go
Как `arch/config.md` выглядит в Go-приложении.
## Формат и загрузчик
- TOML. Разбор и валидация — целиком в `internal/config`; наружу отдаётся
готовая структура `Config`.
- Одна корневая структура `Config` с под-структурами по секциям — имена
структур совпадают с именами секций, чтобы конфиг и код читались рядом.
- Умолчания — в `Default()`, поверх накладывается разобранный файл.
- Флаг `--config=path` переопределяет путь; по умолчанию `config.toml` в
рабочей директории, образец — `config.example.toml`.
## Длительности
`time.Duration` не разбирается из строки TOML сама по себе — нужен свой тип
с `UnmarshalText`, отдающий `time.Duration`:
```go
type Duration time.Duration
func (d *Duration) UnmarshalText(b []byte) error { }
func (d Duration) Std() time.Duration { }
```
Так в конфиге видна единица измерения (`poll_interval = "5s"`), а не голое
число. Цена: ошибка в длительности всплывает **на разборе TOML**, до общей
валидации, поэтому в общий сбор проблем она не попадает — про неё узнаёшь
отдельно и первой.
## Чтение окружения
Приложение не читает окружение для конфигурации. Механизируется
`forbidigo`, и паттерн должен покрывать **все** входы, а не только
`os.Getenv`:
```
^os\.(Getenv|LookupEnv|Environ|ExpandEnv)$
```
Правило про приложение, поэтому за его границей запрет не действует:
- **тесты** — не приложение: интеграционному тесту нормально брать
креды внешнего сервиса из окружения;
- **переменные рантайма** — те, что читает не наш код, а Go или ОС
(`GOMEMLIMIT`, `GOMAXPROCS`, `GODEBUG`, `TZ`).
Отдельный случай — переменные, которые читает **стандартная библиотека от
имени приложения**: дефолтный `http.Transport` уважает
`HTTP_PROXY`/`HTTPS_PROXY`. Формально их читает не наш код, но это
конфигурация поведения приложения, поэтому прокси задаётся полем конфига и
явным `Transport`, а не окружением.
## Валидация
- Проверки собираются `errors.Join`, чтобы за один запуск показать **все**
проблемы конфига, а не первую.
- IANA-зона валидируется `time.LoadLocation`. База зон встраивается
импортом `_ "time/tzdata"` **в `main`**, а не в библиотечном пакете:
иначе ~450 КБ zoneinfo навязываются каждому импортёру. Со встроенной
базой ошибка `LoadLocation` означает битое имя зоны, а не отсутствие
zoneinfo в контейнере.
- Невалидный конфиг — `slog` уровня `ERROR` и `os.Exit(1)` из `main`, до
старта серверов и воркеров.
## Секреты
Go-специфики нет: секреты приходят из деплоя уже в файле, проверка их
непустоты идёт вместе с остальной валидацией — см. базу.
<!-- local:поля -->
<!-- /local -->
<!-- local:механизировано -->
<!-- /local -->
## Связано
- `lang/go/time.md` — зона отображения и формат времени.
- `lang/go/logging.md``slog`, которым падает невалидный конфиг.
<!-- local:связано -->
<!-- /local -->