為什麼Google上十億行代碼都放在同一個倉庫裡

專注於Java領域優質技術號,歡迎關注

本文作者 Rachel Potvin, Josh Levenberg ,由 Jesse 翻譯

導讀:相對於一般公司,Google 使用了單一代碼倉庫,很多人不理解為什麼這麼做。本文作者是谷歌基礎設施小組的工程師,對這個問題進行了詳細解讀。譯者在翻譯過程中受益良多,也相信大家看完之後會認為自己還活在史前時代。

早期 Google 員工決定使用集中式源代碼管理系統來管理代碼庫。 這種方法已經在 Google 運行了 16 年以上,而今天絕大多數的 Google 軟件仍然存儲在一個共享的代碼庫中,隨著 Google 開發軟件數量穩步增加,Google 代碼庫的規模也呈指數增長(圖1)。 因此,用於管理代碼庫的技術也發生了顯著變化。

為什麼Google上十億行代碼都放在同一個倉庫裡

本文概述了該代碼庫的規模,並詳細介紹了 Google 定製的集中式代碼庫以及該模型的選擇原因。Google 使用自主開發的版本控制系統,管理公司的代碼庫。 這個集中式系統是許多 Google 開發人員工作流程的基礎。 在這裡,我們提供了系統和工作流的背景,這些系統和工作流程可以有效地管理和高效地使用這樣一個大型代碼庫。 我們將解釋 Google 的“基於trunk的開發”策略和支持系統,以及構建工作流程,還有保持 Google 的代碼庫健康的工具,包括用於靜態分析,代碼清理和簡化 code review 的軟件。

Google 規模 Google 95%的軟件開發人員使用的代碼庫滿足超大規模系統的定義[4],該倉庫是可以成功擴展集中式代碼庫的證據。

Google 代碼庫包含大約十億個文件,並且具有約3500萬次提交的歷史(包含 Google 18 年所有代碼提交)。 該代碼庫包含 86TB 的數據,包括 900 萬個源文件以及大約 20 億行代碼。 文件總數還包括複製到發佈分支的源文件,最新版本刪除的文件,配置文件,文檔和數據文件; 請參閱此處的表格,以瞭解 2015 年 1 月以來 Google 存儲庫統計信息的摘要。

2014 年,每週在 Google 代碼庫中有 1500 萬行代碼被修改。相比之下,Linux 內核是一個大型開源軟件代碼庫示例,該代碼庫包含 40,000 個文件中共有大約 1500 萬行代碼。 [14]

Google 的代碼庫由來自世界各國數十個辦事處的 25,000 多名 Google 軟件開發人員共享。 在典型的工作日,他們通常會對代碼庫進行 16,000 次更改,另有 24,000 次更改由自動化系統提交。 每天,代碼庫提供數十億次文件讀取請求,峰值每秒大約有 80 萬個查詢,工作日平均每秒大約有 50 萬個查詢。 大部分流量來自 Google 的分佈式構建和測試系統。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖2

圖2 報告了 2010 年 1 月至 2015 年 7 月主要代碼庫每週提交數量。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖3

圖3 報告了在同一時間段內每週向 Google 主代碼庫提交數量。 總提交的代碼包括交互式用例或用戶數據以及自動化提交的代碼。 假期(如聖誕節和元旦,美國感恩節和美國獨立日)會有大幅度提交行數下跌。

2012 年 10 月,Google 代碼庫增加了對 Windows 和 Mac 用戶的支持(之前僅支持Linux),現有的Windows和Mac代碼庫與主代碼庫合併。 Google 的代碼庫合併工具將所有歷史變更歸因於其原始作者,因此[圖2]中圖形中的相應凸起。 這種合併的效果在[圖1]中也是顯而易見的。

每週提交的圖表顯示,到2012年之前,提交率由用戶主導,此時 Google 將代碼庫改為私有實現,如下所述。 在此之後,自動提交到存儲庫開始增加。 代碼提交的增長主要是由於自動化。

管理這種規模的代碼庫和開發對於 Google 來說是一個持續的挑戰。 儘管經過幾年的試驗,Google 還沒有找到一個商業上可用的或開放源代碼版本控制系統,以便在單一代碼庫中支持這種規模。 Google 解決此問題的專有系統是 Piper。

