patternpythonModeratepending
Pattern: Configuration management hierarchy
Viewed 0 times
configurationenvironmenthierarchyvalidationenv-varsconfig
Problem
Configuration is scattered across env vars, config files, command-line args, and defaults. No clear precedence or validation.
Solution
Implement a configuration hierarchy with clear precedence:
# Precedence (highest to lowest):
# 1. Command-line arguments
# 2. Environment variables
# 3. Local config file (.env.local, not in git)
# 4. Environment-specific config (config/production.yml)
# 5. Default config (config/default.yml)
# 6. Hardcoded defaults in code
import os
from dataclasses import dataclass, field
@dataclass
class Config:
port: int = 3000
db_url: str = ''
debug: bool = False
log_level: str = 'info'
max_connections: int = 10
@classmethod
def load(cls):
return cls(
port=int(os.getenv('PORT', 3000)),
db_url=os.environ['DATABASE_URL'], # Required, no default
debug=os.getenv('DEBUG', 'false').lower() == 'true',
log_level=os.getenv('LOG_LEVEL', 'info'),
max_connections=int(os.getenv('MAX_CONNECTIONS', 10)),
)
def validate(self):
if not self.db_url:
raise ValueError('DATABASE_URL is required')
if self.port < 1 or self.port > 65535:
raise ValueError(f'Invalid port: {self.port}')
if self.log_level not in ('debug', 'info', 'warning', 'error'):
raise ValueError(f'Invalid log level: {self.log_level}')
Rules:
# Precedence (highest to lowest):
# 1. Command-line arguments
# 2. Environment variables
# 3. Local config file (.env.local, not in git)
# 4. Environment-specific config (config/production.yml)
# 5. Default config (config/default.yml)
# 6. Hardcoded defaults in code
import os
from dataclasses import dataclass, field
@dataclass
class Config:
port: int = 3000
db_url: str = ''
debug: bool = False
log_level: str = 'info'
max_connections: int = 10
@classmethod
def load(cls):
return cls(
port=int(os.getenv('PORT', 3000)),
db_url=os.environ['DATABASE_URL'], # Required, no default
debug=os.getenv('DEBUG', 'false').lower() == 'true',
log_level=os.getenv('LOG_LEVEL', 'info'),
max_connections=int(os.getenv('MAX_CONNECTIONS', 10)),
)
def validate(self):
if not self.db_url:
raise ValueError('DATABASE_URL is required')
if self.port < 1 or self.port > 65535:
raise ValueError(f'Invalid port: {self.port}')
if self.log_level not in ('debug', 'info', 'warning', 'error'):
raise ValueError(f'Invalid log level: {self.log_level}')
Rules:
- Secrets ONLY in env vars (never in files committed to git)
- Validate config at startup (fail fast)
- Log config values at startup (except secrets)
- Use typed config objects, not raw os.getenv() everywhere
Why
A clear config hierarchy prevents surprises about which value wins. Validation at startup catches misconfigurations before they cause runtime errors.
Revisions (0)
No revisions yet.