文件版號 v2026100501(討論與引用請註明版號)。英文版為正本(English · SSOT),本文為中文版,內容與同版號的英文版一致。給各開發部門,不分技術棧、不分部署在哪裡。文件分兩層:Part A 上版約定是各團隊交給 SRE 的東西,必須遵守;Part B 分支範本是 repo 內部版控的建議做法,可以採用、調整或不用。每一節都標明屬於 約定 或 範本。實作細節見 CI/CD 細節。
SRE 只需要 Part A 的東西。repo 內部怎麼開分支、怎麼審核、怎麼打 tag,由各團隊決定;Part B 是範本,不是規定。
不管有幾個團隊或部門參與,每個 repo 的每次上版只有一位 Release Manager,負責整合 MR 與上版細節,也是 SRE 唯一的窗口。
每個 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
| 角色 | 層級 | 負責 |
|---|---|---|
| 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 節),目前不在流程中。 |
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)。 | 開發 |
只有這三種修正可以不等 release day 上版,其他都等下一個 release day:
Release Manager 提出、SRE 確認:申請中寫明屬於哪一種,並附上依據(客訴單號、帳務異常紀錄、效能數據)。符合條件 SRE 才受理。確認後的 hotfix 跟一般上版一樣交付(tag + 完整 commit hash、退版方式),先上灰度再上 production。repo 內部怎麼開 hotfix 分支由團隊決定;第 9 節是其中一種做法。
不能退版又沒有開關的上版,SRE 無法替你復原。所以每份申請都要寫明退版方式。
| 步驟 | 動作 | 時間 | 說明 |
|---|---|---|---|
| ① | 關掉功能開關 | 秒~分鐘 | 改動在開關後面時優先用;不必退版(第 11 節)。 |
| ② | 依 release plan 反向,對每個 repo 重新部署上一版(SRE) | 分鐘 | 整個 release 一次全退;用既有 artifact,不重 build。資料庫不退:migration 必須向下相容(先加欄位,下一版再刪)。 |
| ③ | 往前修 | 一次上版 | ① ② 都做不到時。由開發負責;符合條件就走 hotfix,否則等下一個 release day。 |
Release Manager 每個 repo 每次上版開一份申請;SRE 核准,平台部署。灰度與 production 用同一份申請,長這樣:
v1.4.0-rc.23f9c2e1d7a6b4c08e5f1a2b3c4d5e6f708192a3bv1.3.0;coupon 在開關 features.coupon 後面一種讓 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 開。 | 開發人員。 |
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。feat/*,等下一次上版。master 開 feat/*,在自驗環境開發與驗證。master 切 release/X.Y.Z,並與其他 repo 的 Release Manager 排定 release plan。feat/* 開 MR 進 release/X.Y.Z;功能審核後合併。release/X.Y.Z 打 vX.Y.Z-rc.1(系統 build artifact),前一工作日交付 tag + commit hash。feat/* 上修,再合進 release,打 rc.2 重新交付,必須在 14:00 前通過灰度。release/X.Y.Z 合進 master,打正式 tag vX.Y.Z(內容與 production 上的 rc 相同);刪除 release 與已合併的 feat 分支。某個功能不能上、其他功能照常上時:各團隊協調要在 release/X.Y.Z revert 哪些 merge(該功能與依賴它的東西),用 MR revert,打新的 rc 再交付。被拿掉的功能留在它的 feat/*,之後再上。有開關的功能維持關閉即可。
master → hotfix/* → MR 進 release/X.Y.Z → 新 rc → 交付 → 灰度 → production → 之後隨 release 合進 master。
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
想在交付前做功能驗收的團隊,可以把 rc 部署到自己的測試環境,在前一工作日之前由 QC 驗收。交付的就是 QC 驗過的 tag,production 跑的就是 QC 測過的版本。目前不在流程中。
| tag | 打在哪 | 用途 |
|---|---|---|
vX.Y.Z-rc.N | release/X.Y.Z(情況 B hotfix:hotfix/*) | 交付給 SRE 的 tag。每重新交付一次 N 加一。 |
vX.Y.Z | 合併後的 master | 正式版紀錄,內容與 production 上的 rc 相同;之後退版時的「上一版」。X 不相容變更、Y 新功能、Z 修正。 |
artifact 由交付的 tag build,以 commit 識別。灰度與 production 跑同一個 artifact;環境設定在部署時注入,不烙進 artifact。
發出去的 tag 不移動、不刪除。tag 打錯就打新的 tag 並更正交付,不移動舊的。
功能開關是執行期讀取的設定,決定新功能的路徑要不要走。它是符合第 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
不用。Part A 是約定;Part B 是符合約定的一種做法。SRE 看的是交付:一位 Release Manager、每個 repo 前一工作日交一個 tag + commit hash、寫明退版方式。
Release Manager 是這個 repo 這次上版對 SRE 的唯一窗口:整合所有團隊的 MR 與上版細節,交付 tag + hash。開發主管仍在自己團隊內決定什麼準備好了。同一個人可以兩者都是。
這個 repo 這次上版仍然只有一位,由各部門協調出人選。同一個 repo,SRE 不接受各部門分開的申請。
前一工作日讓 SRE 有時間核對 tag 與 hash、準備 release plan;release day 當天 SRE 只做部署。11:00–14:00 的時段可以跑好幾輪灰度,下午還留有時間上 production 並在上線後觀察。
由 Release Manager 決定。例如拿掉有問題的 feat,其餘重新上:整組重新交付,必須在 14:00 前通過灰度。過了 14:00 就排下一個 release day。
同一次上版的 repo 透過 release plan 互相依賴。整組撤回、整組重走,灰度與 production 才會對應同一份 plan,而不是混著上了一半。
不能。repo 內部的版控屬於開發。打一個正確的新 tag 並交付;SRE 部署交付的那組 tag + hash。
趕最近的 release day。真的等不了,先看能不能用功能開關把問題關掉;不符合三種條件的 hotfix SRE 不受理。
不建議。一個功能通常有好幾個 commit 加上後續修正,很容易漏挑;cherry-pick 也會產生重複的 commit,之後合併時衝突。分支之間一律用 MR 合併整條分支;要拿掉功能用 revert。
| MR | squash | 原因 |
|---|---|---|
feat/*、hotfix/* → release/X.Y.Z | 可以,選用 | 一個功能在 release 上就是一個 commit,要拿掉只要 revert 一次。 |
release/*、hotfix/* → master | 不建議 | squash 會產生新的 commit,production 上的 rc 不再是 master 的祖先,正式 tag 就無法跟它比對。 |
| 開 MR 前整理自己的 feat commit | 可以 | 只要沒有別人共用這條分支。 |
NEXT_PUBLIC_* 怎麼做到 build 一次?build 時 CI 沒有任何環境的設定,所以 image 裡不能有因環境而異的值。server side 在執行期讀環境變數;瀏覽器端的值用同源相對路徑,或由 server 在執行期提供。見 CI/CD 細節。
每條規則都對應業界主流做法;討論的是這個做法適不適合我們,而不是誰的方法比較好。
-rc.N)