Branching Strategy

Updated: 2026-10-06 05:32 ET · Audited: 2026-10-05

TL;DR:

The flow

StepBranchEntryWhat happens on merge
1devPR from a work branchDeveloper integration. No automated gate.
2qa/X.Y.ZPR dev → qa/X.Y.ZQA test build. The PR validation builds must pass first. No store upload.
3stg/X.Y.ZPR qa/X.Y.Z → stg/X.Y.Z, opened and merged by the release owner after reviewing the stackAutomated tester build for the stores, one build per bundle.
4release/X.Y.ZCut at the stg tipThis is what the stores review. Tagged vX.Y.Z at the go decision.
5mainrelease/X.Y.Z once the store release is live and the build is GAProduction signal. After each GA, main is merged back into dev.

Promotion into stg is bundled

A merge into stg/X.Y.Z produces a tester build for the stores, so merged work stacks ahead of stg until a manual review: the release owner reviews the stack, opens the promotion PR into stg and merges it, so one tester build carries many changes. Exceptions that go alone: a hotfix, or when there is nothing else to stack. Nothing promotes automatically. A promotion PR carries the whole tip of the branch it merges from; a branch cannot be promoted PR by PR without cherry-picking.

Branch names and prefixes

PrefixUseCut from, PR into
feat/, fix/, docs/, chore/, refactor/, test/Shared work by type, named type/AB####-short-namedev to dev. A late fix is cut from the branch where it was found.
task/<wi-id>-Work-item-scoped tasksdev to dev
users/<alias>/<topic>Developer playground. Nobody else depends on it; anything that ships goes through a work branch.never promoted
imp/<topic>Long-running topic integration that needs several PRs before devdev to dev, never straight to qa
qa/X.Y.Z, stg/X.Y.Z, release/X.Y.ZThe release line for one versionThe PR chain above
archive/<name>-<date>Tags only, for retired branchesnever a working branch

Where each check runs

BranchRequired before mergeAfter merge
devNothing automated. Before the PR you have run the unit and architecture tests (the pre-commit hook runs them when code is staged) and built your platform.The developer who merged builds the merged dev tip (not their own branch, because PRs that are each green can break the build together) for the platform(s) the change touches. If it is red, fix forward or revert the same working day. The last merger owns a red dev.
qa/X.Y.ZBlocking builds: PR Validation Android (98), iOS (99) and Docs (100). No reviewer policy: the builds are the gate.QA testing.
stg/X.Y.ZPR from qa/X.Y.Z and 1 reviewer (the creator's vote counts).Automated tester build and store upload.
release/X.Y.Z1 reviewer (the creator's vote counts).What the stores review.
main1 reviewer (the creator's vote counts). A PR that touches docs/ or .githooks/ also runs PR Validation Docs (100), which must pass.The production signal. Docs Publish deploys the docs site from main.

Every PR into dev, qa/, stg/, release/ and main completes as a merge commit (no squash, no rebase), enforced by branch policy. A squash or rebase would give a promoted fix a different commit on each branch and the back-merge rule would stop working. A reviewer policy whose creator's vote counts is a deliberate pause, not an access control.

Docs-only fixes

The docs site is published from main, so a docs fix normally reaches it with the next release. A docs-only fix that cannot wait goes in a docs/ work branch and is PR'd straight into main (merge commit). The docs PR validation (100) and one reviewer gate it. Afterwards merge main back into dev, as after a release.

Late fixes: the back-merge rule

  1. A defect found on qa, stg or release is fixed on a branch cut from that branch and merged by PR into that branch, so the code under test is the code fixed.
  2. The person who merged the fix owns the back-merge. The same working day they open PRs from the branch the fix landed on into every lower branch at once, not as a chain: a fix on release/X.Y.Z gets PRs into stg/, qa/ and dev (skip branches that already contain the commit). Each completes as a merge commit; never cherry-pick. Tag the work item BackMerge.
  3. A release is not cut, and release/X.Y.Z is not merged to main, while a back-merge PR is open. Check: git log --no-merges --oneline origin/dev..origin/release/X.Y.Z must print nothing. Promotion merge commits exist only on the higher branch, so --no-merges is required.
  4. After release/X.Y.Z is merged to main, main is merged back into dev so the tag and any hotfix are reachable from dev.
  5. A post-GA hotfix branches from the tag vX.Y.Z, goes by PR into release/X.Y.Z and then stg/ (the store build is its check), is merged to main, and is back-merged into qa and dev.

Version numbers

  1. After release/X.Y.Z is merged into main (and main into dev), a PR into dev sets Version and ApplicationDisplayVersion in ALPAMobile/ALPAMobile.csproj to the next version. The two lines must stay aligned.
  2. The bump reaches qa/X.Y.Z+1 and stg/X.Y.Z+1 through the normal promotion. Those branches can exist before the bump.
  3. Until the bump has reached a branch, do not upload from it: it still carries the released version, and a store build under a shipped version is rejected or misfiled. The store-upload steps in azdo_windows.yml and azdo_mac.yml run on every branch except dev and main: automatically on a merge into stg/* (the CI trigger), and on a manual run from any other branch, for example a topic or release branch that needs a tester build (AB#3156). A manual run from dev or main builds without uploading.
  4. Anything that matches tester builds by version, such as a triage version prefix, changes with the bump.

Release tags

Emergency path

A branch policy may be bypassed only for an emergency: a production-impacting defect (crash, data loss, sign-in) that must reach the stores quickly, or a gate outage where builds cannot run on the hosted agents and the delay would block a release. A deadline is not an emergency. dev has nothing to bypass: a red dev is fixed forward or reverted.