Engineering Practice · 上版約定與分支範本文件版號 v2026100501
SRE · Engineering Practice · 全公司適用

上版約定與分支範本

文件版號 v2026100501(討論與引用請註明版號)。英文版為正本(English · SSOT),本文為中文版,內容與同版號的英文版一致。給各開發部門,不分技術棧、不分部署在哪裡。文件分兩層:Part A 上版約定是各團隊交給 SRE 的東西,必須遵守;Part B 分支範本是 repo 內部版控的建議做法,可以採用、調整或不用。每一節都標明屬於 約定 或 範本。實作細節見 CI/CD 細節。

1. 一頁看懂

約定是介面,版控歸你們

SRE 只需要 Part A 的東西。repo 內部怎麼開分支、怎麼審核、怎麼打 tag,由各團隊決定;Part B 是範本,不是規定。

每個 repo 每次上版一位 Release Manager

不管有幾個團隊或部門參與,每個 repo 的每次上版只有一位 Release Manager,負責整合 MR 與上版細節,也是 SRE 唯一的窗口。

前一工作日交 tag + commit hash

每個 repo 只交給 SRE 一個 tag 與它的 commit hash,連同 release plan,在 release day 前一個工作日下班前交付。MR 在這之前全部合完。

能退版,或有開關

每次上版要能退回上一版,或改動可以用開關關掉。兩者都沒有,SRE 照上,出事由開發往前修。