背景

在審視使用單一代碼庫的優缺點之前,需要了解一些 Google 工具和工作流的背景。

Piper and CitC 。 Piper是一個大型代碼庫,在標準的 Google 基礎設施上實現,最初是基於 BigTable,現在是基於Spanner。 [3] Piper 分佈在全球 10 個 Google 數據中心,依靠 Paxos [6]算法來保證副本一致性。 該架構提供了高冗餘,並有助於優化Google 軟件開發人員的延遲。 此外,緩存和異步操作可以隱藏大量網絡延遲。 這很重要,因為獲得 Google 雲工具鏈的全部優勢需要開發人員在線。

在推出Piper之前,Google 主要依靠一臺Perforce實例(加上自定義緩存基礎架構[1],提供服務超過10年)。 繼續擴展 Google 代碼庫是開發Piper的主要動力。

由於 Google 的源代碼是公司最重要的資產之一,因此安全功能是 Piper 設計的關鍵考慮因素。 Piper 支持文件級訪問控制列表。 所有Piper用戶都可以看到大部分代碼庫; 也可以更嚴格地控制重要的配置文件或關鍵算法的文件。 可以對 Piper 中的文件進行讀寫訪問。 如果敏感數據文件被意外地提交給 Piper,則可以清除該文件。 讀取日誌允許管理員確定是否有人在刪除問題文件之前訪問過該文件。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖4

在 Piper 工作流程中( 見圖4 ),開發人員在更改代碼庫之前創建文件的本地副本。 這些文件存儲在開發人員擁有的工作區中。 Piper 工作區與 Apache Subversion(Git中的本地克隆)或 Perforce 中的客戶端的工作副本相當。 Piper 代碼庫中的更新可以根據需要被拉入工作空間並與正在進行的工作進行合併( 見圖5 )。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖5

可以與其他開發人員共享工作空間快照以供審核。工作空間中的文件僅在經過 Google code review 過程後才會提交到中央代碼庫。

大多數開發人員通過名為 Clients in Cloud 的系統或 CitC 訪問 Piper,該系統由基於雲的存儲後端和 Linux FUSE [13]文件系統組成。 開發人員將他們的工作空間看作是文件系統中的目錄,將更改覆蓋在完整的Piper庫之上。 CitC 支持代碼瀏覽和 Unix 工具,無需本地克隆或同步狀態。開發人員可以在Piper存儲庫中的任何地方瀏覽和編輯文件,只有修改的文件才存儲在其工作空間中。 這種結構意味著CitC工作區通常僅消耗少量存儲(平均工作空間少於10個文件),同時向開發人員呈現整個Piper代碼庫。

對文件的所有寫入都作為快照存儲在 CitC 中,使得可以根據需要恢復以前的工作階段。 可以明確命名,恢復或標記快照以供審核。

CitC 工作區可以在任何連接到雲的機器上使用,從而輕鬆切換機器並且不間斷地工作。 這也使得開發人員可以在 CitC 工作區中查看彼此的工作。 將所有正在進行中的工作存儲在雲中是 Google 工作流程的重要組成部分。 工作狀態可用於其他工具,包括基於雲的構建系統,自動測試基礎架構以及代碼瀏覽,編輯和查看工具。

有幾個工作流程利用了 CitC 中未提交代碼的特性,使軟件開發人員能夠更有效率的使用大型代碼庫。 例如,當發送更改 code review 時,開發人員可以啟用自動提交選項,這在代碼作者和審閱者處於不同的時區時特別有用。 review 被標記為完成時,測試將會運行; 如果可以通過測試,代碼將被合併到代碼庫,不需要進一步的人工干預。 Google 代碼瀏覽工具 CodeSearch 支持使用 CitC 工作區進行簡單的編輯。 瀏覽資料庫時,開發人員可以點擊按鈕進入編輯模式,並進行簡單的更改(例如修改打字或改進評論)。 然後,在不離開代碼瀏覽器的情況下,他們可以將自己的更改發送到適當的審閱者,並啟用自動提交。

