Skip to content

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/false in 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

python
# 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 set with 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_GENERATION fallback env var, which means the local behaviour is always a hardcoded value rather than a live flag read.

Page history

Field Value
Last updated