ADR-007: Feature Flag Management via Azure App Configuration¶
Context and Problem Statement¶
The portfolio backend needed a mechanism to gate features at runtime — specifically the cv-generation endpoint — without requiring a redeploy. The flag also needed to behave differently per environment (enabled in dev, disabled in prod by default).
Three approaches were considered: environment variables baked into the container image at deploy time, a third-party feature flag service, or Azure App Configuration.
Considered Options¶
-
Environment variables — set
FEATURE_CV_GENERATION=true/falsein the Bicep template or as a GitHub Actions input. Simple to implement, but requires a new deployment to toggle a flag. No audit trail, no runtime control. -
Third-party feature flag service (LaunchDarkly, Flagsmith, etc.) — dedicated SDK, rich targeting rules, audit trail. Adds a paid external dependency and a new secret (API key) to manage.
-
Azure App Configuration — native Azure service, free tier, per-environment label support (
dev/prod), managed identity authentication (no connection strings), and a REST API that the backend can poll with a TTL cache.
Decision Outcome¶
Chosen option: "Azure App Configuration". It is the natural fit for an Azure-native stack: no new external dependency, no new secret to manage (the backend managed identity is granted App Configuration Data Reader via RBAC), and per-environment labels (dev/prod) allow the same flag key (cv-generation) to resolve to different values per deployment without any code change.
The store is provisioned in the shared resource group (dna-shared-portfolio-westeurope-rg, Free tier) so it persists across environment teardowns and is not duplicated per environment.
Implementation pattern¶
# api/feature_flags.py — 30-second TTL cache
_cache: dict[str, bool] = {}
_cache_ts: float = 0.0
CACHE_TTL = 30 # seconds
def get_feature_flags() -> dict[str, bool]:
global _cache, _cache_ts
if time.time() - _cache_ts < CACHE_TTL:
return _cache
# fetch from Azure App Configuration via managed identity
...
The backend falls back to the FEATURE_CV_GENERATION environment variable when the App Config endpoint is not configured (local development, docker-compose).
Consequences¶
- Good, because feature flags can be toggled in the Azure Portal or via
az appconfig kv setwith zero downtime and no redeploy. - Good, because per-environment labels (
dev/prod) allow a single flag key to resolve differently without branching in code. - Good, because no connection string is required — the backend managed identity authenticates via RBAC (
App Configuration Data Reader). - Good, because the Free tier (10 000 operations/day) is sufficient for a portfolio workload with a 30-second client-side cache.
- Bad, because the Free tier has no SLA and no geo-replication — suitable for this use case but not for high-availability production workloads.
- Bad, because adding a new flag requires updating both the shared Bicep (
app_configuration.bicep) and the backend consumer — there is no self-service flag creation UI in the codebase. - Neutral, because local development bypasses App Config entirely via the
FEATURE_CV_GENERATIONfallback env var, which means the local behaviour is always a hardcoded value rather than a live flag read.
Page history
| Field | Value |
|---|---|
| Last updated | — |