Piper 也可以在沒有 CitC 的情況下使用。 開發人員可以將 Piper 工作區存儲在本地計算機上。 Piper 還可以和 Git 互操作。 目前,超過 80% 的 Piper 用戶使用 CitC,由於 CitC 有許多優勢,使用率持續增長。

Piper 和 CitC 可以保證在 Google 代碼庫的規模下,使用單一代碼庫進行有效的工作。 這些系統的設計和架構都受到 Google 採用的基於 trunk 的開發模式的影響,如下所述。

基於 trunk 的開發

Google 在 Piper 源代碼庫之上實施基於 trunk 的開發。 Piper 用戶絕大多數在“head”或最新版本的“trunk”或“mainline”代碼副本中工作。 對代碼庫的更改是串行的。 基於trunk的開發與中央代碼庫的組合定義了單一代碼庫模型。 在任何提交之後,其他所有開發人員都能看到更改。 Piper 用戶對 Google 代碼庫的一致視圖是提供本文後面描述的優勢的關鍵。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖6

基於 trunk 的開發是有益的,因為它避免了合併長支鏈分支時的痛苦。 儘管代碼分支通常用於發佈上線,但是在 Google 代碼分支支持的不好。 通常在 trunk 上開發 bug fix 和必須添加到版本中的增強功能,然後將其引入到 release 分支中(參見圖6 )。 由於需要保持穩定性並限制發佈分支上的流失,所以 release 通常是“head”的快照,根據需要從”head”拉出可選的少量帶代碼。在 branch 和 trunk 上並行開發的長壽命 branch 是非常罕見的。

Piper 和 CitC 可以在 Google 代碼庫的規模下,使用單一源代碼庫進行有效的工作。

當開發新功能時,新舊代碼路徑通常同時存在,通過使用條件標誌來控制。 這種技術避免了開發分支的需要,並且通過配置更新來打開或者關閉功能。 雖然開發人員還需要一些額外的複雜性,但是避免了開發分支合併問題。 標誌翻轉使得用戶切換具有問題的新實現變得更加容易和快捷。 該方法通常用於項目特定的代碼,而不是通用的庫代碼,最終會刪除標誌和舊代碼。 Google 使用類似的方法來對不同代碼做測試。 這樣的A / B test 可以從代碼性能到與產品變化相關的參數。

Google 工作流程

需要幾種最佳實踐和支持系統,以避免在基於 trunk 的開發模式中碰到的問題。 例如,Google 有一個自動測試基礎設施,可以在幾乎每個提交上啟動所有受影響的依賴項測試。 如果一次代碼更改造成構建破壞,系統就會自動撤消更改。 為了減少發生的錯誤代碼的發生率,高度可定製的 Google “預提交”基礎架構可以在更改代碼添加到代碼庫之前自動進行測試和分析。 針對所有更改運行一組全局預先提交分析,代碼所有者可以創建僅在其指定的代碼庫中的目錄上運行的自定義分析。 僅有一小部分非常低級別的核心庫使用branch的機制,以保證在新版本暴露給客戶端代碼之前執行其他測試。

鼓勵代碼質量的一個重要方面是期望在提交到代碼庫之前對所有代碼進行 review。 大多數開發人員可以在代碼庫的任何地方查看和建議更改(除了一組更加精心控制的高度機密代碼之外)。 不熟悉的開發人員更改相關代碼的風險通過代碼 review 過程和代碼所有權的概念得到緩解。Google 代碼庫以樹結構佈局。 每個目錄都有一組所有者控制是否接受目錄中文件的更改。 所有者通常是在相關目錄中處理項目的開發人員。 變更通常會從一位開發人員收到詳細的代碼審查開始,從而評估變更的質量,以及所有者的認可批准,評估變更對的適用性。

代碼 review 者會對代碼質量方面進行評論,包括設計,功能,複雜性,測試,命名,評論質量和代碼風格。 Google 已經編寫了一個名為 Critique 的代碼審查工具,允許審閱者查看代碼的演變,並對任何一行的更改進行評論。 它鼓勵進一步的修改和 review,以達到所有者的要求。

