Engineering Practice · Release Contract & Branching TemplateDocument version v2026100501
SRE · Engineering Practice · Company-wide

Release Contract & Branching Template

Document version v2026100501 (quote this version when discussing or citing) · 中文版. For every development department, whatever the stack and wherever it is deployed. The document has two layers: Part A, the delivery contract, is what every team hands to SRE and is mandatory; Part B, the branching template, is a recommended way to run version control inside a repo, which teams may adopt, adapt or ignore. Each section is marked contract or template. Implementation details are in CI/CD Details.

1. At a glance

The contract is the interface; version control is yours

SRE only needs what is in Part A. How a team branches, reviews and tags inside its repo is the team's choice; Part B is a template, not a rule.

One Release Manager per repo per release

However many teams or departments contribute, each repo's release has exactly one Release Manager, who integrates the MRs and release details and is SRE's only contact.

One tag + commit hash, the working day before

SRE receives exactly one tag and its commit hash per repo, with the release plan, by the end of the working day before release day. All MRs are merged before that.

Revertible, or behind a toggle

Every release can be rolled back to the previous version, or its change can be switched off. If neither holds, SRE still deploys and the development team fixes forward.

flowchart LR
  T["Teams' MRs
merged before tagging"] --> RM["Release Manager
one per repo"] RM -->|"D-1: tag + commit hash
+ release plan"| S["SRE"] S -->|"release day"| G["gray
real traffic"] G -->|"problem in any repo:
withdraw all · re-deliver
gray again until 14:00"| RM G -->|"whole set passed
same tags"| P[("production")] classDef long fill:#111a2c,stroke:#3fd0e8,color:#f0f4fc,stroke-width:2px classDef short fill:#111a2c,stroke:#3a4763,color:#d7deed classDef gate fill:#241d0f,stroke:#e8c56a,color:#f0d9a0 class RM,S long class T short class G gate
Figure 1 — The contract: the Release Manager delivers on D-1; on release day SRE takes the whole set through gray, then production.
Part A · Delivery contract with SRE — mandatory

2. Roles contract

RoleLayerResponsible for
Release Manager (Dev)contractExactly one per repo per release, however many teams or departments have changes in it. Integrates every team's MRs and the release details (what goes in, how it is reverted, which toggles exist); agrees the release plan with the other repos' Release Managers; delivers tag + commit hash on D-1; requests hotfixes and config-only deploys. SRE's only contact for that repo's release.
SREcontractChecks tag ↔ commit hash; on release day deploys the whole set to gray, then to production in release-plan order; withdraws the release from gray when a problem shows; rolls back; confirms hotfix eligibility; sets freeze periods. SRE does not merge, branch or tag in app repos.
DevelopertemplateDevelops and opens MRs; how is up to the team (Part B).
Dev LeadtemplateFeature review and what goes into the release, inside the team. Release duties towards SRE belong to the Release Manager.
QCtemplateAcceptance testing of an rc in a test environment is an optional, future step (section 9); it is not part of today's flow.

3. The delivery contract contract

  1. Release ManagerEach release of a repo has exactly one Release Manager, regardless of how many teams or departments have changes in it. The Release Manager integrates all MRs and release details for that repo and is SRE's only contact; SRE takes deployment requests only from them. When a release spans several repos, their Release Managers agree one release plan (repos, order, version to revert to) and hand it to SRE together.
  2. DeadlineTag + full commit hash + release plan reach SRE by the end of the working day before release day. Anything later moves to the next release day.
  3. One deploymentOne repo, one release day, one tag. All MRs are merged before the tag is created; SRE never waits for merges.
  4. Release dayGray (real traffic) runs in a fixed window: from 11:00 to 14:00 on release day. Within the window SRE may run gray any number of times; each round takes the whole set. If gray shows a problem in any repo, SRE withdraws the whole day's release from gray (every repo in the release plan). After all Release Managers have confirmed, they deliver tag + commit hash again for every repo, changed or not, and the whole set goes through gray again from the start. Gray ends at 14:00: a round still running at 14:00 is stopped and withdrawn, and the whole release moves to the next release day. Only a set that has passed gray by 14:00 goes on; SRE then deploys the same tags and the same artifacts to production in release-plan order (the production deploy itself may run after 14:00).
  5. Tag ↔ hashBefore deploying, SRE checks that each tag resolves to the delivered commit hash. A mismatch is not deployed until the Release Manager confirms a corrected pair.
  6. RevertibilityEvery release is either revertible (the previous version can be redeployed; DB migrations are backward compatible) or its change sits behind a toggle that can be switched off. The Release Manager states which in the request. If neither holds, SRE still deploys; on a problem, the development team fixes forward. That is the development team's responsibility.
  7. RollbackSRE rolls back by redeploying each repo's previous version in reverse release-plan order, the whole release at once, or by switching the toggle off. Whether to release again the same day (for example with the problem feature taken out) is the Release Managers' decision; a same-day re-release is re-delivered as a whole set and must pass gray by 14:00, otherwise it goes to the next release day.
  8. HotfixOnly the three criteria in section 5. The Release Manager requests it with evidence; SRE confirms. Delivered the same way (tag + hash), gray then production, outside release day.
  9. BoundaryVersion-control mistakes inside a repo (a tag on the wrong commit, branches started from the wrong point, merge mistakes) are the development team's to fix. SRE deploys what was delivered.

4. Release day contract

How often release day happens is up to the teams (weekly, every few days, daily). The timeline is always the same:

WhenWhatWho
Before D-1All MRs merged; tag created in the repo however the team works (Part B shows one way).Dev
D-1, end of the working dayTag + full commit hash + release plan delivered for every repo in the release.Release Managers
Release day, 11:00Tag ↔ hash check; gray window opens: the whole set to gray (real traffic); observation.SRE
Gray shows a problem (11:00–14:00)The whole release is withdrawn from gray. Release Managers confirm and re-deliver every repo's tag + hash; the whole set goes to gray again. Any number of rounds.SRE, Release Managers
14:00Gray ends. A round still running is stopped and withdrawn; a release that has not passed gray moves to the next release day.SRE
Whole set passed gray (by 14:00)The same tags to production, in release-plan order (may run after 14:00); production verified.SRE
After a production rollbackThe Release Managers decide whether to release again the same day; if so, the whole set is re-delivered and must pass gray by 14:00.Release Managers, SRE
AfterwardsWhatever the repo does with its branches and tags after a release (Part B: merge into master, release tag).Dev
Figure 2 — Release day. Gold marks the deadlines and the production gate. Dashed lines are automatic platform actions.

5. Hotfix contract

Only these three kinds of fix may be deployed outside release day; everything else waits for the next release day:

  1. Fixes for incidents that produced customer complaints.
  2. Fixes for problems affecting accounting or payment flows.
  3. Fixes that improve system performance.

Requested by the Release Manager, confirmed by SRE: the request states which kind it is and attaches the evidence (complaint ticket number, accounting anomaly record, performance data). SRE accepts it only if it qualifies. A confirmed hotfix is delivered like any release (tag + full commit hash, revert method) and goes to gray, then production. How the fix is branched inside the repo is up to the team; section 9 shows one way.

6. Revertibility and rollback contract

A release that cannot be reverted and has no toggle cannot be undone by SRE. That is why every request states its revert method.

StepActionTimeNotes
①Switch off the feature toggleSeconds to minutesWhen the change is behind a toggle; no rollback needed (section 11).
②In reverse release-plan order, redeploy each repo's previous version (SRE)MinutesThe whole release at once; existing artifacts, no rebuild. The database is not rolled back: migrations must be backward compatible (add columns first, drop them in a later release).
③Fix forwardOne releaseWhen neither ① nor ② is possible. The development team's responsibility; the fix is delivered as a hotfix if it qualifies, otherwise on the next release day.
  1. SREDecides with the Release Managers that a rollback is needed and announces it.
  2. SREIn reverse release-plan order, redeploys each repo's previous version (as stated in the release plan). Every repo in the plan is rolled back.
  3. Release ManagersDecide what ships again and when: the same day (re-delivered as a whole set, passing gray by 14:00) or a later release day.
Not allowed: changing production without going through SRE, editing the production database by hand, rolling back only some of the repos in a release, deploying anything other than the delivered tag + hash.

7. The deployment request contract

The Release Manager opens one request per repo per release; SRE approves it and the platform deploys. Gray and production use the same request. It looks like this:

deploy tms v1.4.0-rc.2 → gray → production (release day 2026-10-08)
release managerone person for tms in this release
tagv1.4.0-rc.2
commit3f9c2e1d7a6b4c08e5f1a2b3c4d5e6f708192a3b
delivered2026-10-07 (D-1)
release plantms → tms-3pl → api-gateway (this is #1)
revertredeploy v1.3.0; coupon behind toggle features.coupon
typenormal release (a hotfix states the criterion and the evidence; config-only is marked)
Approve
Part B · Branching template — recommended, not mandatory

8. Branches template

One way to run a repo that makes the contract easy to meet. Teams can use it as is, adapt it, or use their own.

BranchLifetimePurposeWho changes it
masterPermanentCode verified in production. Only verified release/* and hotfix/* branches come in.MR only, merged by the Release Manager.
feat/*Until its releaseBranched from master, one per feature. The feat/ prefix is the convention; the rest follows common practice (short, lowercase, --separated, ticket number when there is one). Merge master in regularly.Developers.
release/X.Y.ZUntil merged into masterCut from master before D-1, one per release. The shipping feat/* branches are merged in and the rc tag is created before the D-1 deadline; after production is verified it is merged into master and deleted.MR only.
hotfix/*HoursFor the three hotfix criteria. Cut from master.Developers.
Figure 3 — Branch lines: release/1.4.0 is cut before D-1 and takes in two teams' features; rc.1 is delivered on D-1 and fails in gray, so the whole release is withdrawn; the fix becomes rc.2, re-delivered and passing gray before 14:00, and goes to production; a later hotfix is cut from master, merged into the release, and rc.3 goes through gray to production; once every repo is verified, the Release Manager merges the release into master and creates v1.4.0.
"What is in this release" can be tracked with GitLab Milestones; "what is running in production" is in SRE's deployment records. A feature that misses the deadline stays on its feat/* branch for the next release.

9. A worked cycle template

  1. DeveloperBranches feat/* from master, develops and verifies in a self-verification environment.
  2. Release ManagerBefore D-1: agrees with the teams what ships, cuts release/X.Y.Z from master, and agrees the release plan with the other repos' Release Managers.
  3. DeveloperOpens an MR from each shipping feat/* into release/X.Y.Z; it is merged after feature review.
  4. Release ManagerCreates vX.Y.Z-rc.1 on release/X.Y.Z (the system builds the artifact) and delivers tag + commit hash on D-1.
  5. SRERelease day: gray, then production (Part A). If gray fails, the fix is made on the original feat/*, merged into the release again, rc.2 is created, re-delivered and must pass gray by 14:00.
  6. Release ManagerOnce every repo in the release plan is verified in production, merges release/X.Y.Z into master and creates the release tag vX.Y.Z (same content as the rc in production); the release and merged feat branches are deleted.

Removing a feature from the release

When one feature cannot ship but the others should: the teams agree which merges to revert on release/X.Y.Z (the feature and anything depending on it), revert them via MR, create a new rc and deliver it. The reverted feature stays on its feat/* branch for a later release. A feature behind a toggle can simply stay off.

Hotfix branching

Case A: a release is in production, not yet merged into master

master → hotfix/* → MR into release/X.Y.Z → new rc → delivered → gray → production → later merged into master with the release.

Case B: no release waiting to be merged

master → hotfix/* → rc tag on the hotfix branch → delivered → gray → production → merged into master → release tag vX.Y.(Z+1).

flowchart LR
  M0["master"] --> H["hotfix/*"]
  H -->|"Case A: MR"| R["release/X.Y.Z
(in prod, not merged)"] R -->|"new rc · delivered"| G1["gray"] --> P1[("production")] P1 -.->|"merged with the release"| M1["master · vX.Y.Z"] H -->|"Case B: rc tag · delivered"| G2["gray"] --> P2[("production")] P2 -->|"verified · RM merges"| M2["master · vX.Y.(Z+1)"] classDef long fill:#111a2c,stroke:#3fd0e8,color:#f0f4fc,stroke-width:2px classDef short fill:#111a2c,stroke:#3a4763,color:#d7deed classDef gate fill:#241d0f,stroke:#e8c56a,color:#f0d9a0 class M0,M1,M2 long class H,R short class G1,G2 gate
Figure 4 — Hotfix branching in the template. Either way, what SRE receives is one tag + commit hash.

Optional: QC acceptance on an rc (future)

A team that wants functional acceptance before delivery can deploy the rc to its own test environment and have QC accept it before D-1. The tag QC accepted is then the one delivered, so production runs what QC tested. This is not part of today's flow.

10. Versions and artifacts template

TagWherePurpose
vX.Y.Z-rc.Nrelease/X.Y.Z (Case B hotfix: hotfix/*)The tag delivered to SRE. N increases with each re-delivery.
vX.Y.Zmaster, after the mergeRelease record, same content as the rc in production; "the previous version" for later rollbacks. X breaking change, Y new feature, Z fix.
Build once contract

The artifact is built from the delivered tag and identified by its commit. Gray and production run the same artifact; environment settings are injected at deploy time, never baked in.

Tags are immutable

A published tag is never moved or deleted. A wrong tag is fixed with a new tag and a corrected delivery, not by moving the old one.

11. Feature toggles template

A feature toggle is a setting read at runtime that decides whether a new feature's path is used. It is one of the two ways to meet the revertibility rule in section 6. One feature then ships in two deployments:

flowchart LR
  A["① Ship the code
toggle off by default
normal release"] --> B["② Turn the feature on
setting change only, same artifact
config-only request by the RM"] B --> C["③ Remove the toggle
normal release"] B -.->|"problem"| D["Turn it off again"] classDef s fill:#111a2c,stroke:#3a4763,color:#d7deed classDef g fill:#241d0f,stroke:#e8c56a,color:#f0d9a0 class A,C,D s class B g
Figure 5 — A toggle turns "releasing a feature" into a deployment that only changes a setting.

12. FAQ

Do we have to use the branching template?

No. Part A is the contract; Part B is one way to meet it. What SRE checks is the delivery: one Release Manager, one tag + commit hash per repo by D-1, a stated revert method.

What is the difference between the Release Manager and a Dev Lead?

The Release Manager is the repo's single contact towards SRE for one release: integrates every team's MRs and release details and delivers the tag + hash. Dev Leads still decide inside their teams what is ready. One person can be both.

Several departments change the same repo. Who is the Release Manager?

Still exactly one person for that repo's release; the departments agree who. SRE does not take separate requests per department for the same repo.

Why D-1, and why gray from 11:00 to 14:00?

D-1 gives SRE time to check tag ↔ hash and prepare the release plan; on release day SRE only deploys. The 11:00–14:00 window gives room for several gray rounds and still leaves the afternoon for production and for watching it after release.

Production had to be rolled back. Can we release again the same day?

That is the Release Managers' decision. For example, the problem feat is taken out and the rest released again; the whole set is re-delivered and must pass gray by 14:00. After 14:00, it goes to the next release day.

Only one repo failed in gray. Why withdraw everything?

Repos in one release depend on each other through the release plan. Withdrawing and re-running the whole set keeps gray and production consistent with one plan instead of a partial mix.

Our tag pointed at the wrong commit and others branched from it. Can SRE fix it?

No. Version control inside the repo belongs to the development team. Create a correct new tag and deliver it; SRE deploys the pair that was delivered.

My fix is not one of the three hotfix kinds, but it is urgent. What now?

Take the next release day. If it cannot wait, check whether a feature toggle can switch off the problem; SRE does not accept a hotfix that does not meet the three criteria.

Can I cherry-pick commits to another branch?

Not recommended. A feature usually has several commits plus later fixes, so it is easy to miss one, and cherry-picks create duplicate commits that later conflict. Merge whole branches through MRs; remove a feature with revert.

Can an MR use squash?

MRSquashWhy
feat/*, hotfix/* → release/X.Y.ZAllowed, optionalOne feature becomes one commit on the release, so removing it is a single revert.
release/*, hotfix/* → masterNot recommendedSquash creates a new commit, so the rc in production is no longer an ancestor of master and the release tag cannot be checked against it.
Tidying your own feat commits before the MRAllowedAs long as nobody else shares the branch.

How does "build once" work with Next.js NEXT_PUBLIC_*?

At build time CI has no environment settings, so the image cannot contain values that differ per environment. Server-side code reads environment variables at runtime; browser-side values use same-origin relative paths or are provided by the server at runtime. See CI/CD Details.

13. References

Every rule maps to a mainstream industry practice; a discussion is about whether a practice fits us, not whose approach is better.