Configuration Sources
Production services load configuration from multiple sources with a clear precedence order. Environment variables override config files, which override defaults.
Goconfiguration-precedence.go
Environment variables are highest precedence — they are deployment-specific. Config files provide structural configuration. Defaults ensure the service works out of the box. This follows 12-factor app methodology.
Environment Variables
Goenvironment-variable-loading.go
os.Getenv returns the value or empty string. Default values are applied when the env var is not set. getEnvRequired enforces that an env var is set. Validate all configuration after loading.
Config Files
Read the config file with os.ReadFile. Parse with yaml.Unmarshal. Handle missing files gracefully — use defaults. Search multiple locations for flexibility. Always validate after parsing.
Validation
Validate every field with clear error messages. Collect all errors before returning. Validate ranges, required fields, and valid enum values. Fail at startup — never start with invalid configuration.
Defaults
DefaultConfig returns sensible defaults. LoadConfig layers overrides on top: defaults -> config file -> environment variables. mergeConfig applies non-zero values from the file. applyEnvOverrides sets env-specific values.
Reload Boundaries
Gowhat-can-be-reloaded.go
Document which fields can be reloaded and which require a restart. Atomic.Value or sync.Mutex protects reloadable config. Never reload fields that require process restart — port changes, connection strings, etc.
When reloading configuration, validate the new config before applying it. If validation fails, keep the old config and log the error. Never apply invalid configuration — it can crash the service.