Google 的靜態分析系統(Tricorder [10] )和預提交基礎設施還可以在 Google 代碼審查工具中自動提供有關代碼質量,測試覆蓋率和測試結果的數據。 這些計算密集型檢查被定期觸發,發送代碼修改以供 review。 Tricorder 還為許多錯誤提供了修改的建議。 這些系統提供重要數據,以提高代碼審查的有效性,並保持 Google 代碼庫的健康。

Google 開發人員小組不時進行代碼清理,以進一步維護代碼庫的健康。 執行這些更改的開發人員通常將過程分為兩個階段。 首先進行大的向後兼容的更改。 一旦完成,可以進行第二個較小的更改以刪除不再引用的代碼。 Rosie 工具支持這種大規模清理和代碼更改的第一階段。 使用 Rosie,開發人員可以創建一個大補丁。 Rosie負責將大補丁分成較小的補丁,獨立測試,發送出去進行代碼 review,並在通過測試和代碼審查後自動提交。 Rosie 根據項目目錄行拆分補丁,依靠前面描述的代碼所有權層次結構將補丁發送給適當的審閱者。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖7

圖7報告了每月通過 Rosie 進行的更改次數,表明 Rosie 作為 Google 大規模代碼更改的工具的重要性。 使用Rosie需要注意其使用成本。隨著 Rosie 的流行度和使用率的增長,顯而易見,必須建立一些控制措施,以將 Rosie 的用途限制高價值變化中。 2013 年,Google 通過了正式的大規模變化 review 流程,導致了從 2013 年到 2014 年的 Rosie 數量的減少。在評估 Rosie 變更時,評審委員會將變更的收益與審閱者時間和存儲庫流失的成本相平衡。我們稍後更仔細地研究類似的權衡。

總而言之,Google 開發了許多工具來支持其龐大的代碼庫,包括基於 trunk 的開發,分佈式源代碼存儲庫 Piper,工作區客戶端 CitC 以及工作流支持工具 Critique,CodeSearch,Tricorder,和 Rosie。 我們在這裡討論這個模型的利弊。

分析

本節概述並擴展了單一代碼庫的優勢以及與維護此類模型規模相關的成本。

優點 。 支持超大規模的 Google 代碼庫,同時為千上萬的用戶服務,保持良好的性能是一個挑戰,但由於其引人注目的優勢,Google 已經擁抱了單一代代碼庫。

最重要的是它支持:

  • 統一版本
  • 廣泛的代碼共享和重用;
  • 簡化依賴關係管理
  • 原子變化;
  • 大規模重構;
  • 團隊合作;
  • 靈活的團隊邊界和代碼所有權;
  • 代碼可見性和清晰的樹結構,提供隱含的團隊命名空間。

單一代碼庫提供統一的版本控制和單一代碼來源。 對於哪個存儲庫託管文件的權威版本,並不存在任何混淆。 如果一個團隊想要依賴另一個團隊的代碼,可以直接依賴。 Google代碼庫包含大量有用的庫,而單一代碼庫可以引導廣泛的代碼共享和重用。

Google構建系統[5]可以輕鬆地在目錄之間包含代碼,從而簡化依賴關係管理。 對項目的依賴性的更改會觸發依賴代碼的重建。 由於所有代碼都在相同的存儲庫中進行版本控制,所以只有一個版本,也不關心依賴關係的獨立版本。

為什麼Google上十億行代碼都放在同一個倉庫裡

圖8

最值得注意的是,該模型允許 Google 避免當A依賴於B和C時發生的“鑽石依賴”問題( 見圖8 )(B和C都依賴於D,但B需要版本D.1和C需要版本D 0.2)。 在大多數情況下,可能很難在不導致破壞的情況下發布新版本,因為所有調用方必須同時更新。 當庫調用者託管在不同的存儲庫中時,這種更新很困難。

在開源世界中,依賴關係通常被庫更新所破壞,查找所有共同工作的依賴庫版本都是一個挑戰。 更新依賴關係的版本對於開發人員來說可能是痛苦的,延遲更新可能會變成非常昂貴的技術債務。 使用單一代碼庫,對於更新庫的人來說,在同一時間更新所有受影響的依賴關係更容易。 依賴引起的技術性債務在作出變更時立即予以償還。 基礎庫的更改將立即通過依賴關係鏈傳播到依賴於庫的最終產品中,而不需要單獨的同步或遷移步驟。