flowchart LR
  T["各團隊的 MR
打 tag 前合完"] --> RM["Release Manager
每個 repo 一位"] RM -->|"前一工作日:tag + commit hash
+ release plan"| S["SRE"] S -->|"release day"| G["灰度
真實流量"] G -->|"任一 repo 有問題:
全部撤回 · 重新交付
再灰度,直到 14:00"| RM G -->|"整組通過
同一組 tag"| 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
圖 1 — 約定:Release Manager 在前一工作日交付;release day 由 SRE 把整組先上灰度,再上 production。
Part A · 對 SRE 的上版約定 — 必須遵守

2. 角色 約定

角色層級負責
Release Manager(開發)約定每個 repo 每次上版只有一位,不管有幾個團隊或部門的改動在裡面。整合所有團隊的 MR 與上版細節(上了什麼、怎麼退版、有哪些開關);與其他 repo 的 Release Manager 排定 release plan;前一工作日交付 tag + commit hash;提出 hotfix 與只改設定的上版。是這個 repo 這次上版對 SRE 的唯一窗口。
SRE約定核對 tag 與 commit hash;release day 把整組上灰度,再依 release plan 順序上 production;灰度有問題時撤回整組;退版;確認 hotfix 是否符合條件;訂凍結期。SRE 不在 app repo 裡合併、開分支或打 tag。
開發人員範本開發、開 MR;怎麼做由團隊決定(Part B)。
開發主管範本團隊內的功能審核與決定要上什麼。對 SRE 的上版職責屬於 Release Manager。
QC範本在測試環境驗收 rc 是選用、未來的步驟(第 9 節),目前不在流程中。

3. 上版約定 約定

  1. Release Manager每個 repo 的每次上版只有一位 Release Manager,不管有幾個團隊或部門的改動在裡面。Release Manager 整合這個 repo 所有的 MR 與上版細節,是 SRE 唯一的窗口;SRE 只接受 Release Manager 提出的上版申請。一次上版牽涉多個 repo 時,由這些 repo 的 Release Manager 排定一份 release plan(哪些 repo、順序、各自退回哪一版),一起交給 SRE。
  2. 期限tag + 完整 commit hash + release plan 要在 release day 前一個工作日下班前交給 SRE。超過就排到下一個 release day。
  3. 只上一次每個 repo、每次 release day,只有一個 tag。MR 在打 tag 之前全部合完;SRE 不等合併。
  4. Release day灰度(真實流量)有固定時段:release day 當天 11:00 開始、14:00 終止。時段內 SRE 可以灰度 n 次,每次都是整組。灰度上任一個 repo 有問題,SRE 把當天整個 release 從灰度撤回(release plan 上所有 repo)。所有 Release Manager 確認後,每個 repo 都重新交付 tag + commit hash(有改沒改都要),整組從頭重上灰度。14:00 灰度終止:當時還在進行的那一輪直接停止並撤回,整個 release 延到下一個 release day。只有在 14:00 前通過灰度的那一組才會繼續,SRE 再依 release plan 順序把同一組 tag、同一組 artifact 上 production(production 部署本身可以在 14:00 之後)。
  5. tag ↔ hash部署前 SRE 核對每個 tag 是否指向交付的 commit hash。對不上就不上,直到 Release Manager 確認更正後的組合。
  6. 可退版每次上版要嘛能退版(上一版可以直接重新部署;DB migration 向下相容),要嘛改動在開關後面可以關掉。Release Manager 在申請中寫明是哪一種。兩者都沒有,SRE 照上;出事時由開發往前修,這是開發的責任。
  7. 退版SRE 依 release plan 反向,對每個 repo 重新部署上一版,整個 release 一次全退;或是把開關關掉。退版後要不要當天再上(例如拿掉有問題的功能),由 Release Manager 決定;當天再上一樣要整組重新交付,並在 14:00 前通過灰度,否則排下一個 release day。
  8. Hotfix只限第 5 節的三種情況。Release Manager 附依據提出,SRE 確認。交付方式一樣(tag + hash),先灰度再 production,不必等 release day。
  9. 界線repo 內部的版控錯誤(tag 打錯 commit、分支從錯的起點開、合併出錯)由開發自己處理。SRE 部署的就是交付的東西。

4. Release day 約定

release day 多久一次由團隊決定(每週、每幾天、每天),時間軸都一樣:

時間做什麼誰
前一工作日之前MR 全部合完;依團隊自己的做法在 repo 打 tag(Part B 是其中一種做法)。開發
前一工作日下班前這次上版每個 repo 的 tag + 完整 commit hash + release plan 交付完成。Release Manager
Release day 11:00核對 tag 與 hash;灰度時段開始:整組上灰度(真實流量);觀察。SRE
灰度有問題(11:00–14:00)整個 release 從灰度撤回。Release Manager 確認後,重新交付每個 repo 的 tag + hash;整組重上灰度。可以 n 次。SRE、Release Manager
14:00灰度終止。還在進行的那一輪停止並撤回;沒通過灰度的 release 延到下一個 release day。SRE
整組通過灰度(14:00 前)同一組 tag 依 release plan 順序上 production(可以在 14:00 之後);production 驗證。SRE
production 退版之後由 Release Manager 決定當天要不要再上;要的話整組重新交付,並在 14:00 前通過灰度。Release Manager、SRE
之後repo 上版後怎麼處理分支與 tag(Part B:合進 master、打正式 tag)。開發
圖 2 — Release day。金色是期限與上 production 的關卡;虛線是平台自動動作。

5. Hotfix 約定

只有這三種修正可以不等 release day 上版,其他都等下一個 release day:

  1. 有產生客訴事件的系統修正。
  2. 有造成帳務或金流問題的系統修正。
  3. 針對系統效能優化的系統修正。

Release Manager 提出、SRE 確認:申請中寫明屬於哪一種,並附上依據(客訴單號、帳務異常紀錄、效能數據)。符合條件 SRE 才受理。確認後的 hotfix 跟一般上版一樣交付(tag + 完整 commit hash、退版方式),先上灰度再上 production。repo 內部怎麼開 hotfix 分支由團隊決定;第 9 節是其中一種做法。

6. 可退版與退版 約定

不能退版又沒有開關的上版,SRE 無法替你復原。所以每份申請都要寫明退版方式。

步驟動作時間說明
①關掉功能開關秒~分鐘改動在開關後面時優先用;不必退版(第 11 節)。
②依 release plan 反向,對每個 repo 重新部署上一版(SRE)分鐘整個 release 一次全退;用既有 artifact,不重 build。資料庫不退:migration 必須向下相容(先加欄位,下一版再刪)。
③往前修一次上版① ② 都做不到時。由開發負責;符合條件就走 hotfix,否則等下一個 release day。
  1. SRE與 Release Manager 確認需要退版,並宣告。
  2. SRE依 release plan 反向,對每個 repo 重新部署上一版(release plan 上寫的版本)。plan 上的每個 repo 都要退。
  3. Release Manager決定哪些要重新上、什麼時候上:當天(整組重新交付,14:00 前通過灰度),或之後的 release day。
不允許:不經 SRE 改 production、手動改 production 資料庫、同一次 release 只退部分 repo、部署交付的 tag + hash 以外的東西。

7. 上版申請長什麼樣 約定

Release Manager 每個 repo 每次上版開一份申請;SRE 核准,平台部署。灰度與 production 用同一份申請,長這樣:

deploy tms v1.4.0-rc.2 → gray → production(release day 2026-10-08)
release manager這次 tms 上版的唯一窗口
tagv1.4.0-rc.2
commit3f9c2e1d7a6b4c08e5f1a2b3c4d5e6f708192a3b
delivered2026-10-07(前一工作日)
release plantms → tms-3pl → api-gateway(本 repo 為第 1 個)
revert重新部署 v1.3.0;coupon 在開關 features.coupon 後面
type一般上版(hotfix 要註明條件與依據;只改設定要標示)
Approve
Part B · 分支範本 — 建議做法,不強制

8. 分支 範本

一種讓 repo 容易符合約定的做法。可以照用、調整,或用自己的方式。

分支存活時間用途誰能改
master永久在 production 驗證過的程式碼。只收驗證過的 release/* 與 hotfix/*。只能 MR,由 Release Manager 合併。
feat/*到它上版為止從 master 開,一個功能一條。feat/ 前綴是慣例;其餘照業界常見做法(短、小寫、- 分隔,有票號就帶上)。定期把 master 合進來。開發人員。
release/X.Y.Z到合進 master 為止前一工作日之前從 master 切出,每次上版一條。要上的 feat/* 合進來,在前一工作日期限前打 rc tag;production 驗證後合進 master 並刪除。只能 MR。
hotfix/*幾小時符合 hotfix 三種情況時使用。從 master 開。開發人員。
圖 3 — 分支線示意:release/1.4.0 在前一工作日之前切出,收進兩個團隊的功能;rc.1 在前一工作日交付,灰度沒過,整個 release 撤回;修正後的 rc.2 重新交付,在 14:00 前通過灰度並上 production;之後的 hotfix 從 master 開、合進 release,rc.3 經灰度上 production;所有 repo 驗證正常後,Release Manager 把 release 合進 master 並打 v1.4.0。
「這次上版包含什麼」可以用 GitLab Milestone 追蹤;「production 正在跑什麼」看 SRE 的部署紀錄。趕不上期限的功能留在它的 feat/*,等下一次上版。

9. 完整示範週期 範本

  1. 開發人員從 master 開 feat/*,在自驗環境開發與驗證。
  2. Release Manager前一工作日之前:與各團隊確認要上什麼,從 master 切 release/X.Y.Z,並與其他 repo 的 Release Manager 排定 release plan。
  3. 開發人員把要上的 feat/* 開 MR 進 release/X.Y.Z;功能審核後合併。
  4. Release Manager在 release/X.Y.Z 打 vX.Y.Z-rc.1(系統 build artifact),前一工作日交付 tag + commit hash。
  5. SRERelease day:灰度,再 production(Part A)。灰度沒過時,在原本的 feat/* 上修,再合進 release,打 rc.2 重新交付,必須在 14:00 前通過灰度。
  6. Release Managerrelease plan 上所有 repo 都在 production 驗證正常後,把 release/X.Y.Z 合進 master,打正式 tag vX.Y.Z(內容與 production 上的 rc 相同);刪除 release 與已合併的 feat 分支。

從 release 拿掉某個功能

某個功能不能上、其他功能照常上時:各團隊協調要在 release/X.Y.Z revert 哪些 merge(該功能與依賴它的東西),用 MR revert,打新的 rc 再交付。被拿掉的功能留在它的 feat/*,之後再上。有開關的功能維持關閉即可。

Hotfix 的分支做法

情況 A:有 release 已在 production、還沒合進 master

master → hotfix/* → MR 進 release/X.Y.Z → 新 rc → 交付 → 灰度 → production → 之後隨 release 合進 master。

情況 B:沒有待合併的 release

master → hotfix/* → 在 hotfix 分支打 rc → 交付 → 灰度 → production → 合進 master → 正式 tag vX.Y.(Z+1)。

flowchart LR
  M0["master"] --> H["hotfix/*"]
  H -->|"情況 A:MR"| R["release/X.Y.Z
(已上 prod、未合併)"] R -->|"新 rc · 交付"| G1["灰度"] --> P1[("production")] P1 -.->|"隨 release 合併"| M1["master · vX.Y.Z"] H -->|"情況 B:rc tag · 交付"| G2["灰度"] --> P2[("production")] P2 -->|"驗證 · RM 合併"| 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
圖 4 — 範本中的 hotfix 分支做法。不論哪種,SRE 收到的都是一個 tag + commit hash。

選用:QC 驗收 rc(未來)

想在交付前做功能驗收的團隊,可以把 rc 部署到自己的測試環境,在前一工作日之前由 QC 驗收。交付的就是 QC 驗過的 tag,production 跑的就是 QC 測過的版本。目前不在流程中。

10. 版號與 artifact 範本

tag打在哪用途
vX.Y.Z-rc.Nrelease/X.Y.Z(情況 B hotfix:hotfix/*)交付給 SRE 的 tag。每重新交付一次 N 加一。
vX.Y.Z合併後的 master正式版紀錄,內容與 production 上的 rc 相同;之後退版時的「上一版」。X 不相容變更、Y 新功能、Z 修正。
build 一次 約定

artifact 由交付的 tag build,以 commit 識別。灰度與 production 跑同一個 artifact;環境設定在部署時注入,不烙進 artifact。

tag 不可變

發出去的 tag 不移動、不刪除。tag 打錯就打新的 tag 並更正交付,不移動舊的。

11. 功能開關 範本

功能開關是執行期讀取的設定,決定新功能的路徑要不要走。它是符合第 6 節「可退版」的兩種方式之一。一個功能因此分兩次上:

flowchart LR
  A["① 程式上線
開關預設關閉
一般上版"] --> B["② 打開功能
只改設定,artifact 不變
RM 提只改設定的申請"] B --> C["③ 移除開關
一般上版"] B -.->|"有問題"| D["關回去"] classDef s fill:#111a2c,stroke:#3a4763,color:#d7deed classDef g fill:#241d0f,stroke:#e8c56a,color:#f0d9a0 class A,C,D s class B g
圖 5 — 開關把「發布功能」變成只改設定的上版。

12. 常見問題

一定要用分支範本嗎?

不用。Part A 是約定;Part B 是符合約定的一種做法。SRE 看的是交付:一位 Release Manager、每個 repo 前一工作日交一個 tag + commit hash、寫明退版方式。

Release Manager 和開發主管有什麼不同?

Release Manager 是這個 repo 這次上版對 SRE 的唯一窗口:整合所有團隊的 MR 與上版細節,交付 tag + hash。開發主管仍在自己團隊內決定什麼準備好了。同一個人可以兩者都是。

好幾個部門都改同一個 repo,Release Manager 是誰?

這個 repo 這次上版仍然只有一位,由各部門協調出人選。同一個 repo,SRE 不接受各部門分開的申請。

為什麼是前一工作日?為什麼灰度是 11:00 到 14:00?

前一工作日讓 SRE 有時間核對 tag 與 hash、準備 release plan;release day 當天 SRE 只做部署。11:00–14:00 的時段可以跑好幾輪灰度,下午還留有時間上 production 並在上線後觀察。

上線後退版,當天還能再上嗎?

由 Release Manager 決定。例如拿掉有問題的 feat,其餘重新上:整組重新交付,必須在 14:00 前通過灰度。過了 14:00 就排下一個 release day。

只有一個 repo 灰度沒過,為什麼要全部撤回?

同一次上版的 repo 透過 release plan 互相依賴。整組撤回、整組重走,灰度與 production 才會對應同一份 plan,而不是混著上了一半。

我們的 tag 打錯 commit,別人也從那裡開了分支,SRE 能處理嗎?

不能。repo 內部的版控屬於開發。打一個正確的新 tag 並交付;SRE 部署交付的那組 tag + hash。

我的修正不屬於三種 hotfix,但很急怎麼辦?

趕最近的 release day。真的等不了,先看能不能用功能開關把問題關掉;不符合三種條件的 hotfix SRE 不受理。

可以 cherry-pick 到別的分支嗎?

不建議。一個功能通常有好幾個 commit 加上後續修正,很容易漏挑;cherry-pick 也會產生重複的 commit,之後合併時衝突。分支之間一律用 MR 合併整條分支;要拿掉功能用 revert。

MR 可以用 squash 嗎?

MRsquash原因
feat/*、hotfix/* → release/X.Y.Z可以,選用一個功能在 release 上就是一個 commit,要拿掉只要 revert 一次。
release/*、hotfix/* → master不建議squash 會產生新的 commit,production 上的 rc 不再是 master 的祖先,正式 tag 就無法跟它比對。
開 MR 前整理自己的 feat commit可以只要沒有別人共用這條分支。

Next.js 的 NEXT_PUBLIC_* 怎麼做到 build 一次?

build 時 CI 沒有任何環境的設定,所以 image 裡不能有因環境而異的值。server side 在執行期讀環境變數;瀏覽器端的值用同源相對路徑,或由 server 在執行期提供。見 CI/CD 細節。

13. 依據

每條規則都對應業界主流做法;討論的是這個做法適不適合我們,而不是誰的方法比較好。