Env files are not environments: isolate with Azure CLI discipline
Six nearly identical .env.example files feel thorough. They guarantee drift. Someone updates three. Prod still reads the fourth. Agents and humans resolve different paths. The repo looks configured. The cloud is the actual environment, and nobody agrees which subscription is live.
Treat Azure CLI context (subscription, resource group, identity) as the isolation boundary. Keep a small set of live env files plus stage templates that document names, not secrets. POC and MVP can share product code while keeping separate deploy stages.

Figure 1. Cloud isolation is subscription, resource group, identity, and network posture. Env files only describe names. Source: Microsoft Learn: private endpoint overview.

Figure 2. POC and MVP can share product code while keeping separate deploy stages and Azure CLI contexts. Source: Microsoft Learn: App Service deploy best practices.
I learned this on app stacks where dotenv archaeology outgrew the feature work. Local runs worked. Slot swaps failed. The bug was not Node. It was four templates telling four slightly different truths about the same Key Vault name.
The core idea
Files describe configuration shape. Azure CLI context selects the real environment.
Application secrets belong in Key Vault and App Settings. Templates only document names and non-secret defaults. Collapsing redundant examples reduces the places drift can hide. Humans and coding agents should resolve paths from one documented source of truth, not from whichever folder they opened first.
A model that stays explainable
1. Two live files, stage templates, not six clones
Keep a minimal local override pattern and explicit stage templates (for example POC and MVP, or Test and Prod). Delete near-duplicate examples that differ by one hostname. If a value is stage-specific, it belongs in a stage template or in Azure, not in a sixth "example-final-final" file.
2. Names in Git, secrets in Azure
Templates list setting names and point to Key Vault references or App Settings. They do not embed connection strings. Reviewers should reject secret material in env examples the same way they reject committed credentials. Documentation of names is enough for onboarding.
3. Azure CLI context is the environment switch
Subscription, resource group, and the identity you deploy as define where changes land. A correct .env against the wrong subscription is a successful deployment to the wrong world. Scripts should print and verify az account show and target RG before apply. Make context visible in CI logs.
4. Shared product code, separate deploy stages
POC and MVP can share the application. They should not share resource groups, app registrations without intent, or "temporary" settings that became permanent. Stage isolation is a deploy concern. Code forks for env isolation are usually a mistake.
5. One path resolution story for humans and agents
Document canonical paths for templates, local overrides, and deploy parameter files. Coding agents that invent a third env location will recreate drift at machine speed. Put the map in README or a short ops doc next to the templates.
6. Fail closed on missing required names
Startup or deploy checks should fail when required App Settings are absent, not silently fall back to a neighbor stage's values. Friendly defaults that reach the wrong store are hostile in production.
Failure modes I design against
Template sprawl. Six examples, four stale, two wrong.
Secrets in Git "just for local." They leak. Rotate forever.
Right file, wrong subscription. Green deploy, wrong blast radius.
POC settings copied into MVP by habit. Networking and identity assumptions travel unpaid.
Agent-invented env paths. Automation amplifies folklore.
Silent fallbacks across stages. Wrong Key Vault, correct HTTP 200.
Agents make drift louder
Coding agents will happily create .env.local2 and hard-code a resource group they saw in an old transcript. Constrain them with the same path map humans use. Prefer scripts that accept --stage and resolve everything else from documented templates plus Azure CLI context. If an agent can invent a new env dialect in one session, your isolation model was already too fuzzy for humans.
Trade-offs
Fewer templates means less copy-paste onboarding and more reliance on Azure portal or CLI literacy. That is the correct skill for cloud apps. Strict context checks add a few seconds to scripts and prevent week-long misdeploy stories. Shared code across stages requires discipline in feature flags and setting names; the alternative is divergent forks that never merge. Key Vault references add moving parts. They beat plaintext env files in repos.
What I would put on an ADR
- Canonical env template set (count and names) checked into Git.
- Secrets only in Key Vault / App Settings; templates document names.
- Deploy scripts verify Azure CLI subscription and RG before mutate.
- POC and MVP (or Test/Prod) use separate resource groups and stage settings.
- Single documented path map for humans and agents.
- Startup/deploy fails if required settings are missing.
Primary references: Microsoft Learn on Azure CLI configuration, App Service app settings, and Key Vault references.
A minimal template layout that survives contact
One README section names the live override file, the stage templates, and the Azure targets those templates describe. Deploy scripts read stage as an argument, set subscription and resource group explicitly, then apply. Local developers copy a template once and keep secrets out of Git. When someone proposes a seventh example file, ask which of the six already lied. Usually the answer is "more than one."
Print the resolved subscription id and resource group at the start of every deploy log line humans read. Visibility beats tribal memory when someone asks "which env did we just touch?" after a noisy afternoon.
Stage names in scripts should match stage names in Azure resource groups closely enough that a stranger can map them without a decoder ring.
Closing
Env files are not environments. They are hints.
Isolate with Azure CLI discipline, shrink the template set, keep secrets in Azure, and make context impossible to ignore. Drift shrinks when the cloud boundary is the boundary you actually use.