請注意,如下所述,在源/ API 級別以及二進制文件之間可能存在鑽石依賴問題。 [12]在谷歌,通過使用靜態鏈接避免了二進制問題。

進行原子變化的能力也是整體模型的一個非常強大的特徵。 開發人員可以在一致的操作中,對代碼庫中的數百或數千個文件進行重大變更。 例如,開發人員可以在單個提交中重命名類或函數,但不會破壞任何構建或測試。

在單一代碼庫中,或至少在集中式服務器上,所有源代碼的可用性使得核心庫的維護者在提交高影響力更改之前可以更輕鬆地執行測試和性能基準測試。 這種方法對於探索和測量高度破壞性變化的價值是有用的。 一個具體的例子是評估轉換 Google 數據中心以支持非 x86 機器架構的可行性的實驗。

由於Google代碼庫的結構,開發人員無需決定代碼庫邊界。 工程師不需要“branch”共享庫的開發,或者跨倉庫合併來更新代碼。 團隊邊界是流動的。 當項目所有權更改或計劃合併系統時,所有代碼都已在同一個庫中。 這種環境使代碼庫的循環重構和重組變得容易。 移動項目和更新依賴關係可以原子地應用於代碼庫,並且受影響代碼的開發歷史保持不變且可用。

單一代碼庫的另一個屬性是容易理解的代碼庫的佈局,因為它被組織在單個樹中。 每個團隊在主樹中都有一個目錄結構,有效地充當項目自己的命名空間。 每個源文件都可以通過單個字符串唯一標識,該文件路徑可選地包含修訂版本號。 瀏覽代碼庫,很容易瞭解任何源文件如何適用於代碼庫。

Google 代碼庫不斷髮展。 更復雜的代碼庫現代化工作(例如將其更新為 C++ 11 或推出性能優化[9] )通常由專用的代碼庫維護者集中管理。 這樣的努力可以觸及五十萬個變量聲明或函數調用點(分佈在數十萬個源代碼文件中)。 由於所有項目都集中存儲,所以專家團隊可以為整個公司做這項工作,而不是要求很多人開發自己的工具。

舉個例子,請考慮 Google 的編譯器團隊,他們會確保 Google 的開發人員使用最新的工具鏈,並從生成的代碼和“可調試性”的最新改進中獲益。 單一代碼庫使編譯團隊能夠全面瞭解 Google 如何使用各種語言,並允許他們進行代碼庫範圍的清理,以防止更改破壞構建。 這大大簡化了編譯器驗證,從而減少了編譯器發佈週期,並使 Google 有可能安全地執行編譯器版本(通常每年對C ++編譯器來說超過20個)升級。

通過對夜間運行性能測試和迴歸測試產生的數據進行分析,編譯器團隊可以將默認編譯器設置調整為最佳。 例如,谷歌的 Java 開發人員都看到他們的垃圾回收(GC) CPU 消耗量下降了50%以上,而且 GC 停留時間從 2014 年到 2015 年下降了 10%-40%。另外,當軟件發現錯誤,編譯器團隊有可能添加新的警告以防止錯誤重複發生。 結合此更改,他們會掃描整個存儲庫以查找並修復正在存在該問題的其他實例,然後再轉到新的編譯器錯誤。 過去的實踐證明編譯器拒絕有問題的代碼大大提升了 Google 的代碼運行狀況。

將所有源代碼存儲在通用版本控制存儲庫中可以使代碼庫維護者有效地分析和更改 Google 的源代碼。像 Refaster [11] 和 ClangMR [15] (通常與 Rosie 一起使用)這樣的工具利用 Google 源代碼的單一視圖來執行源代碼的高級轉換。 單一代碼庫捕獲所有依賴關係信息。 可以放心地刪除舊的 API,因為可以使所有調用者使用新API。 在任何給定時間,通過確保更改的原子性和整個存儲庫的單一全局視圖,單一代碼庫極大地簡化了這些工具的開發過程。

