工程師與貓
Published on

將公司專案轉換成 Monorepo 遇到的那些事 - 第九篇 如何管理 Monorepo 中的多個團隊協作

Authors
  • avatar
    Name
    Alex Yu
    Twitter
Team Collaboration

如何管理 Monorepo 中的多個團隊協作

在現代軟體開發中,隨著專案規模的擴大,多個團隊協作開發已經成為常態。當我們採用 monorepo 架構時,雖然帶來了許多優勢,但也同時為多團隊協作帶來了新的挑戰。本文將深入探討在 monorepo 環境中,如何有效管理多個團隊的協作流程,包含團隊間的程式碼共享和衝突解決方案。

多團隊協作的挑戰

在傳統的多儲存庫架構中,團隊分工相對清晰,每個團隊負責自己的儲存庫,協作模式相對簡單。但在 monorepo 中,所有團隊共享同一個程式碼庫,這會帶來一系列挑戰:

1. 程式碼所有權與責任模糊

當所有團隊的程式碼都在同一個儲存庫中時,很容易出現「誰是這段程式碼的擁有者」的問題。有時候一個功能或模組可能涉及多個團隊的領域,導致責任邊界模糊不清。這種情況下,當出現問題時,可能沒有特定團隊願意承擔修復責任,或者多個團隊可能同時修改相同的程式碼區域。

2. 程式碼審查壓力大

在大型 monorepo 中,每天可能有數十甚至上百個 Pull Request 需要審查。這不僅增加了團隊負擔,也可能導致程式碼審查品質下降。特別是當一個 PR 涉及多個團隊的專業領域時,找到合適的審查者變得更加困難。

3. 分支管理與合併衝突

多個團隊同時開發時,分支管理變得複雜,合併衝突的頻率也會增加,尤其是當多個團隊修改共享元件或核心程式碼時。這些衝突不僅耗費開發時間去解決,還可能導致整合問題和更嚴重的技術債。

4. 建置和測試的效能問題

當專案規模越來越大,完整的建置和測試時間會越來越長,這會影響團隊的開發效率。如果沒有良好的增量建置和測試策略,每次小改動都可能觸發整個系統的重新建置和測試,大大延長了回饋循環。

團隊協作的最佳實踐

基於多年在大型 monorepo 專案中的經驗,以下是一些經過實戰檢驗的最佳實踐:

程式碼審查流程

CODEOWNERS 機制

PR CODEOWNERS

在 GitHub 和 GitLab 等平台上,我們可以利用 CODEOWNERS 檔案清晰地定義程式碼的所有權。這是一個非常實用的功能,它不僅可以明確每段程式碼的擁有權,還能自動將 PR 分配給相應的擁有者,有效減少了手動指派審查者的時間。

以下是一個實際的 CODEOWNERS 範例:

# 全域預設擁有者
*                   @org/core-team

# 前端團隊負責的區域
/apps/web/          @org/frontend-team
/libs/ui/           @org/ui-team
/libs/shared/       @org/shared-team

# 後端團隊負責的區域
/apps/api/          @org/backend-team
/libs/data-access/  @org/data-team

# 特殊檔案的擁有者
package.json        @org/devops-team
nx.json             @org/devops-team
.github/            @org/infra-team

這種方式可以:

  • 明確每段程式碼的擁有權
  • 自動通知相關團隊進行程式碼審查
  • 防止未經授權的修改
  • 提高程式碼品質,確保專業團隊審查相應區域
  • 減少手動分配審查者的管理負擔

程式碼所有權與團隊結構

團隊結構與 Nx Workspace 結構對應

在我們的實務中,我們根據領域驅動設計 (DDD) 的概念將團隊和程式碼結構對齊。每個團隊負責一個或多個業務領域,例如:

  • 產品團隊:負責使用者面對的產品功能
  • 平台團隊:負責底層平台和共享基礎設施
  • 體驗團隊:負責 UI 元件和使用者體驗
  • 資料團隊:負責資料處理和分析

