Skip to main content

Environments and promotion

An environment is a stage of the software lifecycle with its own AWS account, its own VPC and its own sizing; promotion is how one reviewed change moves through those stages in order. The BuiltForProd platform has four stages, sandbox, dev, staging and prod, each a member account of the plat organizational unit, and every stage is generated from the same definitions with a small block of per-stage values.

Why it matters in production

Environments that were built by hand drift apart, and then staging proves nothing about prod. Environments that share an account share a blast radius, and then a mistake in dev can touch customer data. Separate accounts give each stage a hard identity and billing boundary; identical definitions give each stage the same shape; explicit per-stage values make the differences reviewable; and a fixed promotion order with approvals at the right point makes every production change deliberate.

How the platform applies it

One account per stage

StageSlugAccountPurposeIsolation domain
sandboxsbxplat-sandbox (151515151515)Experimentation; looser access, no production data, excluded from conformance packsnonprod
devdevplat-dev (121212121212)Daily development and automated testing; every merge to main deploys herenonprod
stagingstgplat-staging (131313131313)Production mirror for QA and release verification; high-availability layout like prodnonprod
prodprdplat-prod (141414141414)Customer workloads; changes only through reviewed pull requests and the prod GitHub Environmentprod

Every workload account gets the same seven landing-zone units from the stacks/plat-account template, and its own spoke VPC from the address plan; see landing zones. Resource names carry the stage: acme-usw2-prd-vpc, acme-usw2-dev-eks. SSM parameters follow /acme/usw2/<stage>/<service>/<key>.

The same shape, different values

The blueprint repositories describe each stage with one stack file that instantiates a template and sets only what differs. The web platform's dev and prod files are identical apart from their values:

Valuedevprod
public_domaindev.company.comprod.company.com
eks_node_min_size / max2 / 33 / 4
argocd_hafalsetrue
argocd_auto_synctruefalse
log_retention_days30365
docdb_instance_count13
docdb_deletion_protectionfalsetrue
redis_num_cache_clusters / redis_multi_az1 / false2 / true

Staging mirrors prod's high-availability layout (multi-AZ data stores, ArgoCD in HA mode, a three-node node group) so that failover is exercised before it matters in prod; dev stays single-instance for cost. Each value is tagged # @optional: with its consequence and price, such as about $60 per month per extra DocumentDB instance. The mechanism is described under Terragrunt units and stacks.

Promotion

Infrastructure repositories. One merge to main plans and applies every stage. The blueprint workflows run a matrix of dev, staging and prod whose jobs start in parallel (fail-fast: false), each under the GitHub Environment of the same name; the prod job waits for that Environment's required reviewers. The landing zone's apply is one job gated by the prod Environment. Applies queue per branch and are never cancelled.

StageTriggerApprovalRollback
devMerge to mainNonegit revert, merge, auto-apply
stagingSame mergePull request reviewgit revert, merge, auto-apply
prodSame mergeRequired reviewers of the prod Environmentgit revert plus manual validation

Application code. Images are built once and promoted by immutable tag (main-<sha> per build, vX.Y.Z added at release). The web application reaches dev on merge (auto-merged deploy pull request, ArgoCD auto-sync), staging on a GitHub Release (auto-merged, auto-sync), and prod through promote-prod.yml with the release tag: prod Environment approval, a deploy pull request a person merges, then a manual ArgoCD sync. The ETL pipeline reaches dev on merge and staging on release, then waits for prod approval inside the release workflow before deploying the same tag to prod. Rollback is promoting or re-running the previous tag; nothing is rebuilt. See immutable artifacts and GitOps.

Access narrows toward prod

Engineers hold PowerUser access in sandbox, dev and staging and ReadOnly in prod; only Platform Leads and DevOps Leads can change prod. Secrets follow the same line: every engineer group can decrypt the sandbox and dev secrets, only the lead groups can decrypt staging and prod. In the Baseline, code ownership assigns environments/plat/prod/ to the platform leads and the security team; an approval from either team satisfies the rule.

Worked example: a larger prod database

Prod needs a third DocumentDB replica. An engineer changes docdb_instance_count = 3 in environments/prod/us-west-2/terragrunt.stack.hcl of the web platform and opens a pull request. The plan workflow shows one added instance in the prod matrix job and no change in dev or staging, because their stack files carry their own values. A reviewer approves the pull request; on merge, the dev and staging apply jobs are no-ops and the prod job waits for the prod Environment's reviewers before adding the instance. Had the engineer wanted the same in staging, the same value in the staging file would have applied in the staging job of the same merge, which starts in parallel with the prod job.

Common mistakes

  • Testing in prod because dev "is not the same". Dev is generated from the same definitions; the differences are the values in its stack file, and they are the only differences.
  • Setting a per-stage value in the template or the definition. A template default applies to every stage; per-stage choices belong in the stage file, tagged.
  • Promoting by rebuilding. Staging and prod run the tag that was built for dev; a rebuild is a different artifact.
  • Granting prod write access to speed up a release. Prod changes go through the prod Environment and a person's merge; that is the control, not an obstacle.
  • Adding a stage without placing it in an isolation domain. A new stage must be in exactly one domain in network.hcl, or the plan fails.