鼓勵代碼質量的 Google 文化其中一個重要方面是期望在提交到代碼庫之前對所有代碼進行審核。

成本和權衡

注意單一代碼庫絕不意味著整體化的軟件設計,使用這個模型涉及必須考慮一些缺點和權衡。

這些成本和權衡分為三類:

  • 開發和執行的工具投資;
  • 代碼庫複雜性,包括不必要的依賴性和代碼發現的困難;
  • 達到代碼健壯性的努力。

在許多方面,單一代碼庫導致更簡單的工具。 然而,還需要將工具規模擴展到代碼庫的規模。 例如,Google 已經為 Eclipse 集成開發環境(IDE)編寫了一個自定義插件,以使 IDE 能夠使用大型代碼庫。 Google 的代碼索引系統支持靜態分析,代碼瀏覽工具中的交叉引用,以及 Emacs,Vim 和其他開發環境的豐富的 IDE 功能。 這些工具需要持續的投資來管理日益增長的 Google 代碼庫規模。

除了建立和維護可擴展工具的投資外,Google 還必須承擔運行這些系統的成本,其中一些是非常計算密集型的。 許多 Google 的內部開發人員工具套件,包括自動化測試基礎架構和高度可擴展的構建基礎設施,對於支持單一代碼庫的規模至關重要。 因此,必須權衡如何運行這些工具以平衡執行成本與提供給開發人員的數據的好處。

單一代碼庫更容易理解代碼庫的結構,因為在依賴關係之間沒有跨倉庫邊界。 然而,隨著規模的增加,代碼查找變得更加困難,因為像grep這樣的標準工具基本不可用。 開發人員必須能夠探索代碼庫,找到相關的庫,並瞭解如何使用它們以及誰編寫它們。 庫作者經常需要了解他們的 API 如何被使用。 這需要對代碼搜索和瀏覽工具的重大投資。Google 已經發現這種投資非常有益,提高了所有開發人員的生產力。 [9]

訪問整個代碼庫鼓勵廣泛的代碼共享和重用。 有些人會認為,這種模式依賴於 Google 構建系統的可擴展性,使得添加依賴關係變得太容易,並且減少了軟件開發人員設計穩定且精心設計的 API 的動機。

由於創建依賴關係的輕鬆,通常團隊不要考慮其依賴關係圖,使代碼清理更容易出錯。 不必要的依賴可能會增加項目對下游構建破壞的風險,導致二進制文件膨脹,並在構建和測試中創造額外的工作。 此外,維護遺留項目會導致生產力下降。

Google 的試圖控制不必要的依賴。 已經有工具幫助識別和刪除不需要依賴關係。 還存在用於識別未充分利用的依賴關係或識別不需要的庫的工具。 7 工具 Clipper 依賴於一個自定義的Java編譯器來生成一個精確的交叉引用索引。 然後,它使用索引構建可達性圖,並確定從不使用什麼類。 Clipper 可以通過幫助開發人員找到相對容易刪除或分解的目標來指導依賴重構的工作。

開發人員可以在一個一致的操作中,通過存儲庫中的數百或數千個文件進行重大變更。

依賴重構和清理工具是有幫助的,但理想情況下,代碼所有者應該能夠防止創建不必要的依賴關係。 2011 年,Google 開始推廣 API 可見性的概念,將新 API 的默認可見性設置為“私有”。 這迫使開發人員明確地標記 API,以供其他團隊使用。 從 Google 的大型代碼庫的經驗中學到的教訓應該是儘快實施,以鼓勵更好的依賴結構。

大多數 Google 代碼可供所有 Google 開發人員使用,這導致了一種文化,一些團隊希望其他開發人員閱讀他們的代碼,而不是為他們提供單獨的 API 文檔。 這種做法有利與弊。 開發人員有時會閱讀 API 代碼,最終依賴於底層的實現細節。 這種行為可能會為那些不願意向用戶暴露的細節的團隊提供一些維護負擔。

