開發者該用單一代碼庫還是多代碼庫管理代碼?
譯文如果你負責組織的代碼庫架構,那么如何以可擴展的方式管理這種增長,這個問題早晚要應對。有兩種常見的架構方案可供選擇:
一種是“多代碼庫”架構,我們將代碼庫分成越來越多的小代碼庫,以小團隊或項目為邊界。另一種是“單一代碼庫”,即維護一個不斷變大的大型代碼庫,內含許多項目和庫的代碼,多個團隊圍繞該代碼庫進行協作。
多代碼庫方案看起來容易實現,最初可能很誘人,我們只要在需要時創建更多的代碼庫就好了!我們幾乎不需要任何特殊工具,且可以讓每個團隊在管理代碼方面擁有更大的自主權。
遺憾的是,實際上,多代碼庫架構常常導致代碼庫脆弱、不一致且難以更改。這進而助長了工程部門本身的孤島現象。相比之下,單一代碼庫方案反而是一種更好、更靈活、更具協作性的長期擴展解決方案。
為什么會這樣?代碼庫架構中的難點涉及在有依賴項的情況下管理代碼更改,反之亦然。而在多代碼庫架構中,代碼庫通過已發布的版本化工件(artifact),使用來自其他代碼庫的代碼;這樣一來,更改傳遞愈發困難。
具體來說,當我們作為代碼庫A的所有者需要對所使用的代碼庫B進行一些更改時,會發生什么?首先,我們得找到代碼庫B的看門人,說服他們接受并發布新版本的更改。然后,在理想情況下,有人會找到代碼庫B的所有其他使用者,將它們升級到這個新版本,然后重新發布。現在我們必須找到那些初始使用者的使用者,升級并針對新版本重新發布等等,循環往復,不厭其煩。
但誰來做所有這些工作?他們將如何找到所有這些使用者?畢竟,依賴項元數據駐留在使用者端,而不是被使用者端,沒有簡單的方法來回溯依賴項。當一個問題的歸屬并不直接、解決方案又不明顯時,往往會被忽略,因此實際上不會出現回溯工作。
短時間內可能沒什么關系,因為其他代碼庫固定到了早期版本的依賴項。但這種舒適狀態很難長久,因為遲早有些使用者會被集成到一個可部署的工件中,到時就不得不有人為該工件選擇單一版本的依賴項。因此,我們最終會遇到由一個過去的團隊引起的傳遞版本沖突,它就像埋入代碼庫的定時炸彈,如果另外某個團隊需要將代碼集成到生產環境中,它會引爆炸毀。
如果這個問題很眼熟,那是由于它是臭名昭著的“依賴地獄”(dependency hell)問題的內部版本,該問題通常困擾著代碼庫的外部依賴項。在多代碼庫架構中,第一方依賴項技術上來說被視為第三方依賴項,即使它們碰巧由同一家組織編寫和擁有。因此,如果是多代碼庫架構,我們基本上選擇了面臨大規模擴展的依賴地獄。
單一代碼庫與之形成了對比:所有使用者都在同一源代碼樹中,因此找到它們就像使用grep一樣簡單。又由于沒有發布這一步,所有代碼共享一個版本(由當前提交表示),因此以傳遞且同步地更新使用者流程上簡單直觀。如果我們的測試覆蓋率良好,就可以清楚地知道我們什么時候做對了。
當然,“簡單直觀”與“容易”不一樣:同步升級代碼庫本身可能并非易事。但這就是代碼更改的本質。沒有哪種代碼庫架構可以消除工程問題中不能化簡的部分。但是單一代碼庫現在至少強制我們把必要的難題處理掉,也就不會在以后造成不必要的困難。
多代碼庫架構在未來往往將依賴地獄外化到其他人身上,這是與康威定律(Conway’s Law)相關的一個更寬泛的問題:“任何設計系統的組織都會形成這樣一種設計,其結構與組織溝通結構亦步亦趨”。反過來也大致如此:組織的結構往往是這種溝通所圍繞架構的副本。在這種情況下,分散的代碼庫架構會促進工程部門本身的分裂,代碼庫設計最終讓組織各自把關和職責分散,而不是齊心實現共同目標,因為這些共同目標沒有在架構上加以表示。單一代碼庫支持并能以溫和的方式實現組織統一:每個人都在單一代碼庫方面進行協作,這正是組織成功構建統一產品所需要的溝通渠道。
單一代碼庫并不是靈丹妙藥。它也確實需要合適的工具和流程來保持性能規模化和工程有效性。但借助合適的架構和工具,你就可以確保代碼庫和組織各自有機統一,以規模化表現蒸蒸日上。
原文標題:The monorepo approach to code management,作者:Benjy Weinberger