通過 Git 託管和管理程式碼有兩種主要策略:單版本(monorepo)和多版本(multi-repo)。兩種方法各有利弊。
我們可以在任何語言的任何程式碼庫中使用任何一種方法。無論是包含少量庫還是數千庫的專案,都可以使用其中任何一種策略。即使涉及的團隊成員少則幾人,多則上百人,或者您想託管私有程式碼或開原始碼,您也可以根據各種因素選擇使用 monorepo 或 multi-repo。
每種方法都有哪些優點和缺點?什麼時候應該使用其中一種?讓我們一起來了解一下!
什麼是 repo?
repo (版本庫的簡稱)是一個專案中所有變更和檔案的儲存空間,使開發人員能夠在整個開發階段對專案資產進行 “版本控制”。
我們通常指的是 Git 倉庫(由 GitHub、GitLab 或 Bitbucket 提供),但這一概念也適用於其他版本控制系統(如 Mercurial)。
什麼是 Monorepo?
monorepo 方法使用單個版本庫來託管構成公司專案的多個庫或服務的所有程式碼。在最極端的情況下,一個公司的整個程式碼庫(跨越多個專案並使用不同語言編碼)都託管在一個單一的版本庫中。
Monorepo 的優勢
將整個程式碼庫託管在單個版本庫中有以下好處。
降低入職門檻
當新員工開始為公司工作時,他們需要下載程式碼並安裝所需的工具,才能開始執行任務。假設專案分散在多個資源庫中,每個資源庫都有自己的安裝說明和所需工具。在這種情況下,初始設定將非常複雜,而且文件往往不完整,這就需要這些新團隊成員向同事尋求幫助。
使用 monorepo 可以簡化問題。因為只有一個位置包含所有程式碼和文件,所以可以簡化初始設定。
集中管理程式碼
有了單一版本庫,所有開發人員都能看到所有程式碼。它簡化了程式碼管理,因為我們可以使用單一的問題跟蹤器來觀察整個應用程式生命週期中的所有問題。
例如,當一個問題跨越兩個(或多個)子庫,而錯誤存在於依賴庫中時,這些特性就非常有價值。在多個版本庫中,要找到發生問題的程式碼片段可能具有挑戰性。
除此之外,我們還需要確定使用哪個版本庫來建立問題,然後邀請並交叉標記其他團隊的成員來幫助解決問題。
不過,有了 monorepo,無論是查詢程式碼問題還是協作排除故障都變得更加簡單。
無障礙的全應用程式重構
在建立應用程式範圍內的程式碼重構時,多個庫都會受到影響。如果您通過多個版本庫託管它們,那麼管理所有不同的拉取請求以保持它們之間的同步就會成為一項挑戰。
使用 monorepo 可以輕鬆地對所有庫的所有程式碼進行修改,並在單個拉取請求下提交。
更難破壞相鄰功能
使用 monorepo,我們可以設定所有庫的所有測試,以便在修改任何一個庫時都能執行。因此,對某些庫進行修改對其他庫產生不利影響的可能性降至最低。
團隊共享開發文化
儘管並非不可能,但使用 monorepo 方法,在不同團隊中激發獨特的亞文化就變得具有挑戰性。因為他們共享同一個資源庫,所以很可能共享相同的程式設計和管理方法,並使用相同的開發工具。
單版本庫方法的問題
將我們所有的程式碼都放在一個版本庫中有幾個缺點。
開發週期較慢
當某個庫的程式碼包含破壞性修改,導致依賴庫的測試失敗時,也必須在合併修改之前對程式碼進行修復。
如果這些庫依賴於其他團隊,而這些團隊正忙於其他任務,無法(或不願意)調整自己的程式碼以避免破壞性更改並使測試通過,那麼新功能的開發可能會停滯。
更有甚者,專案很可能只能以公司中最慢團隊的速度開始推進。這種結果可能會讓最快團隊的成員感到沮喪,為他們想要離開公司創造條件。
此外,一個庫還需要為所有其他庫執行測試。需要執行的測試越多,執行這些測試所需的時間就越長,從而降低了我們迭代程式碼的速度。
需要下載整個程式碼庫
當 monorepo 包含一個公司的所有程式碼時,它可能會非常龐大,包含數千兆位元組的資料。任何人都需要下載整個程式碼庫,才能為其中的任何庫做出貢獻。
處理龐大的程式碼庫意味著我們的硬碟空間利用率低,互動速度慢。例如,執行 git status
或使用 regex 在程式碼庫中搜尋等日常操作可能要比使用多個版本庫時多花幾秒甚至幾分鐘。
未修改的庫可能是新版本
當我們標記 monorepo 時,其中的所有程式碼都會被分配新標記。如果該操作觸發了新版本釋出,那麼該版本庫中的所有庫都將使用標籤中的版本號進行新版本釋出,即使其中許多庫可能沒有任何修改。
分叉更加困難
開源專案必須儘可能方便貢獻者參與。有了多個版本庫,貢獻者就可以直接前往他們想要貢獻的專案的特定版本庫。但是,如果一個單源庫承載多個專案,貢獻者必須首先找到正確的專案,並瞭解他們的貢獻會如何影響所有其他專案。
什麼是 Multi-Repo?
Multi-Repo 使用多個版本庫來託管公司所開發專案的多個庫或服務。在最極端的情況下,它會將每一套最基本的可重用程式碼或獨立功能(如微服務)託管到自己的版本庫中。
Multi-Repo 的優勢
將每個庫獨立於所有其他庫託管,可帶來諸多好處。
獨立的庫版本管理
標記一個版本庫時,其整個程式碼庫都會被分配 “new” 標記。由於資源庫中只有特定庫的程式碼,因此該庫可以獨立於託管在其他地方的所有其他庫進行標記和版本控制。
每個庫都有一個獨立的版本,這有助於定義應用程式的依賴樹,讓我們可以配置使用每個庫的哪個版本。
獨立的服務釋出
由於資源庫只包含某些服務的程式碼,沒有其他內容,因此它可以有自己的部署週期,與訪問它的應用程式的進展無關。
服務可以使用快速釋出週期,如持續交付(新程式碼通過所有測試後即可部署)。訪問該服務的某些庫可能會使用較慢的釋出週期,例如每週只發布一次新版本的庫。
幫助定義整個組織的訪問控制
只有參與開發庫的團隊成員才需要新增到相應的資源庫並下載其程式碼。因此,應用程式中的每一層都有一個隱含的訪問控制策略。參與庫開發的人員將被授予編輯許可權,其他人可能無法訪問資源庫。或者,他們可以獲得閱讀許可權,但沒有編輯許可權。
允許團隊自主工作
團隊成員可以設計庫的架構,並在與所有其他團隊隔離的情況下實施程式碼。他們可以根據庫的總體情況做出決策,而不會受到外部團隊或應用程式特定要求的影響。
多版本庫方法的問題
使用多個資源庫會產生幾個問題。
庫必須不斷重新同步
當包含破壞性更改的新版本庫釋出時,依賴於該庫的庫需要進行調整以開始使用最新版本。如果庫的釋出週期快於其依賴庫的釋出週期,那麼它們之間很快就會脫節。
團隊需要不斷追趕,以使用其他團隊的最新版本。由於不同的團隊有不同的優先順序,有時可能很難做到這一點。
因此,跟不上的團隊最終可能會堅持使用所依賴庫的過時版本。這種結果會對應用程式產生影響(在安全性、速度和其他方面),而且不同庫之間的開發差距可能會越來越大。
可能導致團隊分散
當不同的團隊不需要互動時,他們可能會各自為政。從長遠來看,這可能會導致團隊在公司內部形成自己的亞文化,如採用不同的程式設計或管理方法,或使用不同的開發工具。
如果某些團隊成員最終需要在不同的團隊中工作,他們可能會受到一些文化衝擊,需要學習新的工作方式。
Monorepo vs Multi-Repo:主要區別
這兩種方法的最終目標是相同的:管理程式碼庫。因此,它們必須解決相同的難題,包括髮布管理、促進團隊成員之間的協作、處理問題、執行測試等。
它們的主要區別在於團隊成員做決定的時間:單釋出(monorepo)是在前期,多釋出(multi-repo)是在後期。
讓我們來詳細分析一下這個想法。
在多版本中,所有庫的版本號都是獨立的,因此團隊在釋出具有破壞性更改的庫時,只需為最新版本分配一個新的主版本號即可。其他小組可以讓其依賴的庫使用舊版本,並在程式碼調整後切換到新版本。
這種方法將何時調整所有其他庫的決定權留給了每個負責團隊,他們可以隨時調整。如果他們做得太晚,而新的庫版本又已釋出,那麼縮小各庫之間的差距就會變得越來越困難。
因此,雖然一個團隊可以快速、頻繁地迭代程式碼,但其他團隊可能無法跟上,最終導致產生的庫出現分歧。
另一方面,在 monorepo 環境中,我們不能在釋出一個庫的新版本時破壞其他庫,因為它們的測試會失敗。在這種情況下,第一個團隊必須與第二個團隊溝通,以便將更改納入其中。
這種方法迫使團隊在必須對單個庫進行更改時,必須對所有庫進行調整。所有團隊都必須相互溝通,共同達成解決方案。
因此,第一個團隊將無法以他們希望的速度進行迭代,但不同庫的程式碼在任何時候都不會開始出現分歧。
總之,多版本庫方法有助於在團隊中建立一種 “快速行動、打破常規” 的文化,讓靈活的獨立團隊以自己的速度產出成果。相反,monorepo 方法更傾向於建立一種意識和關懷的文化,即團隊不應該被拋在後面獨自處理問題。
多-單混合版本庫方法
如果我們無法決定使用多版本庫還是單版本庫,還有一種介於兩者之間的方法:使用多個版本庫,並使用一些工具保持它們的同步,使其類似於單版本庫,但更具靈活性。
Meta 就是這樣一種工具。它將多個版本庫組織在子目錄下,並提供一個命令列介面,可同時在所有版本庫中執行相同的命令。
meta 倉庫包含了組成專案的倉庫資訊。通過元克隆該版本庫後,將遞迴克隆所有需要的版本庫,使團隊新成員更容易立即開始專案工作。
要克隆 meta 倉庫及其定義的所有多個倉庫,我們必須執行以下操作:
meta git clone [meta repo url]
Meta 會為每個倉庫執行 git clone
,並將其放置在一個子資料夾中:
克隆 meta 專案 (圖片來源)
從那時起,執行 meta exec
命令將在每個子資料夾上執行命令。例如,在每個版本庫中執行 git checkout master
的操作如下:
meta exec "git checkout master"
單-多混合版本方法
另一種方法是在開發時通過 monorepo 管理程式碼,但在部署時將每個庫的程式碼複製到其獨立的資源庫中。這種策略在 PHP 生態系統中非常普遍,因為 Packagist(Composer 的主要版本庫)需要一個公共版本庫 URL 才能釋出軟體包,而且無法指明軟體包位於版本庫的子目錄中。
鑑於 Packagist 的限制,PHP 專案仍可使用單版本庫進行開發,但必須使用多版本庫方法進行部署。
要實現這種轉換,我們可以使用 git subtree split
執行指令碼,或者使用能執行相同邏輯的可用工具之一:
誰在使用 Monorepo 或 Multi-Repo
幾家大型科技公司傾向於使用 monorepo 方法,而其他公司則決定使用 multi-repo 方法。
谷歌、Facebook、Twitter 和 Uber 都公開表示支援 monorepo 方法。微軟執行著全球最大的 Git monorepo,用於託管 Windows 作業系統的原始碼。
與此相反,Netflix、亞馬遜和 Lyft 等著名公司則使用 multi-repo。
在多-單混合版本庫方面,Android 更新了多個版本庫,這些版本庫的管理方式類似於單版本庫。
在多-單混合版本庫方面,Symfony 將其所有元件的程式碼都儲存在一個 monorepo 中。他們將其拆分成獨立的軟體源進行部署(如 symfony/dependency-injection
和 symfony/event-dispatcher
)。
單版本庫和多版本庫示例
GitHub 上的 WordPress 賬戶提供了單版本庫和多版本庫方法的示例。
WordPress 的區塊編輯器 Gutenberg 由幾十個 JavaScript 包組成。這些包都託管在 WordPress/gutenberg monorepo
上,並通過 Lerna 進行管理,以幫助將它們釋出到 npm 程式碼庫中。
Openverse 是開放許可媒體的搜尋引擎,其主要部分託管在獨立的軟體源中: 前端、目錄和 API。
Monorepo vs Multi-Repo:如何選擇?
與許多開發問題一樣,您應該採用哪種方法並沒有預先確定的答案。不同的公司和專案會根據其獨特的條件從一種或另一種策略中獲益,例如:
- 程式碼庫有多大?是否包含數千兆位元組的資料?
- 有多少人會在程式碼庫上工作?是 10 人、100 人還是 1000 人?
- 有多少軟體包?是 10 個、100 個還是 1000 個?
- 團隊需要同時處理多少個軟體包?
- 軟體包的緊密耦合程度如何?
- 是否涉及不同的程式語言?是否需要安裝特定軟體或使用特殊硬體才能執行?
- 需要多少部署工具,設定起來有多複雜?
- 公司的文化是什麼?是否鼓勵團隊合作?
- 團隊知道如何使用哪些工具和技術?
小結
託管和管理程式碼有兩種主要策略:單版本庫(monorepo)和多版本庫(multi-repo)。單釋出(monorepo)方法是將不同庫或專案的程式碼,甚至是一個公司的所有程式碼,都儲存在一個版本庫中。而多版本系統將程式碼劃分為多個單元,如庫或服務,並將其程式碼託管在獨立的版本庫中。
使用哪種方法取決於多種條件。兩種策略各有優缺點,我們將在本文中一一詳述。
關於單版本庫或多版本庫,你還有什麼問題嗎?請在評論區告訴我們!
評論留言