該模型還要求團隊在使用開源代碼時相互協作。 存代碼的一個區域保留用於開源代碼(在 Google 開發或外部開發)。 為了防止依賴衝突,需要確保在任何給定的時間只有一個開源版本可用。 使用開源代碼的團隊在進行依賴升級時,會花時間處理新版本的開源庫。

Google 投入巨大的努力來維護代碼健康,以解決與代碼庫複雜性和依賴關係管理相關的一些問題。 例如,專用工具會自動檢測和刪除死碼,分割大量重構,並自動分配代碼評估(如通過 Rosie),並將 API 標記為不推薦使用。 需要人力運行這些工具並管理相應的大規模代碼更改。 審查代碼庫範圍內的清理和其他工作引起的持續簡單重構也會產生成本。

備擇方案

隨著像Git這樣的分佈式版本控制系統(DVCS)的普及和使用越來越多,Google 考慮是否將Piper轉移到Git作為其主要的版本控制系統。 Google 的一個團隊專注於支持Git,Google 在 Google 主代碼庫之外由 Google 的 Android 和 Chrome 團隊使用。 由於外部合作伙伴和開源協作,使用 Git 對於這些團隊很重要。

Git 社區強烈建議開發人員擁有越來越多的代碼庫。 Git-clone 操作需要將所有內容複製到本地計算機,這是與大型存儲庫不兼容的過程。 要轉移到基於 Git 的源代碼託管,有必要將 Google 的存儲庫拆分成數千個獨立的存儲庫,以實現合理的性能。 這樣的重組將需要 Google 開發人員的文化和工作流程更改。 作為比較,Google 的 Git 託管的 Android 代碼分為超過 800 個獨立的代碼庫。

鑑於 Google 已經建立的現有工具所獲得的價值以及整體代碼庫結構的許多優勢,轉換到越來越多的代碼庫對於 Google 的主代碼庫來說是沒有意義的。 移動到Git或需要代碼庫拆分對 Google 來說並不引人注目。

Google 源代碼團隊目前的投資主要集中在內部源代碼系統的持續可靠性,可擴展性和安全性上。 該團隊還在與Mercurial進行實驗性工作,這是一款類似Git的開源DVCS。 目標是向Mercurial客戶端添加可伸縮性功能,以便高效地支持 Google 的規模。 這將為 Google 開發人員提供一種與單一代碼庫庫一起使用流行的DVCS風格工作流的替代方案。 這一努力與開源的Mercurial社區合作,其中包括來自其他公司的貢獻者。

結論

Google 在 1999 年將現有的 Google 代碼庫從 CVS 遷移到 Perforce 時,選擇了單一源代碼管理策略。 早期的 Google 工程師認為,單獨的代碼庫比多個代碼庫要嚴格得多,儘管當時他們沒有預料到代碼庫的未來規模以及所有支持的工具。

多年來,隨著繼續擴大集中式存儲庫所需的投資增長,Google 領導層偶爾會考慮從單模模式轉變是否有意義。 儘管需要努力,但由於其優勢,Google 選擇堅持使用集中式單一代碼庫。

源代碼管理的單一模型不適合所有人。 它最適合像 Google 這樣的組織,具有開放和協作的文化。 對於代碼庫的大部分是私有的或組之間隱藏的組織來說,這不太適用。

在 Google 方面,我們發現通過一些投資,源代碼管理的整體模式可以成功擴展到具有超過十億個文件,3500萬個提交和全球數千個開發者的代碼庫。 隨著 Google 和 Google 內部項目的規模和複雜性不斷增長,我們希望本文中描述的分析和工作流程可以使他們對其代碼庫的長期結構進行權衡決策。

致謝

我們希望感謝 Google 開發人員基礎架構團隊的所有當前和前任成員,他們致力於構建和維護本文中引用的系統,以及許多幫助審閱文章的人員; 特別是:Jon Perkins和Ingo Walther,當前的Piper技術引領者;凱爾Lippincott和Crutcher Dunnavant; Google 的大型重構大師Hyrum Wright; 和Chris Colohan,Caitlin Sadowski,Morgan Ames,Rob Siemborski,以及Piper和CitC開發和支持團隊,提供有見地的評論意見。

英文原文:https://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext


分享到:


相關文章: