Architecture Decision Records (ADRs)¶
This page provides an overview of all architectural decisions made for envresolve.
What are ADRs?¶
Architecture Decision Records document important architectural decisions along with their context and consequences. They help track the "why" behind design choices.
Current ADRs¶
ADR 0001: Variable Expansion with Regular Expressions¶
Status: Accepted Date: 2025-10-11
Decided to use regular expressions for variable expansion instead of manual string parsing.
ADR 0002: Custom Exception Hierarchy¶
Status: Accepted Date: 2025-10-11
Established a custom exception hierarchy with base exceptions for better error handling.
ADR 0003: Structured Exception Design¶
Status: Accepted Date: 2025-10-11
Defined structured exception design for variable expansion errors.
ADR 0004: Stateless Function-Based Variable Expansion¶
Status: Accepted Date: 2025-10-11
Chose a stateless function (expand_variables()
) as the core API with convenience wrapper classes.
Key Decision: Use expand_variables(text, env)
instead of a stateful VariableExpander(env)
class.
Rationale:
- Simpler and more explicit
- No hidden state
- Better for testing
- More Pythonic for stateless operations
ADR 0005: String-Based API with Idempotent Resolution¶
Status: Accepted Date: 2025-10-12
Decided to use a string-based API instead of data models for resolution results.
Key Decision: Return str
directly instead of ResolutionResult
models.
Rationale:
- Users ultimately need strings for
os.environ
- Zero conversion overhead
- Infrastructure utilities should be transparent
- Idempotent resolution is safer and more composable
ADR 0006: Nested Variable Expansion Implementation¶
Status: Accepted Date: 2025-10-13
Chose a two-phase iterative algorithm for supporting nested variable expansion like ${VAR_${NESTED}}
.
Key Decision: Expand innermost curly braces first, then simple variables, iterating until stable.
Rationale:
- Correct inside-out evaluation order
- Predictable behavior for complex nesting
- No recursion depth limits
- Clear error detection
ADR 0007: Layer Separation (Services vs Application)¶
Status: Accepted Date: 2025-10-13
Separated pure business logic (services layer) from environment integration (application layer).
Key Decision: Move EnvExpander
and DotEnvExpander
from services/expansion.py
to application/expanders.py
.
Rationale:
- Single Responsibility Principle
- Better testability (pure logic without I/O)
- Clear dependency direction
- Matches clean architecture patterns
ADR 0008: Circular Reference Chain Tracking¶
Status: Accepted Date: 2025-10-13
Extended CircularReferenceError
to include full reference chain for better debugging.
Key Decision: Add chain: list[str]
attribute showing complete cycle (e.g., ["A", "B", "C", "A"]
).
Rationale:
- Immediate visibility of complete cycle
- Better debugging experience
- Programmatic access to cycle information
- Actionable error messages
ADR 0009: Manual Provider Registration Pattern¶
Status: Accepted Date: 2025-10-13
Established manual provider registration with a global registry instead of auto-discovery or dependency injection.
Key Decision: Users explicitly call register_azure_kv_provider()
before resolving secrets. Providers are stored in a module-level singleton registry.
Rationale:
- Opt-in dependencies (only load what you need)
- Explicit control over initialization timing
- Resource efficiency through singleton providers
- Clear API surface
ADR 0010: Iterative URI Resolution¶
Status: Accepted Date: 2025-10-13
Implemented iterative resolution with cycle detection to support URI-to-URI resolution chains.
Key Decision: Use a while
loop with a seen
set to resolve URIs iteratively until a stable value is reached or a cycle is detected.
Rationale:
- Supports arbitrary nesting depth
- Maintains idempotency (plain strings pass through)
- Safe cycle detection without infinite loops
- Flexible for mixed variable/URI resolution
ADR 0011: Conditional Doctest Skip¶
Status: Accepted Date: 2025-10-13
Implemented pytest fixture-based conditional skipping for doctests that require optional dependencies.
Key Decision: Use autouse fixture in conftest.py
to detect Azure SDK availability and skip Azure-related doctests when dependencies are missing.
Rationale:
- Doctests validate documentation when dependencies are present
- Graceful degradation without Azure SDK
- Consistent with pytest marker strategy
- Avoids manual
+SKIP
directives
ADR 0012: Pytest Markers for Azure Dependencies¶
Status: Accepted Date: 2025-10-13
Introduced dedicated pytest marker (azure
) to isolate tests requiring optional Azure SDK dependencies.
Key Decision: Mark Azure-dependent tests with @pytest.mark.azure
and provide tests_without_azure
Nox session that excludes them.
Rationale:
- Core test suite runs without optional dependencies
- Clear signal for which tests require Azure
- CI can run lightweight and full test suites separately
- Minimal disruption for contributors
ADR 0013: Class-Based API Design¶
Status: Accepted Date: 2025-10-14
Encapsulated resolution state in EnvResolver
class with module-level facade for backward compatibility.
Key Decision: Introduce EnvResolver
class that encapsulates provider registry and resolver instance, expose singleton instance through module-level functions.
Rationale:
- Eliminates
global
keyword usage - Better testability (tests can instantiate isolated resolvers)
- Maintains simple module-level API
- Supports multiple resolver instances if needed
ADR 0014: Importlib Lazy Import¶
Status: Accepted Date: 2025-10-14
Used importlib.import_module
for lazy loading of optional Azure SDK dependencies with rich error messages.
Key Decision: Defer Azure SDK imports until register_azure_kv_provider()
is called, and raise ProviderRegistrationError
(not ImportError
) with helpful installation instructions when dependencies are missing.
Rationale:
- Users can import envresolve without Azure SDK
- Clear, actionable error messages
- Aligns with custom exception hierarchy (ADR-0002)
- Extensible pattern for future optional providers
ADR 0015: Manage Azure Live Test Infrastructure with Terraform¶
Status: Accepted Date: 2025-10-15
Standardized live Azure Key Vault testing with Terraform-managed resources and explicit pytest gating.
Key Decision: Use Terraform manifests in infra/terraform
plus helper Nox sessions to provision/destroy a Key Vault and sample secret for live tests.
Rationale:
- Repeatable provisioning for contributors and CI
- Clear separation between mocked and live suites via markers
- Easier cleanup through scripted Terraform destroy
ADR Template¶
All ADRs follow a consistent template defined in ADR 0000: ADR Template.
Contributing¶
When making significant architectural decisions, please:
- Review existing ADRs to ensure consistency
- Use the ADR template for new decisions
- Document both what you chose AND what you rejected
- Include the "why" behind your decision