我們的 Nx workspace 結構也相應地組織:

my-workspace/
├── apps/
│   ├── product-a/
│   ├── product-b/
│   └── admin/
├── libs/
│   ├── product/
│   │   ├── feature-a/
│   │   └── feature-b/
│   ├── platform/
│   │   ├── core/
│   │   └── utils/
│   ├── experience/
│   │   ├── ui/
│   │   └── design-system/
│   └── data/
│       ├── models/
│       └── services/

這種結構使每個團隊都能在自己的領域內自主工作,同時透過共享 libs 實現程式碼重用。

複雜 PR 的審查者選擇

除了利用 CODEOWNERS 自動分配外,對於複雜的 PR,我們也會依循以下原則手動選擇額外的審查者:

  • 選擇熟悉相關程式碼的人員
  • 確保至少有一位具有該領域專業知識的成員
  • 考慮團隊成員的工作負載,避免同一人同時審查太多 PR

程式碼審查分級制度

我們實施了一個基於變更範圍和風險的分級審查制度:

  1. 小型變更(如文案修改、簡單 bug 修復):只需一位團隊成員審查
  2. 中型變更(如新功能、重構):需要相關模組的擁有者審查
  3. 高風險變更(如核心架構修改、跨多個團隊的變更):需要資深工程師和架構師審查,並可能需要進行系統設計評審

這種分級制度確保了程式碼審查的效率和品質,讓重要的變更得到足夠的重視,同時避免小改動被拖延。

定時程式碼審查會議

每個團隊每週安排固定的程式碼審查會議,處理積壓的或複雜的 PR。這不僅加速了審查流程,還提供了知識分享的機會。會議遵循以下形式:

  • 開發者簡要介紹 PR 的目的和實現方式(5分鐘)
  • 團隊集體審查關鍵部分(10-15分鐘)
  • 解決任何疑問和討論潛在問題(10分鐘)
  • 制定清晰的後續步驟

這種做法特別適合處理跨團隊的複雜 PR,能加快審查過程並避免來回溝通的延遲。

分支策略與衝突管理

基於 trunk-based 開發的適應性策略

在我們的經驗中,嚴格的 trunk-based 開發模式在大型 monorepo 中效果最好,但需要一些調整:

  • 主分支保護:main 分支必須受到保護,所有程式碼必須透過 PR 合併
  • 短生命週期的功能分支:鼓勵小型、頻繁的提交,功能分支存活時間不超過 2-3 天
  • 頻繁同步主分支:每天至少一次從主分支同步到功能分支,減少合併衝突

以下是我們實際使用的 git workflow:

# 建立功能分支
git checkout -b feature/user-profile

# 進行開發,頻繁提交
git add .
git commit -m "feat: add user profile avatar component"

# 每天同步主分支
git fetch origin main
git rebase origin/main

# 推送到遠端
git push origin feature/user-profile -f  # 因為 rebase 後需要強制推送

# 建立 PR 後,可能需要再次同步主分支
git checkout main
git pull
git checkout feature/user-profile
git rebase main
git push origin feature/user-profile -f

這種方法可以:

  • 減少長期分支造成的合併困難
  • 鼓勵更小、更容易審查的修改
  • 保持主分支的穩定性
  • 降低整合問題的風險

建置和測試優化

使用 Nx 的分散式快取

Nx 提供了強大的分散式快取功能,可以大幅提升建置和測試速度。在我們的專案中,使用 Nx Cloud 後,CI 時間減少了 70%。

// nx.json
{
  "tasksRunnerOptions": {
    "default": {
      "runner": "@nrwl/nx-cloud",
      "options": {
        "cacheableOperations": ["build", "test", "lint", "e2e"],
        "accessToken": "your-nx-cloud-token"
      }
    }
  }
}

如我們在第八篇文章中討論的,也可以使用 S3 作為快取後端來替代 Nx Cloud:

