Branching, Previews & Environment Promotion
Trunk-based vs GitFlow, ephemeral PR previews, environment promotion state machine, config vs artifact separation.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
A feature needs to reach users
Prefer
Short pull request into main
Trunk-based flow. The unfinished behavior sits behind a flag. A preview shows the pull request. Staging promotes the digest from main.
- Everyone integrates continuously.
- Hotfix is a short branch back to main, then deploy.
- Preview TTL keeps the cloud bill bounded.
Alternative
Long-lived GitFlow branches
develop, release, and hotfix each carry their own CI. Merges drift. A freeze window feels safer and the feedback loop gets longer.
- The same fix lands twice, or it lands once and the other branch forgets.
- You pay duplicate pipelines on release branches.
- Staging bits stop matching the branch that will ship.
States of one digest
CI owns every transition through prod-eligible. A traffic controller owns prod-live. A shell script does not invent another source of truth.
- 1
Built
The digest exists and is signed. - 2
Previewed
The pull-request stack ran that digest. - 3
Staged
Staging smoke passed on that digest. - 4
Eligible
Policy and approvals passed. The pipeline stops here. - 5
Live, then retired
The traffic controller serves it, possibly as a canary. A newer digest or a rollback retires it.
Overview
Branching decides how change flows. Previews decide how a human sees the change before merge. Promotion decides how a digest climbs toward production.
Trunk-based development with short-lived pull requests and ephemeral previews is the default for teams that deploy often. Long-lived release branches still fit software that ships on a store cadence or a regulated freeze. They multiply hotfix paths and CI.
Interview stance: prefer trunk-based for services. Explain GitFlow when you see it, which is usually mobile release trains or a mandated freeze, and still build one digest per release candidate.
Branching models
| Model | Flow | CD fit | Pain |
|---|---|---|---|
| Trunk-based | Short pull requests into main. Unfinished work behind flags | Excellent | Needs discipline and flags |
| GitHub Flow | Pull request into main, then deploy | Strong | Needs solid CI gates |
| GitFlow | develop, plus release/*, plus hotfix/* | Weak to medium | Merge debt, dual CI |
| Release branches | Cut release-x from trunk | Medium | Backports |
Long-lived feature branches diverge from trunk, create merge conflicts, and skip continuous integration with everyone else's work. Flags plus a short pull request keep the unfinished work off the default user path without a private branch that rots.
A trunk-based hotfix is a fix on main, or a short branch into main, deployed immediately. Cherry-pick onto a release branch only when that branch still has to ship the fix.
Flow
- 1
1. Open a short-lived PR
- next2. Run CI and a preview
- 2
2. Run CI and a preview
- next3. Merge into main
- 3
3. Merge into main
- next4. Promote digest to staging
- 4
4. Promote digest to staging
- next5. Pass the prod gate
- 5
5. Pass the prod gate
- next6. Mark the digest eligible
- 6
6. Mark the digest eligible
- next7. Return hotfixes to main
- 7
7. Return hotfixes to main
Lesson map
Branching, Previews & Environment Promotion
Trunk-based vs GitFlow, ephemeral PR previews, environment promotion state machine, config vs artifact separation.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB a["1. Open a short-lived PR"] b["2. Run CI and a preview"] c["3. Merge into main"] d["4. Promote digest to staging"] a -->|1. Open a short-lived PR| b b -->|2. Run CI and a preview| c c -->|3. Merge into main| d
Monorepos keep one branch model. Path-based pipelines decide which services build and promote. Per-service long-lived branches recreate GitFlow inside the repo. Which paths skip the image build is the CI performance lesson.
Preview environments
A preview is an ephemeral stack per pull request: the app and its dependencies, a URL on the pull request, torn down on close.
| Strength | Cost |
|---|---|
| Reviewers see the real UX or API | Idle stacks cost money |
| Wiring bugs show up before merge | Seed data and PII policy |
| Design can review early | Shared SaaS quotas |
Design rules:
- One namespace or stack per pull request, with a hard TTL.
- Synthetic seed data. Never a production snapshot that contains personal data.
- Sign-on tied to the pull-request identity. No open admin panel.
- The preview runs the pull-request digest, not
main:latest.
Previews isolate one pull request. Shared staging validates main with production-like dependencies. Serious systems use both.
Feature flags and previews solve different problems. A flag controls exposure on a shared environment. A preview isolates unfinished UX. They complement each other. Fowler's toggle essay is the flag taxonomy. This page only needs the split.
Cost controls: destroy on close and on idle TTL, scale to zero where you can, share a data plane with a schema per pull request when isolation allows, cap concurrent previews per repo, and use smaller instances than staging.
Promotion contract
Promotion is a policy transition on an artifact.
- The pull-request digest goes to preview.
- The
maindigest goes to staging, usually automatically. - The staging-proven digest goes to production, through a gate.
Config travels separately: environment variables, feature flags, a secret manager. Baking DATABASE_URL into the image forces a rebuild per environment and breaks the hub rule.
| State | Meaning | Who moves it |
|---|---|---|
built | Digest exists and is signed | CI build |
previewed | Ran on the pull-request stack | Preview deploy |
staged | Staging smoke passed | Staging promotion |
prod_eligible | Policy and approvals passed | Gate job |
prod_live | Serving traffic, maybe as a canary | Traffic controller |
retired | Superseded or rolled back | New production digest, or rollback |
CI owns the transitions through prod_eligible. Traffic controllers own prod_live. Analysis of a canary belongs with metrics, not with sleep in a shell script. Deployments, probes, and autoscaling are out of scope here. The contract hands the eligible digest to Rolling, Blue-Green and Canary.
Deploying every main commit is continuous deployment when the gates, the progressive rollout, and an instant rollback exist. Otherwise stop at continuous delivery: a production-ready digest and a short approval. The approval mechanism is the environment rule on the anatomy page.
| Concern | Travels with the digest? | Mechanism |
|---|---|---|
| App binary or image | Yes | Registry digest |
| Feature flags | No | Flag service |
| Database URL and secrets | No | Secret manager and IAM |
| Hashed static assets | Often a separate digest | CDN object keyed by content hash |
| Migration scripts | Versioned with the app | Expand/contract order |
Environment drift is staging config, data, or infrastructure diverging from production so the tests lie. Fight it with infrastructure-as-code parity, digest promotion, and synthetic data contracts.
If GitFlow is mandatory
Still build once per release-candidate digest. Hotfix branches merge back to the integration branch and to trunk immediately. Accept duplicate CI on release/*, or negotiate trunk-based flow for the services that do not need a store freeze.
A story you can tell: pull-request previews for UX, automatic staging of digests from main, production promotion through an environment approval, then a canary owned by the traffic controller. Change-fail rate drops because staging bits and production bits match. Preview TTL is what cuts the cloud spend.
Sketch
jobs:
preview:
if: github.event_name == 'pull_request'
environment:
name: preview-pr-${{ github.event.pull_request.number }}
url: https://pr-${{ github.event.pull_request.number }}.preview.example.com
runs-on: ubuntu-latest
steps:
- run: echo "Deploy the PR digest to an ephemeral stack"
prod:
if: startsWith(github.ref, 'refs/tags/v')
needs: [staging_ok]
environment: production
runs-on: ubuntu-latest
steps:
- run: echo "Promote the staging digest. Do not rebuild."Helpers you can run
Press Run. Snippets must be self-contained — no network, files, or native modules.
Press Run. Snippets must be self-contained — no network, files, or native modules.
Press Run. Snippets must be self-contained — no network, files, or native modules.
Interview Q&A
Why are long-lived feature branches hostile to CI/CD?
Answer
They diverge from trunk, pile up merge debt, and skip integration with everyone else's commits. Prefer flags and a short pull request.
How do hotfixes work on trunk?
Answer
Fix on main or on a short branch into main, and deploy. Cherry-pick onto a release branch only if that branch still needs the fix.
Preview versus shared staging?
Answer
A preview isolates one pull request and raises review fidelity. Staging validates main against production-like dependencies. Use both.
Should production deploy on every main commit?
Answer
Yes, when gates, a progressive rollout, and instant rollback exist. Otherwise stop at a production-ready digest and a short approval. The traffic shift is Rolling, Blue-Green and Canary.
How do monorepos map branches to services?
Answer
One branch model for the repo. Path-based pipelines choose which services build and promote. Avoid a long-lived branch per service.
What is environment drift?
Answer
Staging config, data, or infrastructure diverges from production, so a green staging run lies. Digest promotion, infrastructure parity, and synthetic data contracts are the countermeasures.
Feature flags versus preview environments?
Answer
Flags control runtime exposure on a shared environment. Previews isolate unfinished UX. Use both. Neither one replaces a digest pin.
Pitfalls
- A preview that tracks
main:latestand reviews the wrong bytes. - Production snapshots, personal data included, copied into a pull-request database.
- A promotion script that rebuilds so it can embed the database URL.
- Canary math, replica counts, or probe settings answered from this page.
- GitFlow hotfix branches that never merge back to trunk.
Take the last production incident you remember. Say which promotion state the bad digest was in, whether staging had run those exact bytes, and which system was allowed to move it to live. Stop before you describe replica counts.