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

4.4 KiB
Raw Blame History

status, extends
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:

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-специфики нет: секреты приходят из деплоя уже в файле, проверка их непустоты идёт вместе с остальной валидацией — см. базу.

Связано

  • lang/go/time.md — зона отображения и формат времени.
  • lang/go/logging.mdslog, которым падает невалидный конфиг.