Skip to content

ADR-066: Centralized settings management with pydantic-settings

Category: architecture Provenance: guided-ai

Decision

Use pydantic-settings for centralized operator configuration instead of scattered os.getenv calls throughout the codebase.

Rationale

Scattered os.getenv calls throughout the codebase lead to: (1) Inconsistent parsing and validation, (2) Difficult discovery of available configuration options, (3) Type safety issues, (4) Hard to test with mocked configurations, (5) No central documentation of settings. pydantic-settings provides: (1) Automatic type validation and coercion, (2) Single source of truth for all configuration, (3) Better IDE support with autocomplete, (4) Easy testing with model instances, (5) Built-in documentation through Field descriptions, (6) Support for .env files and multiple sources, (7) Validation errors with clear messages. Trade-off: Minimal - pydantic-settings was already a dependency, just not utilized properly.

Context: The operator had environment variable parsing scattered across operator.py, constants.py, drift_detection_service.py, health.py, leader_election.py, and various other modules. Each location manually parsed strings to bools, ints, and floats with inconsistent default handling. This made it difficult to understand what configuration options existed and how they interacted. Centralizing in a Settings class using pydantic-settings provides validation, type safety, and documentation in one location.

References: - https://docs.pydantic.dev/latest/concepts/pydantic_settings/ - https://github.com/pydantic/pydantic-settings

Agent Instructions

When adding new configuration options, add them to the Settings class in src/keycloak_operator/settings.py. Never use os.getenv directly in other modules. Import settings from the settings module instead. All environment variables must be documented in the Settings class with Field descriptions.

Rejected Alternatives

Config dataclass

Rejected due to lack of validation and env parsing.

python-dotenv + manual parsing

Rejected due to lack of type safety.

Custom config class

Rejected - reinventing the wheel when pydantic-settings exists.

Keep scattered os.getenv

Rejected due to maintainability issues.