Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXIX · Branching StrategiesStrategies

Environment branches — branch per environment and why it is an anti-pattern in GitOps

Advanced⏱ ~26 mingit

What you'll learn

  • Articulate what environment branches are: a branch per deployment target
  • Identify the historical reason environment branches existed
  • Recognise why environment branches are an anti-pattern in GitOps
  • Apply the GitOps-correct alternative: a single trunk with environment overlays

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Environment branches are the pattern where each deployment target has its own branch: production, staging, development. The convention was: develop on development, merge to staging when ready for QA, merge to production when ready for release. The branch was the environment; the merge was the promotion.

This pattern predates GitOps. It is an anti-pattern in GitOps because the controller is the deployment surface: the controller reads one branch and applies committed state. With environment branches, the controller must either read multiple branches (audit trail splits) or read one branch (others drift).

What environment branches are

The typical layout: main is the development branch; staging is pre-production; production is live. Promotion is a merge: git checkout staging && git merge main to promote to staging; git checkout production && git merge staging to promote to production.

flowchart LR
    M["main - development"]
    S["staging - QA"]
    P["production - live"]
    M -->|"merge"| S
    S -->|"merge"| P
    S -.->|"drift"| M
    P -.->|"drift"| S

The diagram shows the environment-branches topology: a linear promotion path with drift arrows pointing back from each promoted branch. Drift is inevitable.

Why the pattern existed

Environment branches made sense in a world where deployment was manual (promotion was a deliberate human action), promotion was rare, and no controller existed. The branch was the promotion event; the merge was the deployment.

Why the pattern is an anti-pattern in GitOps

  • The controller reads one branch. Environment branches require either multiple controllers (one per branch) or one controller reading one branch (others drift).
  • Drift is unavoidable. Each branch accumulates its own history: hotfixes on production never made it to staging; merges to staging never made it to production. Within weeks the branches are three different codebases.
  • Promotion bypasses the controller. The promotion is a git merge — a local operation; the controller is not involved.
  • The audit trail is split. The auditor must read three branches to reconstruct the timeline.

The GitOps-correct alternative

The correct pattern is a single trunk with environment overlays; the controller manages promotion.

Kustomize. The trunk holds the base manifests; each environment has an overlay directory that patches the base. The controller reads the base and the overlay for its cluster. Promotion is a change to the overlay, not a merge between branches.

Helm values. The trunk holds the chart; each environment has a values file. Promotion is a values change.

flowchart LR
    subgraph TRUNK["main - single source"]
        B["base/"]
        O1["overlays/staging/"]
        O2["overlays/production/"]
    end
    O1 -->|"controller"| C1["staging cluster"]
    O2 -->|"controller"| C2["production cluster"]

The diagram shows the GitOps-correct pattern: one branch, one base, multiple overlays, multiple controllers each reading its own overlay.

When environment branches are still useful

Two situations still benefit:

  • Multi-tenant configurations with significant divergence. A team serving multiple tenants with very different configurations may find a branch-per-tenant cleaner.
  • Regional deployments with regulatory divergence. A team deploying to regions with different regulatory requirements may need branches per region.

For most infrastructure teams, neither applies. Staging and production with values-level differences fit the overlay pattern.

Production discipline

  1. Default to one trunk, multiple overlays. Single branch with overlays, one controller per cluster.
  2. Promote by overlay change, not by branch merge. Promotion is a change to the production overlay.
  3. Audit drift weekly. If environment branches exist, audit them weekly.
  4. Migrate to overlays when drift becomes unmanageable. Forward-going; old branches archived.
  5. Document the per-environment branch exception. Multi-tenant or regional cases documented with rationale.

Cross-course references

  • GitOps with Argo CD - Part II (RepoLayout) covers the GitOps repo layout for the overlay pattern.
  • Kubernetes for Production Sysadmins - Part VIII (KustomizeAndHelm) covers Kustomize and Helm in depth.
  • Terraform for Production Sysadmins - Part XII (WorkspaceStrategy) covers the Terraform analogue.

Quiz

Knowledge check · 4 questions

  1. Q1. A team has run environment branches for six months and is adopting GitOps. They want one Argo CD controller per environment, each reading its own branch. What is the most significant cost?

  2. Q2. Environment branches are not always an anti-pattern; controlled promotion workflows can be a legitimate use case.

  3. Q3. Name the three reasons environment branches are an anti-pattern in GitOps.

  4. Q4. Diagnose a team running environment branches alongside GitOps and recommend a migration to overlays.

    A team has run environment branches for a year. `main`, `staging`, `production` have diverged: production has 34 hotfix commits not on staging or main; staging has 89 commits not on main (abandoned feature work); main has 412 commits not on staging or production. The team is adopting Argo CD. The production audit trail (34 hotfixes) must be preserved.

Passing score: 75%. Answers are checked in this browser.