版本管理策略 — Versioning、Release、SNAPSHOT 與 CI/CD 分支規範
問題背景
最近負責一個 POC 的開發,是個前後端分離、又外部團隊、multirepo 的專案,起初直接開始規劃與開發,但開發到一個階段,覺的版本號、分支管理等等的好像很亂,所以想特別研究一下
這篇筆記的目標:從 Team Lead / SD 的視角,建立一套可落地的版本管理思維與規範。
一、語義化版本(Semantic Versioning)
標準格式: MAJOR.MINOR.PATCH,例如 2.4.1
| 部分 | 何時遞增 | 例子 |
|---|---|---|
MAJOR |
有不相容的 breaking change | API 改介面、資料結構不向下相容 |
MINOR |
加入新功能,向下相容 | 新增一個 endpoint、新增 feature |
PATCH |
Bug fix,向下相容 | 修正某個邏輯錯誤、安全性修補 |
延伸格式(Pre-release / Build Metadata):
1.0.0-alpha # 早期開發,API 可能大幅變動
1.0.0-beta.1 # 功能完整,還在測試
1.0.0-rc.1 # Release Candidate,準備正式發布
1.0.0 # 正式版
1.0.0+build.20260608 # 加上 build metadata(不影響版本比較)重要規則:
0.y.z版本代表初期開發,任何東西都可能隨時改變- 一旦發布
1.0.0,就進入穩定 API 承諾期 - 版本號一旦發布就不能修改(immutable),有問題要發新版本
二、SNAPSHOT vs RELEASE
這是 Java / Maven 生態的術語,但概念適用於所有語言。
SNAPSHOT
1.2.0-SNAPSHOT- 代表開發中、尚未穩定的版本
- 每次 build 都可能覆蓋同一個 artifact(內容會變動)
- 適合在 CI 環境、開發環境、功能測試中使用
- 不應該部署到 staging 或 production
RELEASE
1.2.0- 代表正式發布、不可變的版本
- 同一個版本號只會有一個對應的 artifact(build once, deploy many)
- 一旦發布到 artifact repository(如 Nexus / JFrog / GitHub Packages),就不能覆蓋
- 部署到 staging、production 的版本必須是 RELEASE
版本生命週期
feature/xxx → develop → 1.2.0-SNAPSHOT(CI build)
develop → release/1.2.0 → 測試通過 → 移除 SNAPSHOT → 1.2.0(RELEASE tag)
release/1.2.0 → main → 部署 production一句話記憶:
develop= SNAPSHOT(開發中、會變)release branch / main / production= RELEASE(穩定、不可變)
三、分支策略
3.1 Git Flow(適合有明確 release 節奏的產品)
main ──────●──────────────────────●──── (production)
↑ ↑
release/1.2.0 ────┤───fix───────────tag──┤
↑
develop ─────●──────●──────●──────────── (integration)
↑ ↑ ↑
feature/A ──────┘ feature/B feature/C各分支職責:
| 分支 | 版本類型 | 說明 |
|---|---|---|
main / master |
RELEASE tag | 永遠是最新的穩定正式版本;只有 release 分支或 hotfix 可以 merge 進來 |
develop |
x.y.z-SNAPSHOT |
整合所有 feature;每次 CI build 產出 SNAPSHOT artifact |
feature/xxx |
無版本號 | 個人功能開發,只 merge 進 develop |
release/x.y.z |
從 SNAPSHOT → RELEASE | 從 develop 切出,進行最終測試、bug fix,不加新功能;完成後 merge 進 main 和 develop |
hotfix/x.y.z |
PATCH 版本 | 從 main 切出,修正 production 緊急 bug;完成後 merge 回 main 和 develop |
3.2 GitHub Flow(適合持續部署、小型團隊)
main ──────●──────●──────●──── (隨時可部署)
↑ ↑ ↑
feature/A ──────┘ feature/B feature/C- 只有一條長生命分支
main - 所有開發都從 main 切出 feature branch
- 透過 PR review 後 merge 回 main
- merge 後立即觸發 CI/CD 部署
- 版本號用 Git tag 或 build number 標記
適合情境: Web service、SaaS 產品、有自動測試保護的服務
3.3 Trunk-based Development(適合高成熟度團隊)
- 所有人直接推到
main(或透過短暫的 feature branch,最多 1-2 天) - 依賴 Feature Flag 控制功能開關
- 需要高度的自動化測試覆蓋率(Unit + Integration + E2E)
- CI 每次 push 都跑完整測試並部署
四、分支 ↔ 版本號 ↔ 環境 對應規範
以 Git Flow 為例的完整對應:
| 分支 | 版本格式 | 部署環境 | Artifact 是否可覆蓋 |
|---|---|---|---|
feature/xxx |
無(或 feature build number) | 不部署(或 feature env) | 可 |
develop |
x.y.z-SNAPSHOT |
Dev / Feature Testing | 可(每次 build 覆蓋) |
release/x.y.z |
x.y.z-RC.n 或 x.y.z-SNAPSHOT |
Staging / UAT | 不可(RC 後 immutable) |
main(tag x.y.z) |
x.y.z(RELEASE) |
Production | 不可 |
hotfix/x.y.z+1 |
x.y.(z+1) |
緊急修補後直上 Production | 不可 |
五、CI/CD Pipeline 設計
5.1 Pipeline 觸發規則
# 範例:GitHub Actions trigger 規則
on:
push:
branches:
- develop # 推送到 develop → 觸發 SNAPSHOT build + Dev 部署
- 'release/**' # 推送到 release/* → 觸發 RC build + Staging 部署
- main # 推送到 main → 觸發 Production 部署
pull_request:
branches:
- develop
- main # PR 時跑完整測試但不部署
workflow_dispatch: # 手動觸發(緊急修補)5.2 Build 版本號自動化
方式一:從 Git tag 取版本(推薦)
# 從最近的 tag 推導版本號
VERSION=$(git describe --tags --abbrev=0)
# 如果 HEAD 不在 tag 上,加上 commit hash
VERSION=$(git describe --tags)
# 輸出:1.2.0-3-g1a2b3c4(距離 tag 3 個 commit)方式二:Maven Release Plugin(Spring Boot 後端)
Maven Release Plugin 是 Java 生態中專門處理「把 SNAPSHOT 版本升級為 RELEASE 版本」的工具,一個指令自動完成三件事:
- 把
pom.xml的版本從1.2.0-SNAPSHOT改成1.2.0 - 在 Git 上打一個 tag
v1.2.0 - 把版本號往前推到下一個 SNAPSHOT:
1.2.1-SNAPSHOT
<!-- pom.xml -->
<version>1.2.0-SNAPSHOT</version>
<!-- 加入 plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
</plugin># 執行 release(會自動改版本、tag、推進 SNAPSHOT)
mvn release:prepare release:perform方式三:Angular 版本管理(npm 生態)
Angular(npm 生態)沒有「SNAPSHOT」這個概念,但有對應的工具鏈:
| Maven 生態 | Angular/npm 等效 |
|---|---|
| SNAPSHOT | x.y.z-beta.0 / x.y.z-rc.0,或直接用 commit hash |
| RELEASE | 正式的 x.y.z tag |
| Maven Release Plugin | standard-version 或 semantic-release |
| CHANGELOG 自動生成 | conventional-changelog |
# 安裝 standard-version(Angular 專案常用)
npm install -D standard-version
# 根據 commit message 自動決定版本號升級幅度
npx standard-version
# 等效操作:
# 1. 更新 package.json 的 version
# 2. 更新 CHANGELOG.md
# 3. 打 Git tag# semantic-release(更自動化,常用於 CI 環境)
npx semantic-release
# 從 commit message 判斷要 bump MAJOR / MINOR / PATCH
# feat: → MINOR bump(1.1.0 → 1.2.0)
# fix: → PATCH bump(1.2.0 → 1.2.1)
# BREAKING CHANGE: → MAJOR bump(1.2.1 → 2.0.0)5.3 名詞解釋
Staging 環境是什麼?
軟體開發通常有多個環境,依照接近 production 的程度由低到高:
| 環境 | 用途 | 誰用 |
|---|---|---|
| Local | 開發者本機跑的 | 開發者個人 |
| Dev | 整合後的開發環境,跑 SNAPSHOT | 開發者、自動化 CI |
| Staging | 高度模擬 production 的環境,跑 RC / RELEASE | QA、PM、UAT |
| Production | 真實使用者的環境,跑正式 RELEASE | 使用者 |
Staging「stage」的原意就是「舞台」——像舞台彩排,正式演出前的最後一次完整驗證。
Staging 的資料、設定盡量與 production 相同(或匿名化後的 production dump),才能提前發現問題。
5.4 Spring Boot + Angular 完整 Pipeline 流程
這個專案是:後端 Spring Boot(Maven)+ 前端 Angular(npm),兩個 repo 或 monorepo 都適用。
【開發階段】
開發者 push feature/PROJ-123-xxx
└─ PR → Code Review → Merge to develop
↓
後端 CI(Spring Boot):
mvn test # Unit + Integration Test
mvn package -DSNAPSHOT # build: backend-1.2.0-SNAPSHOT.jar
docker build → 推送 ECR/GHCR # image tag: 1.2.0-SNAPSHOT
↓
前端 CI(Angular):
npm test # Jest / Karma unit test
npm run build -- --configuration=dev
# 產出 dist/,artifact 版本:package.json 的 "1.2.0-dev"
↓
Deploy to Dev Environment
後端:deploy backend-1.2.0-SNAPSHOT
前端:deploy Angular dist 1.2.0-dev
【Release 準備階段】(QA 通過 Dev,準備進 Staging)
切出 release/1.2.0 分支:
git checkout develop
git checkout -b release/1.2.0
↓
後端:移除 SNAPSHOT
pom.xml: 1.2.0-SNAPSHOT → 1.2.0-RC.1
或直接用 Maven Release Plugin: mvn release:prepare
↓
前端:更新版本號
package.json: "version": "1.2.0-rc.1"
npx standard-version --prerelease rc
↓
Build & Deploy to Staging:
後端:backend-1.2.0-RC.1.jar → deploy to staging
前端:npm run build -- --configuration=staging → deploy to staging
【UAT + 驗收階段】
Staging 上跑 E2E 測試(Cypress / Playwright)
PM / Client 做 UAT
Performance Test(JMeter / k6)
【正式發布】(UAT 通過)
後端:
pom.xml: 1.2.0-RC.1 → 1.2.0(移除 RC)
mvn release:perform(自動打 tag v1.2.0,版本推進 1.2.1-SNAPSHOT)
↓
前端:
package.json: "version": "1.2.0"
npx standard-version(更新 CHANGELOG,打 tag v1.2.0)
↓
merge release/1.2.0 → main
merge release/1.2.0 → develop(帶回 release 期間的 bug fix)
↓
Build RELEASE artifact:
後端:backend-1.2.0.jar / docker image:1.2.0
前端:npm run build -- --configuration=production
↓
Deploy to Production(Blue/Green 或 Canary)
↓
Smoke Test on Production
↓
🎉 Release 1.2.0 complete六、Migration Guide 是什麼?
Migration Guide(遷移指南)是當版本升級有 breaking change(MAJOR 版本遞增)時,寫給使用者 / 其他開發者的說明文件,告訴他們:
- 哪些東西不能向下相容了(API 改名、參數刪除、行為變更)
- 如何把舊的用法改成新的用法(step by step)
- 升級過程中需要執行哪些操作(資料庫 migration script、設定檔修改等)
Spring Boot 後端範例:
## Migration Guide: v1.x → v2.0
### Breaking Changes
#### 1. UserController API 路徑變更
- 舊:POST /api/user/create
- 新:POST /api/v2/users
- 說明:統一改為 RESTful 命名規範
#### 2. JWT Token 格式變更
- 舊:token payload 包含 userId(int)
- 新:token payload 包含 userId(UUID 字串)
- 影響:所有呼叫方需重新登入取得新 token
### Migration Steps
1. 執行資料庫腳本:`db/migrations/v2.0.0__user_id_to_uuid.sql`
2. 更新前端 API 呼叫路徑(見下方前端 Migration Guide)
3. 清除所有現有 session / token,要求使用者重新登入Angular 前端範例:
## Migration Guide: v1.x → v2.0
### 1. UserService API 呼叫路徑更新
舊:
this.userService.createUser(data) // POST /api/user/create
新:
this.userService.createUser(data) // POST /api/v2/users(Service 內部已更新)
### 2. User Model 型別變更
舊:interface User { id: number }
新:interface User { id: string } // UUID
需全域搜尋 userId 型別宣告並更新。什麼時候必須寫 Migration Guide?
MAJOR版本 bump(1.x.x → 2.0.0)時必寫- 有對外 API 的
MINOR版本若有重要行為改變,也建議寫- 純
PATCH(bug fix)通常不需要
七、作為 Team Lead / SD 的規範設計
7.1 定義前先問這些問題
- 部署頻率:每天多次?每週一次?有排程視窗?→ 影響你選 Git Flow 還是 Trunk-based
- 團隊規模:2 人?15 人?→ 影響分支複雜度,小團隊過度設計反而是負擔
- 有無 SLA:是否有不可停機要求?→ 影響 Blue/Green、Canary、Rollback 策略
- 語言生態:Maven / Gradle / npm / pip → 版本管理工具不同
- 現有 CI/CD 工具:Jenkins / GitHub Actions / GitLab CI / Azure DevOps
7.2 Team Lead 應文件化的最低規範
建議在 CONTRIBUTING.md 或 docs/branching-strategy.md 中寫清楚:
## 分支命名規則
- feature/[ticket-id]-short-description (例:feature/PROJ-123-add-login)
- bugfix/[ticket-id]-short-description
- hotfix/x.y.z
- release/x.y.z
## 版本號規則
- develop 分支永遠是 x.y.z-SNAPSHOT
- release 分支完成後移除 SNAPSHOT
- 每次 MAJOR bump 要寫 Migration Guide
- Hotfix 只改 PATCH 號
## PR 規則
- 每個 PR 至少 1 個 reviewer approve
- 必須通過所有 CI check 才能 merge
- squash merge 到 develop;merge commit 到 main
# squash merge:把整個 feature branch 的多個 commit 壓成一個 commit 再 merge
# 好處:develop 的 git log 整潔,每個 feature 是一條記錄
# merge commit:保留所有 commit 歷史,並加一個「Merge branch xxx」commit
# main 用 merge commit 是為了保留完整的 release 歷史軌跡
## Commit Message 規範(Conventional Commits)
feat: 新功能
fix: Bug 修正
docs: 只改文件
chore: 建構流程、依賴更新
BREAKING CHANGE: footer 寫 breaking change 說明7.3 版本發布 Checklist(Release Checklist)
## Release Checklist v{VERSION}
### 準備階段
- [ ] 從 develop 切出 release/{VERSION} 分支
- [ ] 更新 CHANGELOG.md
- [ ] 移除版本號中的 SNAPSHOT
- [ ] 確認所有 ticket 都已關閉或移至下一 sprint
### 測試階段
- [ ] CI 全部 green(Unit / Integration / E2E)
- [ ] Staging 部署完成
- [ ] UAT 簽核
- [ ] Performance Baseline 未退化
### 發布階段
- [ ] merge release/{VERSION} → main
- [ ] 打 tag v{VERSION}
- [ ] merge release/{VERSION} → develop(帶回 bug fix)
- [ ] 刪除 release branch
### 發布後
- [ ] Production Smoke Test
- [ ] 通知 Stakeholders
- [ ] 更新 develop 版本號至下一個 SNAPSHOT7.4 Hotfix 流程
# 1. 從 main 切出
git checkout main
git checkout -b hotfix/2.3.1
# 2. 修 bug,版本號從 2.3.0 → 2.3.1
# 3. 跑測試,CI green
# 4. merge 回 main 並 tag
git checkout main
git merge --no-ff hotfix/2.3.1
git tag -a v2.3.1 -m "Hotfix: fix critical payment bug"
# 5. 同步回 develop(確保 fix 不被下次 release 遺漏)
git checkout develop
git merge --no-ff hotfix/2.3.1
# 6. 刪分支
git branch -d hotfix/2.3.1八、常見陷阱
| 陷阱 | 問題 | 解法 |
|---|---|---|
| Production 部署 SNAPSHOT | 不知道確切是哪個 commit 的 build | 只允許 RELEASE 版本部署到 production |
| 版本號沒人維護 | 大家都不改,永遠是 1.0.0 |
CI pipeline 加驗證:develop push 要有 SNAPSHOT,release tag 要是 RELEASE |
| 多個 branch 同時改版本號 | merge 時發生衝突 | 版本號統一由 release/x.y.z 分支負責修改(見下方說明) |
| Hotfix 沒有 merge 回 develop | 下次 release 又出現同個 bug | Hotfix 完成後強制規定要同時 merge 回 main 和 develop |
| tag 打錯刪掉重打 | 已推到 remote 的 tag 被改 | git tag 打錯後要明確通知所有人,避免有人已用舊 tag build |
| CHANGELOG 沒人維護 | 上線了不知道改了什麼 | 用 conventional-changelog 自動從 commit message 生成 |
補充:「版本號統一由 release 分支負責修改」是什麼意思?
release 分支 就是 release/x.y.z,它是從 develop 切出來的一條短生命分支,專門用來準備這個版本的正式發布。
為什麼版本號改動要集中在這裡?
如果 feature branch 各自改版本號,再 merge 進 develop,每次都會衝突:
# 衝突情境(不好的做法)
feature/A:pom.xml version = 1.2.0-SNAPSHOT ← 改過
feature/B:pom.xml version = 1.3.0-SNAPSHOT ← 也改過
merge 時就衝突了正確做法:
feature/*分支不改版本號develop的版本號由 Team Lead 在 sprint 開始時統一設定(例如:1.2.0-SNAPSHOT)- 等 feature 都 merge 進 develop、QA 通過後,才切出
release/1.2.0 - 在
release/1.2.0分支上把版本號從1.2.0-SNAPSHOT改成1.2.0(移除 SNAPSHOT) - 這樣版本號變更只發生在一個地方,不會有衝突
誰負責改版本號?
分支 誰改 改成什麼
feature/* 不改 (維持 develop 的版本)
develop Team Lead 設定 x.y.z-SNAPSHOT
release/x.y.z Release Manager x.y.z-RC.n → x.y.z
main 自動(CI/CD) 打 tag x.y.z九、實戰場景:Multi-Repo + GitLab + Nexus
背景:
- 一個
platformrepo(整合所有 feature)- 多個
feature-xxxrepo(各自有後端 Spring Boot + 前端 Angular)- GitLab 管理 source code 與 CI/CD
- Nexus 作為 artifact repository(Maven JAR + npm package)
- Feature repo 把自己的 artifact 發布到 Nexus;Platform repo 宣告對 feature 的依賴
9.1 整體架構
┌─────────────────────────────────────────────────────────┐
│ GitLab Group │
│ │
│ platform/ feature-auth/ feature-payment/ │
│ ├─ backend/ ├─ backend/ ├─ backend/ │
│ │ pom.xml │ pom.xml │ pom.xml │
│ └─ frontend/ └─ frontend/ └─ frontend/ │
│ package.json package.json package.json │
└─────────────────────────────────────────────────────────┘
│
│ publish artifact
▼
┌─────────────────────────────────────────────────────────┐
│ Nexus │
│ │
│ maven-snapshots/ maven-releases/ │
│ ├─ feature-auth-1.2.0-SNAPSHOT.jar │
│ └─ ... ├─ feature-auth-1.1.0.jar │
│ └─ feature-payment-2.0.0.jar │
│ │
│ npm-snapshots/ npm-releases/ │
│ ├─ @feature/auth-1.2.0-snapshot.tgz │
│ └─ ... └─ @feature/auth-1.1.0.tgz │
└─────────────────────────────────────────────────────────┘
│
│ resolve dependency
▼
┌─────────────────────────────────────────────────────────┐
│ platform repo │
│ backend/pom.xml: │
│ <dependency> │
│ <artifactId>feature-auth</artifactId> │
│ <version>1.2.0-SNAPSHOT</version> ← dev 用 SNAPSHOT│
│ </dependency> │
│ │
│ frontend/package.json: │
│ "@feature/auth": "1.2.0-snapshot" ← dev 用 snapshot│
└─────────────────────────────────────────────────────────┘9.2 Nexus 倉庫設計
Nexus 建議設兩組倉庫(Maven + npm 各一組):
Maven 倉庫:
| Nexus Repo | 類型 | 用途 | 可覆蓋? |
|---|---|---|---|
maven-snapshots |
hosted | 接收 SNAPSHOT artifact | ✅ 可(每次 CI 覆蓋) |
maven-releases |
hosted | 接收 RELEASE artifact | ❌ 不可(immutable) |
maven-proxy |
proxy | 代理 Maven Central(外部依賴) | — |
maven-group |
group | 統一入口,合併上面三個 | — |
npm 倉庫:
| Nexus Repo | 類型 | 用途 |
|---|---|---|
npm-snapshots |
hosted | 開發中的 npm package |
npm-releases |
hosted | 正式版 npm package |
npm-proxy |
proxy | 代理 npmjs.com |
npm-group |
group | 統一 registry 入口 |
在
~/.npmrc或.npmrc中設定:registry=https://nexus.your-company.com/repository/npm-group/ //nexus.your-company.com/repository/npm-group/:_authToken=${NEXUS_NPM_TOKEN}
9.3 Feature Repo:版本 & 分支規範
每個 feature repo(例如 feature-auth)遵循 Git Flow:
feature/xxx → develop → release/1.2.0 → main版本號命名:
| 分支 | 後端版本(pom.xml) | 前端版本(package.json) |
|---|---|---|
feature/* |
不改(繼承 develop) | 不改 |
develop |
1.2.0-SNAPSHOT |
1.2.0-snapshot |
release/1.2.0 |
1.2.0-RC.1 → 1.2.0 |
1.2.0-rc.1 → 1.2.0 |
main(tag) |
1.2.0 |
1.2.0 |
npm 不支援
SNAPSHOT這個關鍵字(這是 Maven 術語),
npm 慣例是用1.2.0-snapshot.0、1.2.0-rc.1、1.2.0-beta.1這類 prerelease 格式。
Feature Repo 的 .gitlab-ci.yml:
stages:
- test
- build
- publish
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
NEXUS_URL: "https://nexus.your-company.com"
# ─────────────────────────────────────
# 後端(Spring Boot)
# ─────────────────────────────────────
backend:test:
stage: test
script:
- cd backend
- mvn test
only:
- merge_requests
- develop
- /^release\/.*/
backend:publish-snapshot:
stage: publish
script:
- cd backend
- mvn deploy -DskipTests
-DaltDeploymentRepository=snapshots::default::${NEXUS_URL}/repository/maven-snapshots/
only:
- develop # develop push → 自動發布 SNAPSHOT 到 Nexus
backend:publish-release:
stage: publish
script:
- cd backend
# 移除 SNAPSHOT,打 tag,發布 RELEASE 到 Nexus
- mvn release:prepare release:perform
-DreleaseVersion=${RELEASE_VERSION}
-DdevelopmentVersion=${NEXT_SNAPSHOT_VERSION}
-DaltReleaseDeploymentRepository=releases::default::${NEXUS_URL}/repository/maven-releases/
only:
- /^release\/.*/ # release branch push → 手動或 trigger 發布 RELEASE
when: manual # 避免意外觸發,需手動確認
# ─────────────────────────────────────
# 前端(Angular)
# ─────────────────────────────────────
frontend:test:
stage: test
script:
- cd frontend
- npm ci
- npm test -- --watch=false --browsers=ChromeHeadless
only:
- merge_requests
- develop
- /^release\/.*/
frontend:publish-snapshot:
stage: publish
script:
- cd frontend
- npm ci
- npm run build -- --configuration=production
# 設定 snapshot 版本號(從 package.json 取版本再加 -snapshot)
- VERSION=$(node -p "require('./package.json').version")
- npm version ${VERSION}-snapshot.$(date +%Y%m%d%H%M%S) --no-git-tag-version
- npm publish --registry ${NEXUS_URL}/repository/npm-snapshots/
only:
- develop
frontend:publish-release:
stage: publish
script:
- cd frontend
- npm ci
- npm run build -- --configuration=production
- npm publish --registry ${NEXUS_URL}/repository/npm-releases/
only:
- /^release\/.*/
when: manual9.4 Platform Repo:依賴管理
Platform repo 整合所有 feature 的依賴,本身也有自己的版本號。
後端 pom.xml(Spring Boot):
<project>
<groupId>com.company</groupId>
<artifactId>platform</artifactId>
<version>3.0.0-SNAPSHOT</version> <!-- platform 自己的版本 -->
<properties>
<!-- 在這裡統一管理各 feature 的版本號 -->
<feature-auth.version>1.2.0-SNAPSHOT</feature-auth.version>
<feature-payment.version>2.1.0-SNAPSHOT</feature-payment.version>
</properties>
<dependencies>
<dependency>
<groupId>com.company</groupId>
<artifactId>feature-auth-backend</artifactId>
<version>${feature-auth.version}</version>
</dependency>
<dependency>
<groupId>com.company</groupId>
<artifactId>feature-payment-backend</artifactId>
<version>${feature-payment.version}</version>
</dependency>
</dependencies>
<repositories>
<!-- 從 Nexus group 取得依賴(含 SNAPSHOT + RELEASE + proxy) -->
<repository>
<id>nexus-group</id>
<url>https://nexus.your-company.com/repository/maven-group/</url>
<snapshots><enabled>true</enabled></snapshots>
<releases><enabled>true</enabled></releases>
</repository>
</repositories>
</project>前端 package.json(Angular):
{
"name": "@company/platform-frontend",
"version": "3.0.0-snapshot",
"dependencies": {
"@feature/auth-frontend": "1.2.0-snapshot.20260608120000",
"@feature/payment-frontend": "2.1.0-snapshot.20260607090000"
}
}前端 snapshot 版本號較難「浮動」,npm 沒有 Maven 那種「永遠拉最新 SNAPSHOT」的機制,
建議:在 CI 自動更新到最新 snapshot tag,或用1.2.0-snapshot.x配合npm update。
9.5 Release 時的跨 Repo 協調流程
這是 Multi-Repo 最麻煩的地方:feature repo 必須先 release,platform repo 才能 release。
【Sprint 結束,準備 Release】
Step 1:各 Feature Repo 各自 release
┌──────────────────────────────────────────┐
│ feature-auth: develop → release/1.2.0 │
│ ├─ pom.xml: 1.2.0-SNAPSHOT → 1.2.0 │
│ ├─ package.json: 1.2.0-snapshot → 1.2.0│
│ ├─ CI 跑測試 │
│ ├─ 發布 1.2.0 到 Nexus maven-releases │
│ ├─ 發布 1.2.0 到 Nexus npm-releases │
│ └─ merge to main, tag v1.2.0 │
│ │
│ feature-payment: 同上流程 → v2.1.0 │
└──────────────────────────────────────────┘
↓
Step 2:Platform Repo 更新依賴版本,進行 Release
┌──────────────────────────────────────────┐
│ platform: develop → release/3.0.0 │
│ ├─ pom.xml: feature-auth 改為 1.2.0 │
│ │ (從 SNAPSHOT 改為 RELEASE) │
│ ├─ package.json: @feature/auth 改為 1.2.0│
│ ├─ CI 重跑完整整合測試 │
│ ├─ Deploy to Staging │
│ ├─ UAT 通過後,platform 版本改 3.0.0 │
│ ├─ 發布 platform 3.0.0 artifacts │
│ └─ merge to main, tag v3.0.0 │
└──────────────────────────────────────────┘
↓
Step 3:Deploy to Production
platform 3.0.0 → Production關鍵原則:
部署到 Staging 或 Production 的 platform,其所有 feature 依賴必須是 RELEASE 版本,不可有任何 SNAPSHOT。
可在 Maven 加入這個 CI 驗證:
<!-- 在 CI pipeline 加一個驗證 job,發現 SNAPSHOT 依賴就 fail -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>no-snapshots-in-release</id>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<requireReleaseDeps>
<onlyWhenRelease>true</onlyWhenRelease>
<message>Release build 不允許 SNAPSHOT 依賴!</message>
</requireReleaseDeps>
</rules>
</configuration>
</execution>
</executions>
</plugin>9.6 版本依賴矩陣(Compatibility Matrix)
當 feature repo 越來越多,需要維護一張「哪個 platform 版本對應哪些 feature 版本」的表:
| Platform | feature-auth | feature-payment | feature-report |
|---|---|---|---|
| 3.0.0 | 1.2.0 | 2.1.0 | 1.0.0 |
| 2.5.0 | 1.1.0 | 2.0.0 | — |
| 2.0.0 | 1.0.0 | 1.5.0 | — |
建議把這張表維護在:
- Platform repo 的
CHANGELOG.md或docs/compatibility.md - 或用 GitLab Wiki
9.7 常見跨 Repo 問題與解法
| 問題 | 情境 | 解法 |
|---|---|---|
| Platform 拉到過期 SNAPSHOT | feature-auth 的 SNAPSHOT 被覆蓋,platform 拉到的是半小時前的舊 build | CI pipeline 固定 SNAPSHOT 版本加上時間戳(1.2.0-snapshot.20260608120000),確保可追溯 |
| Feature 改了 API,Platform 還沒更新 | feature-auth 2.0.0 有 breaking change,platform 還在用 1.x | Feature repo 發布 MAJOR 版本時必須通知 platform team,並附 Migration Guide |
| Release 順序搞錯 | Platform 先 release,feature 還在 SNAPSHOT | CI 加入 SNAPSHOT 依賴檢查(上面的 enforcer plugin),有 SNAPSHOT 就不讓 release pipeline 跑 |
| 哪個 feature 版本是哪個 commit | SNAPSHOT 不好追 | 每個 SNAPSHOT build 在 Nexus 的 metadata 裡加上 git.commit.id(用 git-commit-id-maven-plugin) |
| 同時有多個 feature 在開發,platform 整合爆炸 | 多個 feature SNAPSHOT 互相影響 | 每個 feature 的 SNAPSHOT 只在各自的整合環境測試,platform develop 的整合測試排在最後 |
9.8 GitLab Pipeline 整體觸發關係
feature-auth repo
develop push
→ CI: test + build
→ publish feature-auth-1.2.0-SNAPSHOT.jar to Nexus
→ trigger platform repo pipeline(可選)
feature-auth repo
release/1.2.0 merge to main
→ CI: test + build
→ publish feature-auth-1.2.0.jar to Nexus
→ 自動開 GitLab Issue / 通知 platform team:「auth 1.2.0 已 release,請更新依賴」
platform repo
develop push(開發者手動更新依賴版本後)
→ CI: mvn dependency:resolve(確認 Nexus 上的依賴存在)
→ 整合測試
→ Deploy to Dev
platform repo
release/3.0.0(所有 feature 都 release 後)
→ 驗證無 SNAPSHOT 依賴(enforcer plugin)
→ CI: 完整測試
→ Deploy to Staging → UAT → Deploy to Production在 GitLab 中可以用 Pipeline Triggers 或 GitLab CI downstream pipeline 串接:
# feature-auth/.gitlab-ci.yml
notify-platform:
stage: notify
trigger:
project: company/platform
branch: develop
strategy: depend # 等待 downstream pipeline 完成
only:
- develop疑問 / 待確認
- Monorepo 下的版本管理(Nx / Turborepo / Lerna)如何處理多個 package 的版本?
- Feature Flag 服務(LaunchDarkly / Unleash)與版本號的關係?
- Helm Chart 版本號 vs Application 版本號 如何同步?
- Container image tag 策略:用
latest的風險與替代方案? - 當 feature repo 數量很多時,Release Train(固定時間點集體 release)vs 獨立 release 哪個更適合?