为什么Google上十亿行代码都放在同一个仓库里?
導(dǎo)讀:相對于一般公司,Google 使用了單一代碼倉庫,很多人不理解為什么這么做。本文作者是谷歌基礎(chǔ)設(shè)施小組的工程師,對這個問題進行了詳細(xì)解讀。譯者在翻譯過程中受益良多,也相信大家看完之后會認(rèn)為自己還活在史前時代。
早期 Google 員工決定使用集中式源代碼管理系統(tǒng)來管理代碼庫。 這種方法已經(jīng)在 Google 運行了 16 年以上,而今天絕大多數(shù)的 Google 軟件仍然存儲在一個共享的代碼庫中,隨著 Google 開發(fā)軟件數(shù)量穩(wěn)步增加,Google 代碼庫的規(guī)模也呈指數(shù)增長(圖1)。 因此,用于管理代碼庫的技術(shù)也發(fā)生了顯著變化。
圖1本文概述了該代碼庫的規(guī)模,并詳細(xì)介紹了 Google 定制的集中式代碼庫以及該模型的選擇原因。Google 使用自主開發(fā)的版本控制系統(tǒng),管理公司的代碼庫。 這個集中式系統(tǒng)是許多 Google 開發(fā)人員工作流程的基礎(chǔ)。 在這里,我們提供了系統(tǒng)和工作流的背景,這些系統(tǒng)和工作流程可以有效地管理和高效地使用這樣一個大型代碼庫。 我們將解釋 Google 的“基于trunk的開發(fā)”策略和支持系統(tǒng),以及構(gòu)建工作流程,還有保持 Google 的代碼庫健康的工具,包括用于靜態(tài)分析,代碼清理和簡化 code review 的軟件。
?
Google 規(guī)模 Google 95%的軟件開發(fā)人員使用的代碼庫滿足超大規(guī)模系統(tǒng)的定義[4],該倉庫是可以成功擴展集中式代碼庫的證據(jù)。
Google 代碼庫包含大約十億個文件,并且具有約3500萬次提交的歷史(包含 Google 18 年所有代碼提交)。 該代碼庫包含 86TB 的數(shù)據(jù),包括 900 萬個源文件以及大約 20 億行代碼。 文件總數(shù)還包括復(fù)制到發(fā)布分支的源文件,最新版本刪除的文件,配置文件,文檔和數(shù)據(jù)文件; 請參閱此處的表格,以了解 2015 年 1 月以來 Google 存儲庫統(tǒng)計信息的摘要。
2014 年,每周在 Google 代碼庫中有 1500 萬行代碼被修改。相比之下,Linux 內(nèi)核是一個大型開源軟件代碼庫示例,該代碼庫包含 40,000 個文件中共有大約 1500 萬行代碼。 [14]
Google 的代碼庫由來自世界各國數(shù)十個辦事處的 25,000 多名 Google 軟件開發(fā)人員共享。 在典型的工作日,他們通常會對代碼庫進行 16,000 次更改,另有 24,000 次更改由自動化系統(tǒng)提交。 每天,代碼庫提供數(shù)十億次文件讀取請求,峰值每秒大約有 80 萬個查詢,工作日平均每秒大約有 50 萬個查詢。 大部分流量來自 Google 的分布式構(gòu)建和測試系統(tǒng)。?
圖2圖2 報告了 2010 年 1 月至 2015 年 7 月主要代碼庫每周提交數(shù)量。?
圖3圖3 報告了在同一時間段內(nèi)每周向 Google 主代碼庫提交數(shù)量。 總提交的代碼包括交互式用例或用戶數(shù)據(jù)以及自動化提交的代碼。 假期(如圣誕節(jié)和元旦,美國感恩節(jié)和美國獨立日)會有大幅度提交行數(shù)下跌。
2012 年 10 月,Google 代碼庫增加了對 Windows 和 Mac 用戶的支持(之前僅支持Linux),現(xiàn)有的Windows和Mac代碼庫與主代碼庫合并。 Google 的代碼庫合并工具將所有歷史變更歸因于其原始作者,因此[圖2]中圖形中的相應(yīng)凸起。 這種合并的效果在[圖1]中也是顯而易見的。
每周提交的圖表顯示,到2012年之前,提交率由用戶主導(dǎo),此時 Google 將代碼庫改為私有實現(xiàn),如下所述。 在此之后,自動提交到存儲庫開始增加。 代碼提交的增長主要是由于自動化。
管理這種規(guī)模的代碼庫和開發(fā)對于 Google 來說是一個持續(xù)的挑戰(zhàn)。 盡管經(jīng)過幾年的試驗,Google 還沒有找到一個商業(yè)上可用的或開放源代碼版本控制系統(tǒng),以便在單一代碼庫中支持這種規(guī)模。 Google 解決此問題的專有系統(tǒng)是 Piper。
?
背景
在審視使用單一代碼庫的優(yōu)缺點之前,需要了解一些 Google 工具和工作流的背景。
Piper and CitC?。 Piper是一個大型代碼庫,在標(biāo)準(zhǔn)的 Google 基礎(chǔ)設(shè)施上實現(xiàn),最初是基于 BigTable,現(xiàn)在是基于Spanner。 [3] Piper 分布在全球 10 個 Google 數(shù)據(jù)中心,依靠 Paxos [6]算法來保證副本一致性。 該架構(gòu)提供了高冗余,并有助于優(yōu)化Google 軟件開發(fā)人員的延遲。 此外,緩存和異步操作可以隱藏大量網(wǎng)絡(luò)延遲。 這很重要,因為獲得 Google 云工具鏈的全部優(yōu)勢需要開發(fā)人員在線。
在推出Piper之前,Google 主要依靠一臺Perforce實例(加上自定義緩存基礎(chǔ)架構(gòu)[1],提供服務(wù)超過10年)。 繼續(xù)擴展 Google 代碼庫是開發(fā)Piper的主要動力。
由于 Google 的源代碼是公司最重要的資產(chǎn)之一,因此安全功能是 Piper 設(shè)計的關(guān)鍵考慮因素。 Piper 支持文件級訪問控制列表。 所有Piper用戶都可以看到大部分代碼庫; 也可以更嚴(yán)格地控制重要的配置文件或關(guān)鍵算法的文件。 可以對 Piper 中的文件進行讀寫訪問。 如果敏感數(shù)據(jù)文件被意外地提交給 Piper,則可以清除該文件。 讀取日志允許管理員確定是否有人在刪除問題文件之前訪問過該文件。
在 Piper 工作流程中( 見圖4 ),開發(fā)人員在更改代碼庫之前創(chuàng)建文件的本地副本。 這些文件存儲在開發(fā)人員擁有的工作區(qū)中。 Piper 工作區(qū)與 Apache Subversion(Git中的本地克隆)或 Perforce 中的客戶端的工作副本相當(dāng)。 Piper 代碼庫中的更新可以根據(jù)需要被拉入工作空間并與正在進行的工作進行合并( 見圖5 )。?
圖5可以與其他開發(fā)人員共享工作空間快照以供審核。工作空間中的文件僅在經(jīng)過 Google code review 過程后才會提交到中央代碼庫。
大多數(shù)開發(fā)人員通過名為 Clients in Cloud 的系統(tǒng)或 CitC 訪問 Piper,該系統(tǒng)由基于云的存儲后端和 Linux FUSE [13]文件系統(tǒng)組成。 開發(fā)人員將他們的工作空間看作是文件系統(tǒng)中的目錄,將更改覆蓋在完整的Piper庫之上。 CitC 支持代碼瀏覽和 Unix 工具,無需本地克隆或同步狀態(tài)。開發(fā)人員可以在Piper存儲庫中的任何地方瀏覽和編輯文件,只有修改的文件才存儲在其工作空間中。 這種結(jié)構(gòu)意味著CitC工作區(qū)通常僅消耗少量存儲(平均工作空間少于10個文件),同時向開發(fā)人員呈現(xiàn)整個Piper代碼庫。
對文件的所有寫入都作為快照存儲在 CitC 中,使得可以根據(jù)需要恢復(fù)以前的工作階段。 可以明確命名,恢復(fù)或標(biāo)記快照以供審核。
CitC 工作區(qū)可以在任何連接到云的機器上使用,從而輕松切換機器并且不間斷地工作。 這也使得開發(fā)人員可以在 CitC 工作區(qū)中查看彼此的工作。 將所有正在進行中的工作存儲在云中是 Google 工作流程的重要組成部分。 工作狀態(tài)可用于其他工具,包括基于云的構(gòu)建系統(tǒng),自動測試基礎(chǔ)架構(gòu)以及代碼瀏覽,編輯和查看工具。
有幾個工作流程利用了 CitC 中未提交代碼的特性,使軟件開發(fā)人員能夠更有效率的使用大型代碼庫。 例如,當(dāng)發(fā)送更改 code review 時,開發(fā)人員可以啟用自動提交選項,這在代碼作者和審閱者處于不同的時區(qū)時特別有用。 review 被標(biāo)記為完成時,測試將會運行; 如果可以通過測試,代碼將被合并到代碼庫,不需要進一步的人工干預(yù)。 Google 代碼瀏覽工具 CodeSearch 支持使用 CitC 工作區(qū)進行簡單的編輯。 瀏覽資料庫時,開發(fā)人員可以點擊按鈕進入編輯模式,并進行簡單的更改(例如修改打字或改進評論)。 然后,在不離開代碼瀏覽器的情況下,他們可以將自己的更改發(fā)送到適當(dāng)?shù)膶忛喺?#xff0c;并啟用自動提交。
Piper 也可以在沒有 CitC 的情況下使用。 開發(fā)人員可以將 Piper 工作區(qū)存儲在本地計算機上。 Piper 還可以和 Git 互操作。 目前,超過 80% 的 Piper 用戶使用 CitC,由于 CitC 有許多優(yōu)勢,使用率持續(xù)增長。
Piper 和 CitC 可以保證在 Google 代碼庫的規(guī)模下,使用單一代碼庫進行有效的工作。 這些系統(tǒng)的設(shè)計和架構(gòu)都受到 Google 采用的基于 trunk 的開發(fā)模式的影響,如下所述。
基于 trunk 的開發(fā)?。 Google 在 Piper 源代碼庫之上實施基于 trunk 的開發(fā)。 Piper 用戶絕大多數(shù)在“head”或最新版本的“trunk”或“mainline”代碼副本中工作。 對代碼庫的更改是串行的。 基于trunk的開發(fā)與中央代碼庫的組合定義了單一代碼庫模型。 在任何提交之后,其他所有開發(fā)人員都能看到更改。 Piper 用戶對 Google 代碼庫的一致視圖是提供本文后面描述的優(yōu)勢的關(guān)鍵。
基于 trunk 的開發(fā)是有益的,因為它避免了合并長支鏈分支時的痛苦。 盡管代碼分支通常用于發(fā)布上線,但是在 Google 代碼分支支持的不好。 通常在 trunk 上開發(fā) bug fix 和必須添加到版本中的增強功能,然后將其引入到 release 分支中(參見圖6 )。 由于需要保持穩(wěn)定性并限制發(fā)布分支上的流失,所以 release 通常是“head”的快照,根據(jù)需要從”head”拉出可選的少量帶代碼。在 branch 和 trunk 上并行開發(fā)的長壽命 branch 是非常罕見的。
Piper 和 CitC 可以在 Google 代碼庫的規(guī)模下,使用單一源代碼庫進行有效的工作。
當(dāng)開發(fā)新功能時,新舊代碼路徑通常同時存在,通過使用條件標(biāo)志來控制。 這種技術(shù)避免了開發(fā)分支的需要,并且通過配置更新來打開或者關(guān)閉功能。 雖然開發(fā)人員還需要一些額外的復(fù)雜性,但是避免了開發(fā)分支合并問題。 標(biāo)志翻轉(zhuǎn)使得用戶切換具有問題的新實現(xiàn)變得更加容易和快捷。 該方法通常用于項目特定的代碼,而不是通用的庫代碼,最終會刪除標(biāo)志和舊代碼。 Google 使用類似的方法來對不同代碼做測試。 這樣的A / B test 可以從代碼性能到與產(chǎn)品變化相關(guān)的參數(shù)。
Google 工作流程?。 需要幾種最佳實踐和支持系統(tǒng),以避免在基于 trunk 的開發(fā)模式中碰到的問題。 例如,Google 有一個自動測試基礎(chǔ)設(shè)施,可以在幾乎每個提交上啟動所有受影響的依賴項測試。 如果一次代碼更改造成構(gòu)建破壞,系統(tǒng)就會自動撤消更改。 為了減少發(fā)生的錯誤代碼的發(fā)生率,高度可定制的 Google “預(yù)提交”基礎(chǔ)架構(gòu)可以在更改代碼添加到代碼庫之前自動進行測試和分析。 針對所有更改運行一組全局預(yù)先提交分析,代碼所有者可以創(chuàng)建僅在其指定的代碼庫中的目錄上運行的自定義分析。 僅有一小部分非常低級別的核心庫使用branch的機制,以保證在新版本暴露給客戶端代碼之前執(zhí)行其他測試。
鼓勵代碼質(zhì)量的一個重要方面是期望在提交到代碼庫之前對所有代碼進行 review。 大多數(shù)開發(fā)人員可以在代碼庫的任何地方查看和建議更改(除了一組更加精心控制的高度機密代碼之外)。 不熟悉的開發(fā)人員更改相關(guān)代碼的風(fēng)險通過代碼 review 過程和代碼所有權(quán)的概念得到緩解。Google 代碼庫以樹結(jié)構(gòu)布局。 每個目錄都有一組所有者控制是否接受目錄中文件的更改。 所有者通常是在相關(guān)目錄中處理項目的開發(fā)人員。 變更通常會從一位開發(fā)人員收到詳細(xì)的代碼審查開始,從而評估變更的質(zhì)量,以及所有者的認(rèn)可批準(zhǔn),評估變更對的適用性。
代碼 review 者會對代碼質(zhì)量方面進行評論,包括設(shè)計,功能,復(fù)雜性,測試,命名,評論質(zhì)量和代碼風(fēng)格。 Google 已經(jīng)編寫了一個名為 Critique 的代碼審查工具,允許審閱者查看代碼的演變,并對任何一行的更改進行評論。 它鼓勵進一步的修改和 review,以達到所有者的要求。
Google 的靜態(tài)分析系統(tǒng)(Tricorder [10] )和預(yù)提交基礎(chǔ)設(shè)施還可以在 Google 代碼審查工具中自動提供有關(guān)代碼質(zhì)量,測試覆蓋率和測試結(jié)果的數(shù)據(jù)。 這些計算密集型檢查被定期觸發(fā),發(fā)送代碼修改以供 review。 Tricorder 還為許多錯誤提供了修改的建議。 這些系統(tǒng)提供重要數(shù)據(jù),以提高代碼審查的有效性,并保持 Google 代碼庫的健康。
Google 開發(fā)人員小組不時進行代碼清理,以進一步維護代碼庫的健康。 執(zhí)行這些更改的開發(fā)人員通常將過程分為兩個階段。 首先進行大的向后兼容的更改。 一旦完成,可以進行第二個較小的更改以刪除不再引用的代碼。 Rosie 工具支持這種大規(guī)模清理和代碼更改的第一階段。 使用 Rosie,開發(fā)人員可以創(chuàng)建一個大補丁。 Rosie負(fù)責(zé)將大補丁分成較小的補丁,獨立測試,發(fā)送出去進行代碼 review,并在通過測試和代碼審查后自動提交。 Rosie 根據(jù)項目目錄行拆分補丁,依靠前面描述的代碼所有權(quán)層次結(jié)構(gòu)將補丁發(fā)送給適當(dāng)?shù)膶忛喺摺?/p>
圖7
圖7報告了每月通過 Rosie 進行的更改次數(shù),表明 Rosie 作為 Google 大規(guī)模代碼更改的工具的重要性。 使用Rosie需要注意其使用成本。隨著 Rosie 的流行度和使用率的增長,顯而易見,必須建立一些控制措施,以將 Rosie 的用途限制高價值變化中。 2013 年,Google 通過了正式的大規(guī)模變化 review 流程,導(dǎo)致了從 2013 年到 2014 年的 Rosie 數(shù)量的減少。在評估 Rosie 變更時,評審委員會將變更的收益與審閱者時間和存儲庫流失的成本相平衡。我們稍后更仔細(xì)地研究類似的權(quán)衡。
總而言之,Google 開發(fā)了許多工具來支持其龐大的代碼庫,包括基于 trunk 的開發(fā),分布式源代碼存儲庫 Piper,工作區(qū)客戶端 CitC 以及工作流支持工具 Critique,CodeSearch,Tricorder,和 Rosie。 我們在這里討論這個模型的利弊。
?
分析
本節(jié)概述并擴展了單一代碼庫的優(yōu)勢以及與維護此類模型規(guī)模相關(guān)的成本。
優(yōu)點?。 支持超大規(guī)模的 Google 代碼庫,同時為千上萬的用戶服務(wù),保持良好的性能是一個挑戰(zhàn),但由于其引人注目的優(yōu)勢,Google 已經(jīng)擁抱了單一代代碼庫。
最重要的是它支持:
-
統(tǒng)一版本
-
廣泛的代碼共享和重用;
-
簡化依賴關(guān)系管理
-
原子變化;
-
大規(guī)模重構(gòu);
-
團隊合作;
-
靈活的團隊邊界和代碼所有權(quán);
-
代碼可見性和清晰的樹結(jié)構(gòu),提供隱含的團隊命名空間。
單一代碼庫提供統(tǒng)一的版本控制和單一代碼來源。 對于哪個存儲庫托管文件的權(quán)威版本,并不存在任何混淆。 如果一個團隊想要依賴另一個團隊的代碼,可以直接依賴。 Google代碼庫包含大量有用的庫,而單一代碼庫可以引導(dǎo)廣泛的代碼共享和重用。
Google構(gòu)建系統(tǒng)[5]可以輕松地在目錄之間包含代碼,從而簡化依賴關(guān)系管理。 對項目的依賴性的更改會觸發(fā)依賴代碼的重建。 由于所有代碼都在相同的存儲庫中進行版本控制,所以只有一個版本,也不關(guān)心依賴關(guān)系的獨立版本。
最值得注意的是,該模型允許 Google 避免當(dāng)A依賴于B和C時發(fā)生的“鉆石依賴”問題( 見圖8 )(B和C都依賴于D,但B需要版本D.1和C需要版本D 0.2)。 在大多數(shù)情況下,可能很難在不導(dǎo)致破壞的情況下發(fā)布新版本,因為所有調(diào)用方必須同時更新。 當(dāng)庫調(diào)用者托管在不同的存儲庫中時,這種更新很困難。
在開源世界中,依賴關(guān)系通常被庫更新所破壞,查找所有共同工作的依賴庫版本都是一個挑戰(zhàn)。 更新依賴關(guān)系的版本對于開發(fā)人員來說可能是痛苦的,延遲更新可能會變成非常昂貴的技術(shù)債務(wù)。 使用單一代碼庫,對于更新庫的人來說,在同一時間更新所有受影響的依賴關(guān)系更容易。 依賴引起的技術(shù)性債務(wù)在作出變更時立即予以償還。 基礎(chǔ)庫的更改將立即通過依賴關(guān)系鏈傳播到依賴于庫的最終產(chǎn)品中,而不需要單獨的同步或遷移步驟。
請注意,如下所述,在源/ API 級別以及二進制文件之間可能存在鉆石依賴問題。 [12]在谷歌,通過使用靜態(tài)鏈接避免了二進制問題。
進行原子變化的能力也是整體模型的一個非常強大的特征。 開發(fā)人員可以在一致的操作中,對代碼庫中的數(shù)百或數(shù)千個文件進行重大變更。 例如,開發(fā)人員可以在單個提交中重命名類或函數(shù),但不會破壞任何構(gòu)建或測試。
在單一代碼庫中,或至少在集中式服務(wù)器上,所有源代碼的可用性使得核心庫的維護者在提交高影響力更改之前可以更輕松地執(zhí)行測試和性能基準(zhǔn)測試。 這種方法對于探索和測量高度破壞性變化的價值是有用的。 一個具體的例子是評估轉(zhuǎn)換 Google 數(shù)據(jù)中心以支持非 x86 機器架構(gòu)的可行性的實驗。
由于Google代碼庫的結(jié)構(gòu),開發(fā)人員無需決定代碼庫邊界。 工程師不需要“branch”共享庫的開發(fā),或者跨倉庫合并來更新代碼。 團隊邊界是流動的。 當(dāng)項目所有權(quán)更改或計劃合并系統(tǒng)時,所有代碼都已在同一個庫中。 這種環(huán)境使代碼庫的循環(huán)重構(gòu)和重組變得容易。 移動項目和更新依賴關(guān)系可以原子地應(yīng)用于代碼庫,并且受影響代碼的開發(fā)歷史保持不變且可用。
單一代碼庫的另一個屬性是容易理解的代碼庫的布局,因為它被組織在單個樹中。 每個團隊在主樹中都有一個目錄結(jié)構(gòu),有效地充當(dāng)項目自己的命名空間。 每個源文件都可以通過單個字符串唯一標(biāo)識,該文件路徑可選地包含修訂版本號。 瀏覽代碼庫,很容易了解任何源文件如何適用于代碼庫。
Google 代碼庫不斷發(fā)展。 更復(fù)雜的代碼庫現(xiàn)代化工作(例如將其更新為 C++ 11 或推出性能優(yōu)化[9] )通常由專用的代碼庫維護者集中管理。 這樣的努力可以觸及五十萬個變量聲明或函數(shù)調(diào)用點(分布在數(shù)十萬個源代碼文件中)。 由于所有項目都集中存儲,所以專家團隊可以為整個公司做這項工作,而不是要求很多人開發(fā)自己的工具。
舉個例子,請考慮 Google 的編譯器團隊,他們會確保 Google 的開發(fā)人員使用最新的工具鏈,并從生成的代碼和“可調(diào)試性”的最新改進中獲益。 單一代碼庫使編譯團隊能夠全面了解 Google 如何使用各種語言,并允許他們進行代碼庫范圍的清理,以防止更改破壞構(gòu)建。 這大大簡化了編譯器驗證,從而減少了編譯器發(fā)布周期,并使 Google 有可能安全地執(zhí)行編譯器版本(通常每年對C ++編譯器來說超過20個)升級。
通過對夜間運行性能測試和回歸測試產(chǎn)生的數(shù)據(jù)進行分析,編譯器團隊可以將默認(rèn)編譯器設(shè)置調(diào)整為最佳。 例如,谷歌的 Java 開發(fā)人員都看到他們的垃圾回收(GC) CPU 消耗量下降了50%以上,而且 GC 停留時間從 2014 年到 2015 年下降了 10%-40%。另外,當(dāng)軟件發(fā)現(xiàn)錯誤,編譯器團隊有可能添加新的警告以防止錯誤重復(fù)發(fā)生。 結(jié)合此更改,他們會掃描整個存儲庫以查找并修復(fù)正在存在該問題的其他實例,然后再轉(zhuǎn)到新的編譯器錯誤。 過去的實踐證明編譯器拒絕有問題的代碼大大提升了 Google 的代碼運行狀況。
將所有源代碼存儲在通用版本控制存儲庫中可以使代碼庫維護者有效地分析和更改 Google 的源代碼。像 Refaster [11] 和 ClangMR [15] (通常與 Rosie 一起使用)這樣的工具利用 Google 源代碼的單一視圖來執(zhí)行源代碼的高級轉(zhuǎn)換。 單一代碼庫捕獲所有依賴關(guān)系信息。 可以放心地刪除舊的 API,因為可以使所有調(diào)用者使用新API。 在任何給定時間,通過確保更改的原子性和整個存儲庫的單一全局視圖,單一代碼庫極大地簡化了這些工具的開發(fā)過程。
鼓勵代碼質(zhì)量的 Google 文化其中一個重要方面是期望在提交到代碼庫之前對所有代碼進行審核。
成本和權(quán)衡?。 注意單一代碼庫絕不意味著整體化的軟件設(shè)計,使用這個模型涉及必須考慮一些缺點和權(quán)衡。
這些成本和權(quán)衡分為三類:
-
開發(fā)和執(zhí)行的工具投資;
-
代碼庫復(fù)雜性,包括不必要的依賴性和代碼發(fā)現(xiàn)的困難;
-
達到代碼健壯性的努力。
在許多方面,單一代碼庫導(dǎo)致更簡單的工具。 然而,還需要將工具規(guī)模擴展到代碼庫的規(guī)模。 例如,Google 已經(jīng)為 Eclipse 集成開發(fā)環(huán)境(IDE)編寫了一個自定義插件,以使 IDE 能夠使用大型代碼庫。 Google 的代碼索引系統(tǒng)支持靜態(tài)分析,代碼瀏覽工具中的交叉引用,以及 Emacs,Vim 和其他開發(fā)環(huán)境的豐富的 IDE 功能。 這些工具需要持續(xù)的投資來管理日益增長的 Google 代碼庫規(guī)模。
除了建立和維護可擴展工具的投資外,Google 還必須承擔(dān)運行這些系統(tǒng)的成本,其中一些是非常計算密集型的。 許多 Google 的內(nèi)部開發(fā)人員工具套件,包括自動化測試基礎(chǔ)架構(gòu)和高度可擴展的構(gòu)建基礎(chǔ)設(shè)施,對于支持單一代碼庫的規(guī)模至關(guān)重要。 因此,必須權(quán)衡如何運行這些工具以平衡執(zhí)行成本與提供給開發(fā)人員的數(shù)據(jù)的好處。
單一代碼庫更容易理解代碼庫的結(jié)構(gòu),因為在依賴關(guān)系之間沒有跨倉庫邊界。 然而,隨著規(guī)模的增加,代碼查找變得更加困難,因為像grep這樣的標(biāo)準(zhǔn)工具基本不可用。 開發(fā)人員必須能夠探索代碼庫,找到相關(guān)的庫,并了解如何使用它們以及誰編寫它們。 庫作者經(jīng)常需要了解他們的 API 如何被使用。 這需要對代碼搜索和瀏覽工具的重大投資。Google 已經(jīng)發(fā)現(xiàn)這種投資非常有益,提高了所有開發(fā)人員的生產(chǎn)力。 [9]
訪問整個代碼庫鼓勵廣泛的代碼共享和重用。 有些人會認(rèn)為,這種模式依賴于 Google 構(gòu)建系統(tǒng)的可擴展性,使得添加依賴關(guān)系變得太容易,并且減少了軟件開發(fā)人員設(shè)計穩(wěn)定且精心設(shè)計的 API 的動機。
由于創(chuàng)建依賴關(guān)系的輕松,通常團隊不要考慮其依賴關(guān)系圖,使代碼清理更容易出錯。 不必要的依賴可能會增加項目對下游構(gòu)建破壞的風(fēng)險,導(dǎo)致二進制文件膨脹,并在構(gòu)建和測試中創(chuàng)造額外的工作。 此外,維護遺留項目會導(dǎo)致生產(chǎn)力下降。
Google 的試圖控制不必要的依賴。 已經(jīng)有工具幫助識別和刪除不需要依賴關(guān)系。 還存在用于識別未充分利用的依賴關(guān)系或識別不需要的庫的工具。 7 工具 Clipper 依賴于一個自定義的Java編譯器來生成一個精確的交叉引用索引。 然后,它使用索引構(gòu)建可達性圖,并確定從不使用什么類。 Clipper 可以通過幫助開發(fā)人員找到相對容易刪除或分解的目標(biāo)來指導(dǎo)依賴重構(gòu)的工作。
開發(fā)人員可以在一個一致的操作中,通過存儲庫中的數(shù)百或數(shù)千個文件進行重大變更。
依賴重構(gòu)和清理工具是有幫助的,但理想情況下,代碼所有者應(yīng)該能夠防止創(chuàng)建不必要的依賴關(guān)系。 2011 年,Google 開始推廣 API 可見性的概念,將新 API 的默認(rèn)可見性設(shè)置為“私有”。 這迫使開發(fā)人員明確地標(biāo)記 API,以供其他團隊使用。 從 Google 的大型代碼庫的經(jīng)驗中學(xué)到的教訓(xùn)應(yīng)該是盡快實施,以鼓勵更好的依賴結(jié)構(gòu)。
大多數(shù) Google 代碼可供所有 Google 開發(fā)人員使用,這導(dǎo)致了一種文化,一些團隊希望其他開發(fā)人員閱讀他們的代碼,而不是為他們提供單獨的 API 文檔。 這種做法有利與弊。 開發(fā)人員有時會閱讀 API 代碼,最終依賴于底層的實現(xiàn)細(xì)節(jié)。 這種行為可能會為那些不愿意向用戶暴露的細(xì)節(jié)的團隊提供一些維護負(fù)擔(dān)。
該模型還要求團隊在使用開源代碼時相互協(xié)作。 存代碼的一個區(qū)域保留用于開源代碼(在 Google 開發(fā)或外部開發(fā))。 為了防止依賴沖突,需要確保在任何給定的時間只有一個開源版本可用。 使用開源代碼的團隊在進行依賴升級時,會花時間處理新版本的開源庫。
Google 投入巨大的努力來維護代碼健康,以解決與代碼庫復(fù)雜性和依賴關(guān)系管理相關(guān)的一些問題。 例如,專用工具會自動檢測和刪除死碼,分割大量重構(gòu),并自動分配代碼評估(如通過 Rosie),并將 API 標(biāo)記為不推薦使用。 需要人力運行這些工具并管理相應(yīng)的大規(guī)模代碼更改。 審查代碼庫范圍內(nèi)的清理和其他工作引起的持續(xù)簡單重構(gòu)也會產(chǎn)生成本。
?
備擇方案
隨著像Git這樣的分布式版本控制系統(tǒng)(DVCS)的普及和使用越來越多,Google 考慮是否將Piper轉(zhuǎn)移到Git作為其主要的版本控制系統(tǒng)。 Google 的一個團隊專注于支持Git,Google 在 Google 主代碼庫之外由 Google 的 Android 和 Chrome 團隊使用。 由于外部合作伙伴和開源協(xié)作,使用 Git 對于這些團隊很重要。
Git 社區(qū)強烈建議開發(fā)人員擁有越來越多的代碼庫。 Git-clone 操作需要將所有內(nèi)容復(fù)制到本地計算機,這是與大型存儲庫不兼容的過程。 要轉(zhuǎn)移到基于 Git 的源代碼托管,有必要將 Google 的存儲庫拆分成數(shù)千個獨立的存儲庫,以實現(xiàn)合理的性能。 這樣的重組將需要 Google 開發(fā)人員的文化和工作流程更改。 作為比較,Google 的 Git 托管的 Android 代碼分為超過 800 個獨立的代碼庫。
鑒于 Google 已經(jīng)建立的現(xiàn)有工具所獲得的價值以及整體代碼庫結(jié)構(gòu)的許多優(yōu)勢,轉(zhuǎn)換到越來越多的代碼庫對于 Google 的主代碼庫來說是沒有意義的。 移動到Git或需要代碼庫拆分對 Google 來說并不引人注目。
Google 源代碼團隊目前的投資主要集中在內(nèi)部源代碼系統(tǒng)的持續(xù)可靠性,可擴展性和安全性上。 該團隊還在與Mercurial進行實驗性工作,這是一款類似Git的開源DVCS。 目標(biāo)是向Mercurial客戶端添加可伸縮性功能,以便高效地支持 Google 的規(guī)模。 這將為 Google 開發(fā)人員提供一種與單一代碼庫庫一起使用流行的DVCS風(fēng)格工作流的替代方案。 這一努力與開源的Mercurial社區(qū)合作,其中包括來自其他公司的貢獻者。
?
結(jié)論
Google 在 1999 年將現(xiàn)有的 Google 代碼庫從 CVS 遷移到 Perforce 時,選擇了單一源代碼管理策略。 早期的 Google 工程師認(rèn)為,單獨的代碼庫比多個代碼庫要嚴(yán)格得多,盡管當(dāng)時他們沒有預(yù)料到代碼庫的未來規(guī)模以及所有支持的工具。
多年來,隨著繼續(xù)擴大集中式存儲庫所需的投資增長,Google 領(lǐng)導(dǎo)層偶爾會考慮從單模模式轉(zhuǎn)變是否有意義。 盡管需要努力,但由于其優(yōu)勢,Google 選擇堅持使用集中式單一代碼庫。
源代碼管理的單一模型不適合所有人。 它最適合像 Google 這樣的組織,具有開放和協(xié)作的文化。 對于代碼庫的大部分是私有的或組之間隱藏的組織來說,這不太適用。
在 Google 方面,我們發(fā)現(xiàn)通過一些投資,源代碼管理的整體模式可以成功擴展到具有超過十億個文件,3500萬個提交和全球數(shù)千個開發(fā)者的代碼庫。 隨著 Google 和 Google 內(nèi)部項目的規(guī)模和復(fù)雜性不斷增長,我們希望本文中描述的分析和工作流程可以使他們對其代碼庫的長期結(jié)構(gòu)進行權(quán)衡決策。
?
致謝
我們希望感謝 Google 開發(fā)人員基礎(chǔ)架構(gòu)團隊的所有當(dāng)前和前任成員,他們致力于構(gòu)建和維護本文中引用的系統(tǒng),以及許多幫助審閱文章的人員; 特別是:Jon Perkins和Ingo Walther,當(dāng)前的Piper技術(shù)引領(lǐng)者;凱爾Lippincott和Crutcher Dunnavant; ?Google 的大型重構(gòu)大師Hyrum Wright; 和Chris Colohan,Caitlin Sadowski,Morgan Ames,Rob Siemborski,以及Piper和CitC開發(fā)和支持團隊,提供有見地的評論意見。
1. Bloch, D.?Still All on One Server: Perforce at Scale. Google White Paper, 2011;?http://info.perforce.com/rs/perforce/images/GoogleWhitePaper-StillAllonOneServer-PerforceatScale.pdf
2. Chang, F., Dean, J., Ghemawat, S., Hsieh, W.C., Wallach, D.A., Burrows, M., Chandra, T., Fikes, A., and Gruber, R.E. Bigtable: A distributed storage system for structured data.?ACM Transactions on Computer Systems 26, 2 (June 2008).
3. Corbett, J.C., Dean, J., Epstein, M., Fikes, A., Frost, C., Furman, J., Ghemawat, S., Gubarev, A., Heiser, C., Hochschild, P. et al. Spanner: Google's globally distributed database.?ACM Transactions on Computer Systems 31, 3 (Aug. 2013).
4. Gabriel, R.P., Northrop, L., Schmidt, D.C., and Sullivan, K. Ultra-large-scale systems. In?Companion to the 21st ACM SIGPLAN Symposium on Object-Oriented Programming Systems, Languages, and Applications(Portland, OR, Oct. 22-26). ACM Press, New York, 2006, 632–634.
5. Kemper, C. Build in the Cloud: How the Build System works. Google Engineering Tools blog post, 2011;?http://google-engtools.blogspot.com/2011/08/build-in-cloud-how-build-system-works.html
6. Lamport, L. Paxos made simple.?ACM Sigact News 32, 4 (Nov. 2001), 18–25.
7. Morgenthaler, J.D., Gridnev, M., Sauciuc, R., and Bhansali, S. Searching for build debt: Experiences managing technical debt at Google. In?Proceedings of the Third International Workshop on Managing Technical Debt?(Zürich, Switzerland, June 2-9). IEEE Press Piscataway, NJ, 2012, 1–6.
8. Ren, G., Tune, E., Moseley, T., Shi, Y., Rus, S., and Hundt, R. Google-wide profiling: A continuous profiling infrastructure for data centers.?IEEE Micro 30, 4 (2010), 65–79.
9. Sadowski, C., Stolee, K., and Elbaum, S. How developers search for code: A case study. In?Proceedings of the 10th?Joint Meeting on Foundations of Software Engineering?(Bergamo, Italy, Aug. 30-Sept. 4). ACM Press, New York, 2015, 191–201.
10. Sadowski, C., van Gogh, J., Jaspan, C., Soederberg, E., and Winter, C. Tricorder: Building a program analysis ecosystem. In?Proceedings of the 37th?International Conference on Software Engineering, Vol. 1(Firenze, Italy, May 16-24). IEEE Press Piscataway, NJ, 2015, 598–608.
11. Wasserman, L. Scalable, example-based refactorings with Refaster. In?Proceedings of the 2013 ACM Workshop on Refactoring Tools?(Indianapolis, IN, Oct. 26-31). ACM Press, New York, 2013, 25–28.
12. Wikipedia. Dependency hell. Accessed Jan. 20, 2015;?http://en.wikipedia.org/w/index.php?title=Dependency_hell&oldid=634636715
13. Wikipedia. Filesystem in userspace. Accessed June, 4, 2015;?http://en.wikipedia.org/w/index.php?title=Filesystem_in_Userspace&oldid=664776514
14. Wikipedia. Linux kernel. Accessed Jan. 20, 2015;?http://en.wikipedia.org/w/index.php?title=Linux_kernel&oldid=643170399
15. Wright, H.K., Jasper, D., Klimek, M., Carruth, C., and Wan, Z. Large-scale automated refactoring using ClangMR. In?Proceedings of the IEEE International Conference on Software Maintenance?(Eindhoven, The Netherlands, Sept. 22-28). IEEE Press, 2013, 548–551.
?
英文原文:https://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext
總結(jié)
以上是生活随笔為你收集整理的为什么Google上十亿行代码都放在同一个仓库里?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 区块链及比特币入门指南
- 下一篇: 深入了解 gRPC:协议