Skip to content

7.3 Branching & Promotion

This page defines how a change travels from a developer's branch to customers. There is one main branch; a release candidate is built from a reviewed commit and the same image digests are used for later promotion. Environments are not branches. Development is local with Docker. The current cloud path is stage; production and automatic push-to-release orchestration are not activated by this policy.

7.3.1 One Main Branch

Every repository has one long-lived branch, main, which is always releasable. Work happens in short-lived branches that live for days, not weeks:

gitGraph commit id: "a1" branch feat/calving-import commit id: "b1" commit id: "b2" checkout main merge feat/calving-import id: "c1" branch fix/milk-units commit id: "d1" checkout main merge fix/milk-units id: "e1" tag: "v1.4.0"
  • Branch names are <type>/<short-description>, with type feat, fix, chore or docs.
  • Our documented merge policy requires a branch to reach main only through a pull request, with the relevant checks passed (format, lint, tests as applicable), no merge conflicts and another code owner's approval.
  • Jacopo and Lorenzo review and approve each other's pull requests. The author does not approve their own change under this policy.
  • A ruleset on main enforces repository protections in every repository: changes arrive only through a pull request, review threads are resolved, the merge is a squash, and main cannot be force-pushed or deleted. Nobody bypasses it, administrators included. CI moves to Cloud Build (decided 2026-11-04): until its checks report to GitHub, the ruleset requires none, and the author checks that CI passed before merging.
  • Pull requests are squash-merged; the pull request title becomes the line in the release notes.
  • Work that is not ready for customers is merged behind a feature flag, so main stays releasable.

Why there are no stage or prod branches

With a branch per environment, the code in prod is a merge of a merge, never exactly what was tested in stage, and the branches drift apart until merging them hurts. Promoting one build keeps what was tested and what runs identical.

7.3.2 From Merge to Prod

flowchart LR pr[Pull request<br/>checks pass + code-owner review] --> main[Merge into main] main --> build[Checks and build candidate] build --> release[Prepare release] release -- rollout approval --> stage[Stage] stage -- "successful rollout + required tests<br/>tag and future approval" --> prod[Prod]
Environment Receives When Who decides
Local development Work-in-progress code and synthetic fixtures Before proposing the change Developer
Stage A verified candidate from main After checks, publication, release preparation and rollout approval A member of stage-release-admins, for the intended stage target
Prod, future The tagged SHA and image digests that succeeded in stage and passed required tests After the production checks and approval A member of the existing release-admins group, a second person, never the author of the change

There is no cloud development deployment to pass first. A production release never skips stage: an approval or a started rollout is insufficient; the rollout must succeed and the required tests must pass for the same candidate. The automatic production evidence check remains a prerequisite to implement before production activation.

Releases are delivered by Cloud Deploy using environment targets. Promotion reuses the same images, identified by digest; environment configuration is rendered and verified separately. Stage and future prod require approval. Access currently implements the stage approver grant on its delivery pipeline with an IAM condition for the stage target. Do not infer environment separation from group names alone; Cloud Build approval remains project-wide. A native delivery handles one Cloud Run workload per target/render; multiple components require explicitly coordinated deliveries, not an assumed atomic release. See Application Delivery.

The initial approved source-free release build is initiated explicitly from a reviewed candidate. Generalizing it does not enable deployment on every GitHub push. Infrastructure changes follow the separate Terraform workflow.

The initial stage operator CLI accepts current remote main, not any historical merged commit. Its kit, manual same-component release ordering, retry and compatible fix/revert procedure are defined in Stage Release and Recovery. A rollout retry is not a rollback; the future production recovery policy below does not certify that either recovery path has already been enabled and tested in stage.

7.3.3 When Prod Breaks

  • First restore, then fix. Send traffic back to the previous version, as described in Service Recovery. This takes minutes and needs no new build.
  • The fix is a normal pull request to main. It may be promoted straight to stage, and it still passes stage like any other change. There are no hotfix branches.
  • Database migrations stay compatible with the previous release, so going back never needs a database rollback.