// nx.json
{
  "tasksRunnerOptions": {
    "default": {
      "runner": "@pellegrims/nx-remotecache-s3",
      "options": {
        "cacheableOperations": ["build", "lint", "test", "e2e"],
        "s3Options": {
          "region": "ap-northeast-1",
          "bucketName": "your-nx-cache-bucket"
        }
      }
    }
  }
}

這種快取機制對多團隊協作特別有價值:

  • 團隊不會重複執行已經由其他團隊完成的任務
  • 從一個 PR 獲得的快取結果可以用於其他 PR
  • 大大減少了等待時間,提高開發效率

影響分析與智能測試

Nx 的影響分析功能可以幫助我們只運行受影響的測試,這在大型團隊中尤為重要:

# 只運行受當前變更影響的測試
nx affected:test

# 只建置受影響的應用程式
nx affected:build

# 自動化 CI 腳本
echo "Running tests for affected projects"
nx affected:test --configuration=ci

在我們的實務中,這使得平均 CI 運行時間從 45 分鐘減少到 8 分鐘,極大提高了開發效率。具體是透過:

  • 只測試受影響的程式碼,而不是整個程式碼庫
  • 利用 Nx 的相依圖來精確確定哪些測試需要運行
  • 平行執行測試以進一步節省時間

協作工具與流程

共享元件庫管理

在多團隊協作中,共享元件的管理尤為重要。我們建立了一套完整的流程:

共享元件開發流程

  1. 提案階段:任何新的共享元件首先需要提交提案,包含用例、API 設計和技術細節
  2. 審查階段:由架構委員會審查提案,確保與現有系統的一致性
  3. 開發階段:由提案團隊或平台團隊開發,必須包含完整文件和測試
  4. 試用階段:在有限範圍內試用,收集回饋
  5. 正式發布:完成試用後,正式發布並通知所有團隊

這個流程確保了我們的共享元件高品質且適合實際需求,同時避免了重複開發。

使用 Storybook 作為元件展示平台

我們使用 Storybook 作為所有共享元件的展示平台,這不僅方便了開發者了解可用元件,也成為了溝通的橋樑:

// libs/ui/buttons/src/lib/primary-button.stories.tsx
import { Meta, Story } from '@storybook/react';
import { PrimaryButton, PrimaryButtonProps } from './primary-button';

export default {
  title: 'UI/Buttons/PrimaryButton',
  component: PrimaryButton,
  argTypes: {
    onClick: { action: 'clicked' },
    variant: {
      control: {
        type: 'select',
        options: ['default', 'success', 'warning', 'danger'],
      },
    },
  },
} as Meta;

const Template: Story<PrimaryButtonProps> = (args) => <PrimaryButton {...args} />;

export const Default = Template.bind({});
Default.args = {
  children: '按鈕',
  variant: 'default',
};

export const Success = Template.bind({});
Success.args = {
  children: '成功按鈕',
  variant: 'success',
};

這個 Storybook 成為了團隊設計參考的權威來源。它提供了:

  • 每個元件的即時展示
  • 互動式文件,顯示 API 和用法
  • 不同變體和狀態的視覺呈現
  • 程式碼範例和最佳實務

衝突解決機制

即使有了良好的流程和工具,衝突仍然不可避免。以下是我們處理衝突的方式:

架構審查委員會

對於跨團隊的架構決策或衝突,我們建立了一個架構審查委員會,由各團隊的資深工程師組成。委員會負責:

  • 評估重大架構變更
  • 仲裁團隊間的技術分歧
  • 制定共享標準和最佳實務

委員會定期開會討論提案和問題,並且對於重大決策有最終裁決權。這種集中治理模式確保了整個系統的架構一致性和技術方向的統一。

定期同步會議

每個團隊選派代表參加每週的跨團隊同步會議,會議內容包括:

  • 分享各團隊當前的工作重點
  • 討論可能影響其他團隊的變更
  • 協調需要多團隊參與的專案
  • 分享學習心得和最佳實務

這些會議通常控制在 30 分鐘以內,重點是交流資訊而不是詳細討論。會議紀錄會共享給所有團隊成員,以保持透明度。

實際案例分析

以下是一個在 monorepo 環境中多團隊協作的實際案例:

案例:跨團隊共享元件庫升級

背景

我們需要對共享 UI 元件庫進行一次重大升級,涉及到設計變更和 API 重構,影響到所有使用這些元件的產品團隊。

挑戰

  1. 不同產品團隊有不同的發布週期和優先級
  2. 某些元件在不同產品中有不同的客製化需求
  3. 需要確保向後相容或提供平滑遷移路徑

解決方案

  1. 協作開發新版本

    • UI 團隊負責核心元件的開發
    • 各產品團隊派代表參與設計審查和API定義
    • 使用 feature flags 控制新功能的啟用
  2. 漸進式發布策略

    • 首先發布 alpha 版本供早期使用者試用
    • 基於回饋改進,發布 beta 版本
    • 在非關鍵產品中先行應用
    • 所有產品完成遷移後,發布穩定版
  3. 詳細文件和遷移指南

    • 為每個變更撰寫詳細說明
    • 提供遷移步驟和範例程式碼
    • 針對常見問題製作 FAQ

結果

升級過程持續了 6 個月,但最終所有團隊都順利完成了遷移。關鍵成功因素:

  • 各團隊的早期參與
  • 自動化工具減少了手動遷移工作
  • 漸進式發布降低了風險

結論與建議

經過多年在大型 monorepo 中管理多團隊協作的經驗,我總結出以下關鍵建議:

1. 明確的治理結構

建立明確的程式碼所有權和決策流程,避免責任模糊導致的延遲和衝突。CODEOWNERS 檔案和 RFC 流程是非常實用的工具。在我們的實務中,清晰的治理結構幫助我們減少了 30% 的程式碼審查時間,並且讓團隊間的合作更加順暢。

2. 重視自動化

在多團隊協作中,自動化不僅是提高效率的工具,更是確保一致性和減少錯誤的關鍵。投資於自動化測試、CI/CD、程式碼生成和重構工具,會帶來長期收益。我們發現,自動化流程幫助我們將發布頻率提高了 2 倍,同時保持了高品質標準。

3. 溝通與透明

頻繁而有效的溝通是多團隊協作的基石。確保團隊之間的資訊流通暢,包括定期同步會議、技術分享、文件更新等。透明的決策過程和清晰的溝通管道能有效減少誤解和衝突。

4. 漸進式變更

避免大規模、一次性的變更,而是採用漸進式的方法,分階段實施,這樣可以降低風險並及早發現問題。在我們的經驗中,將大型變更分解為多個小步驟,成功率提高了 80%。

5. 建立共享知識庫

在 monorepo 中,知識共享尤為重要。建立完善的文件系統、設計決策紀錄、技術部落格等,幫助團隊成員了解整個系統。我們使用了 Storybook 和內部技術分享會等多種方式,讓知識在團隊間流通。


在多團隊協作的 monorepo 中,管理複雜度是一場持久戰。透過清晰的結構、良好的流程、有效的工具和持續的改進,我們可以將這種複雜度轉化為團隊的競爭優勢,讓多個團隊能夠在同一個程式碼庫中和諧高效地協作。

正如我們在系列前幾篇文章中所討論的,良好的 monorepo 結構設計(第三篇)、有效的循環依賴管理(第五篇)、合理的版本控制(第六篇)、優化的測試流程(第七篇)和高效的效能解決方案(第八篇)都是多團隊協作的基礎。本文作為系列的第九篇,聚焦於人與組織的協作層面,與前面的技術討論相輔相成,共同構成了 monorepo 成功的關鍵要素。

希望這篇文章能對你在 monorepo 中管理多團隊協作有所幫助。如果有任何問題或經驗想要分享,歡迎在評論區留言交流。