【轉(zhuǎn)】5 分鐘搞懂 Monorepo

由于谷歌在 Monorepo 上的實(shí)踐,Monorepo 受到了越來越多的關(guān)注。Monorepo 意味著把所有項(xiàng)目的所有代碼統(tǒng)一維護(hù)在一個(gè)單一的代碼版本庫中,和多代碼庫方案相比,兩者各有優(yōu)劣,需要根據(jù)公司文化和產(chǎn)品特性進(jìn)行取舍。原文:What is monorepo? (and should you use it?)[1]

Monorepos(單一代碼庫)有助于加快開發(fā)工作流程,在本文中,我們將幫助你認(rèn)識(shí)這一代碼組織模型是否適合你的團(tuán)隊(duì)和公司。

什么是 monorepo?

Monorepo 的意思是在版本控制系統(tǒng)的單個(gè)代碼庫里包含了許多項(xiàng)目的代碼。這些項(xiàng)目雖然有可能是相關(guān)的,但通常在邏輯上是獨(dú)立的,并由不同的團(tuán)隊(duì)維護(hù)。

有些公司將所有代碼存儲(chǔ)在一個(gè)代碼庫中,由所有人共享,因此 Monorepos 可以非常大。例如,理論上谷歌擁有有史以來最大的代碼庫,每天有成百上千次提交,整個(gè)代碼庫超過 80 TB。其他已知運(yùn)營大型單一代碼庫的公司還有微軟、Facebook 和 Twitter。

Monorepos 有時(shí)被稱為單體代碼庫(monolithic repositories),但不應(yīng)該與單體架構(gòu)(monolithic architecture)相混淆,單體架構(gòu)是一種用于編寫自包含應(yīng)用程序的軟件開發(fā)實(shí)踐。這方面的一個(gè)例子就是 Ruby on Rails,它可以處理 Web、API 和后端工作。

單一代碼庫(monorepos) vs 多代碼庫(multirepos)

與單一代碼庫相反的是多代碼庫(multirepos),每個(gè)項(xiàng)目都儲(chǔ)存在一個(gè)完全獨(dú)立的、版本控制的代碼庫中。多代碼庫是很自然的選擇——我們大多數(shù)人在開始一個(gè)新項(xiàng)目時(shí)都愿意開一個(gè)新的代碼庫,畢竟,誰都喜歡從 0 開始.

從多代碼庫到單一代碼庫的變化就意味著將所有項(xiàng)目移到一個(gè)代碼庫中。


當(dāng)然,這只是開始。當(dāng)我們開始重構(gòu)和整合時(shí),困難的工作就來了。

$ mkdir monorepo

復(fù)制代碼

多代碼庫不是微服務(wù)(microservices)的同義詞,兩者之間并沒有耦合關(guān)系。事實(shí)上,我們稍后將討論將單一代碼庫和微服務(wù)結(jié)合起來的例子。只要仔細(xì)設(shè)置用于部署的 CI/CD 流水線[2],單一代碼庫就可以托管任意數(shù)量的微服務(wù)。

單一代碼庫的好處

乍一看,單一代碼庫和多代碼庫之間的選擇似乎不是什么大問題,但這是一個(gè)會(huì)深刻影響到公司開發(fā)流程的決定。至于單一代碼庫的好處,可以列舉如下:

  • 可見性(Visibility):每個(gè)人都可以看到其他人的代碼,這樣可以帶來更好的協(xié)作和跨團(tuán)隊(duì)貢獻(xiàn)——不同團(tuán)隊(duì)的開發(fā)人員都可以修復(fù)代碼中的 bug,而你甚至都不知道這個(gè) bug 的存在。

  • 更簡單的依賴關(guān)系管理(Simpler dependency management):共享依賴關(guān)系很簡單,因?yàn)樗心K都托管在同一個(gè)存儲(chǔ)庫中,因此都不需要包管理器。

  • 唯一依賴源(Single source of truth):每個(gè)依賴只有一個(gè)版本,意味著沒有版本沖突,沒有依賴地獄。

  • 一致性(Consistency):當(dāng)你把所有代碼庫放在一個(gè)地方時(shí),執(zhí)行代碼質(zhì)量標(biāo)準(zhǔn)和統(tǒng)一的風(fēng)格會(huì)更容易。

  • 共享時(shí)間線(Shared timeline):API 或共享庫的變更會(huì)立即被暴露出來,迫使不同團(tuán)隊(duì)提前溝通合作,每個(gè)人都得努力跟上變化。

  • 原子提交(Atomic commits):原子提交使大規(guī)模重構(gòu)更容易,開發(fā)人員可以在一次提交中更新多個(gè)包或項(xiàng)目。

  • 隱式 CI(Implicit CI):因?yàn)樗写a已經(jīng)統(tǒng)一維護(hù)在一個(gè)地方,因此可以保證持續(xù)集成[3]。

  • 統(tǒng)一的 CI/CD(Unified CI/CD):可以為代碼庫中的每個(gè)項(xiàng)目使用相同的 CI/CD[4]部署流程。

  • 統(tǒng)一的構(gòu)建流程(Unified build process):代碼庫中的每個(gè)應(yīng)用程序可以共享一致的構(gòu)建流程[5]

單一代碼庫的缺陷

隨著單一代碼庫的發(fā)展,我們?cè)诎姹究刂乒ぞ摺?gòu)建系統(tǒng)和持續(xù)集成流水線方面達(dá)到了設(shè)計(jì)極限。這些問題可能會(huì)讓一家公司走上多代碼庫的道路:

  • 性能差(Bad performance):單一代碼庫難以擴(kuò)大規(guī)模,像 git blame 這樣的命令可能會(huì)不合理的花費(fèi)很長時(shí)間執(zhí)行,IDE 也開始變得緩慢,生產(chǎn)力受到影響,對(duì)每個(gè)提交測試整個(gè) repo 變得不可行。

  • 破壞主線(Broken main/master):主線損壞會(huì)影響到在單一代碼庫中工作的每個(gè)人,這既可以被看作是災(zāi)難,也可以看作是保證測試既可以保持簡潔又可以跟上開發(fā)的好機(jī)會(huì)。

  • 學(xué)習(xí)曲線(Learning curve):如果代碼庫包含了許多緊密耦合的項(xiàng)目,那么新成員的學(xué)習(xí)曲線會(huì)更陡峭。

  • 大量的數(shù)據(jù)(Large volumes of data):單一代碼庫每天都要處理大量的數(shù)據(jù)和提交。

  • 所有權(quán)(Ownership):維護(hù)文件的所有權(quán)更有挑戰(zhàn)性,因?yàn)橄?Git 或 Mercurial 這樣的系統(tǒng)沒有內(nèi)置的目錄權(quán)限。

  • Code reviews:通知可能會(huì)變得非常嘈雜。例如,GitHub 有有限的通知設(shè)置,不適合大量的 pull request 和 code review。

你可能已經(jīng)注意到,這些問題中的大多數(shù)都和技術(shù)有關(guān)。在下面的章節(jié)中,我們將了解堅(jiān)持使用單一代碼庫的公司是如何通過投資工具、添加集成以及編寫定制解決方案來解決大部分問題的。

這不僅僅是技術(shù)問題

選擇代碼庫策略不僅是一個(gè)技術(shù)問題,也是關(guān)于人們?nèi)绾谓涣鞯膯栴}。正如康威定律所言,溝通對(duì)于創(chuàng)造偉大的產(chǎn)品至關(guān)重要:

任何組織所設(shè)計(jì)的系統(tǒng)(此處的定義比信息系統(tǒng)寬泛得多)架構(gòu),都不可避免的反映為該組織溝通結(jié)構(gòu)的副本?!低?/strong>

雖然多代碼倉庫允許每個(gè)團(tuán)隊(duì)獨(dú)立管理他們的項(xiàng)目,但同時(shí)也阻礙了協(xié)作。它們就像眼罩一樣,讓開發(fā)人員只關(guān)注自己所擁有的部分,而忽略了整體。

另一方面,單一代碼庫就像一個(gè)樞紐、一個(gè)廣場,每個(gè)開發(fā)人員、工程師、測試人員和業(yè)務(wù)分析師都可以在這里會(huì)面和交談。單一代碼庫鼓勵(lì)對(duì)話,幫助我們消除“豎井”。

Monorepo 文化

Monorepos 已經(jīng)存在很長時(shí)間了。三十年來,F(xiàn)reeBSD 一直使用 CVS 和后來的 subversion monorepos[6]進(jìn)行開發(fā)和包分發(fā)。

許多開源項(xiàng)目已經(jīng)成功使用了單一代碼庫。例如:

  • Laravel[7]:一個(gè)用于 Web 開發(fā)的 PHP 框架。

  • Symfony[8]:一個(gè)用 PHP 編寫的 MVC 框架。有趣的是,他們已經(jīng)為每個(gè) Symfony 工具和庫創(chuàng)建了只讀存儲(chǔ)庫,這種方法被稱為分庫(split-repo)。

  • NixOS[9]:一個(gè)用單一代碼庫發(fā)布包的 Linux 發(fā)行版

  • Babel[10]:一個(gè)用戶 Web 開發(fā)的流行的 JavaScript 編譯器,其單一代碼庫包含了完整的項(xiàng)目及其所有插件。

  • 此外,React[11]、Ember[12]和 Meteor[13]等前端框架都使用單一代碼庫。

然而,真正的問題是商業(yè)軟件是否能從單一代碼庫中獲益??紤]到這些優(yōu)點(diǎn)和缺點(diǎn),讓我們來看一些已經(jīng)嘗試過的公司的經(jīng)驗(yàn)。

Segment,告別多代碼庫

Alex Noonan 講述了一個(gè)關(guān)于告別多代碼庫的故事[14]。她所在的公司 Segment.com 提供活動(dòng)收集和轉(zhuǎn)發(fā)服務(wù),每個(gè)客戶都需要使用一種特殊格式的數(shù)據(jù)。因此,工程團(tuán)隊(duì)最初決定混合使用微服務(wù)和多代碼庫。

這一策略效果很好——隨著客戶基數(shù)的增長,他們擴(kuò)大了規(guī)模,沒有出現(xiàn)問題。但是,當(dāng)轉(zhuǎn)發(fā)目的地的數(shù)量超過 100 個(gè)時(shí),事情開始變得糟糕起來。維護(hù)、測試和部署超過 140 個(gè)代碼庫(每個(gè)代碼庫都有數(shù)百個(gè)日益分化的依賴關(guān)系)的管理負(fù)擔(dān)太高了。

“最終,團(tuán)隊(duì)發(fā)現(xiàn)他們無法取得進(jìn)展,三個(gè)全職工程師花費(fèi)了大部分時(shí)間來維持系統(tǒng)的運(yùn)行?!?/strong>

對(duì)于 Segment 來說,補(bǔ)救辦法就是合并,將所有的服務(wù)和依賴遷移到一個(gè)單一代碼庫中。他們必須協(xié)調(diào)共享庫并且測試所有內(nèi)容,雖然花了很大的代價(jià),但遷移非常成功,最終的結(jié)果是降低了復(fù)雜性,增加了可維護(hù)性。

“更快的速度就是證據(jù)。[…] 我們?cè)谶^去 6 個(gè)月里對(duì)庫的改進(jìn)比 2016 年全年都要多?!?/strong>

多年后,當(dāng)一個(gè)小組詢問她在微服務(wù)方面的經(jīng)驗(yàn)時(shí)[15],Alex 解釋了她遷移到單一代碼庫的原因:

“這并沒有像我們想象的那樣成為我們的優(yōu)勢。我們改變的主要?jiǎng)訖C(jī)是因?yàn)槭〉臏y試會(huì)影響到不同的東西。 [..] 把它們分成單獨(dú)的代碼庫只會(huì)讓情況變得更糟,因?yàn)橛锌赡苣銜?huì)過了 6 個(gè)月才集成某個(gè)庫的更新,結(jié)果測試完全被破壞了,而你又不想花時(shí)間去修復(fù)。我見過的成功分解為獨(dú)立代碼庫而不是服務(wù)的案例之一是,當(dāng)我們有一塊代碼在多個(gè)服務(wù)之間共享時(shí),我們想讓它成為一個(gè)共享庫。除此之外,我們發(fā)現(xiàn),即使轉(zhuǎn)向微服務(wù),我們?nèi)匀桓矚g單一代碼庫。”

Airbnb 和 monorail

Airbnb 的基礎(chǔ)設(shè)施工程師延斯·范德海格(Jens Vanderhaeghe)也講述了微服務(wù)和單一代碼庫是如何幫助他們向全球擴(kuò)張的[16]。

Airbnb 最初的版本被稱為“monorail”,是一個(gè)獨(dú)立的 Ruby on Rails 應(yīng)用程序。當(dāng)公司開始指數(shù)級(jí)增長時(shí),代碼庫也隨之增長。當(dāng)時(shí),Airbnb 實(shí)施了一項(xiàng)名為“民主發(fā)布(democratic releases)”的新發(fā)布政策,意味著任何開發(fā)者都可以在任何時(shí)候發(fā)布產(chǎn)品。

隨著 Airbnb 的擴(kuò)張,民主程序的限制也受到了考驗(yàn),合并更改變得越來越困難。Jens 的團(tuán)隊(duì)實(shí)施了一些緩解措施,比如合并隊(duì)列和增強(qiáng)監(jiān)控。這在一段時(shí)間內(nèi)有幫助,但從長遠(yuǎn)來看還是不夠。

Airbnb 的工程師們?yōu)榫S持 monorail 系統(tǒng)進(jìn)行了英勇的斗爭,但最終,經(jīng)過數(shù)周的辯論,他們決定將該應(yīng)用分割為多個(gè)微服務(wù)。因此,他們創(chuàng)建了兩個(gè)單一代碼庫:一個(gè)用于前端,一個(gè)用于后端。兩者都包含數(shù)百個(gè)服務(wù)、文檔、用于部署的 Terraform 和 Kubernetes 資源以及所有維護(hù)工具。

當(dāng)被問及單一代碼庫的優(yōu)勢時(shí),Jens 說道:

“我們不想處理所有這些微服務(wù)之間的版本依賴關(guān)系。[使用單一代碼庫]你可以通過一個(gè)提交在兩個(gè)微服務(wù)之間做出改變。[..] 我們可以圍繞一個(gè)代碼庫構(gòu)建所有工具。最大的賣點(diǎn)是你可以同時(shí)在多個(gè)微服務(wù)上做出改變。我們通過腳本檢測單一代碼庫中的哪些應(yīng)用程序會(huì)受到影響,然后部署這些應(yīng)用程序。我們獲得的主要好處是源代碼控制。”

Uber,來來回回

來自優(yōu)步(Uber)的艾米?盧西多(Aimee Lucido)描述了從單一代碼庫到多代碼庫再切換回來的過程[17]。

當(dāng)時(shí),她正在 Android 客戶團(tuán)隊(duì)工作。他們從一開始就使用單一代碼庫,但是經(jīng)過 5 年的快速發(fā)展,單一代碼庫的問題開始顯現(xiàn)出來。

“我們開始遭遇可怕的 IDE 死鎖,我們甚至不能在 Android Studio 中翻看代碼,否則 IDE 就會(huì)沒有任何反應(yīng)?!?/strong>

問題并不只是發(fā)生在 IDE,也影響到 Git,構(gòu)建過程一拖再拖。更糟糕的是,他們經(jīng)常會(huì)破壞主線,這讓他們無法構(gòu)建任何東西。

“公司規(guī)模越大,就越頻繁地遇到主線被破壞的情況?!?/strong>

當(dāng) Uber 達(dá)到中等規(guī)模時(shí),團(tuán)隊(duì)決定切換到多代碼庫,這解決了很多問題。Uber 的工程師們喜歡這樣的狀態(tài):他們可以擁有一部分代碼,并且只對(duì)這部分代碼負(fù)責(zé)。

“如果你只構(gòu)建地圖應(yīng)用,那么你可以很快構(gòu)建出來,這太棒了?!?/strong>

但故事并沒有到此結(jié)束。又過了一段時(shí)間,多代碼庫策略開始顯示出它的弱點(diǎn)。這一次不僅僅是關(guān)于技術(shù)問題,而是關(guān)于人們?nèi)绾魏献?。團(tuán)隊(duì)被分解成多個(gè)豎井,管理數(shù)千個(gè)代碼庫的開銷消耗了大量寶貴的時(shí)間。

每個(gè)團(tuán)隊(duì)都有自己的編碼風(fēng)格、框架和測試實(shí)踐。管理依賴關(guān)系也變得更加困難,依賴關(guān)系的惡魔抬起了它丑陋的頭,這使得最終將所有內(nèi)容整合到一個(gè)產(chǎn)品中變得非常困難。

就在這個(gè)時(shí)候,Uber 的工程師們?cè)俅魏献鳎瑳Q定再給單一代碼庫一次機(jī)會(huì)。有了更多的資源并且提前知道他們將面臨的問題,他們選擇投資于工具:他們修改了 IDE,實(shí)現(xiàn)了合并隊(duì)列,并使用增量構(gòu)建來加快部署。

“當(dāng)你發(fā)展到一個(gè)大公司的規(guī)模時(shí),可以投入資源,讓你的大公司感覺像一個(gè)小公司,把缺點(diǎn)變成優(yōu)點(diǎn)。”

Pinterest,全力投資單一代碼庫

讓我們以一家正在經(jīng)歷三年轉(zhuǎn)型的公司作為總結(jié):Pinterest。他們的努力包括了兩個(gè)方面。首先,將 1300 多個(gè)代碼庫切換到四個(gè)單一代碼庫中。其次,將數(shù)百個(gè)依賴項(xiàng)整合到一個(gè)完整的 Web 應(yīng)用程序中。

他們?yōu)槭裁匆@么做?Eden JnBaptiste[18]解釋說,多代碼庫使得他們很難重用代碼。情況是一樣的:代碼太過分散,每個(gè)團(tuán)隊(duì)都有自己的倉庫,有自己的風(fēng)格和結(jié)構(gòu)。構(gòu)建過程質(zhì)量標(biāo)準(zhǔn)是高度可變的,構(gòu)建和部署變得非常困難。

Pinterest 發(fā)現(xiàn),基于主干開發(fā)[19]配合單一代碼庫有助于取得進(jìn)展?;谥鞲傻拈_發(fā)的基礎(chǔ)是只使用臨時(shí)分支,盡可能頻繁的合并到主分支上,從而減少合并沖突的機(jī)會(huì)。

“將所有代碼放在一個(gè)倉庫中有助于我們減少(構(gòu)建系統(tǒng)中的)反饋循環(huán)。”

對(duì)于 Pinterest,單一代碼庫提供了一致的開發(fā)工作流。發(fā)布實(shí)踐的自動(dòng)化、簡化和標(biāo)準(zhǔn)化允許他們減少文檔,讓開發(fā)人員專注于編寫代碼。

投資于工具

如果我們必須從所有這些故事中吸取一個(gè)教訓(xùn),那就是正確的工具是有效的單一代碼庫的關(guān)鍵——需要重寫思考構(gòu)建和測試。我們可以使用智能構(gòu)建系統(tǒng),而不是在每次更新時(shí)重新構(gòu)建完整的倉庫,它需要了解項(xiàng)目結(jié)構(gòu),并且只對(duì)自上次提交以來變更的部分進(jìn)行操作。

[圖片上傳失敗...(image-20f388-1671887905137)]

我們大多數(shù)人都沒有谷歌或 Facebook 的資源。那我們能做什么呢?幸運(yùn)的是,許多大公司的構(gòu)建系統(tǒng)都是開源的:

  • Bazel[20]:由谷歌發(fā)布,部分基于他們自己的構(gòu)建系統(tǒng)(Blaze)。Bazel 支持多種語言,并支持大規(guī)模構(gòu)建和測試。

  • Buck[21]: Facebook 開源的快速構(gòu)建系統(tǒng),支持在多種語言和平臺(tái)上進(jìn)行不同的構(gòu)建。

  • Pands[22]:Pands 構(gòu)建系統(tǒng)是與 Twitter、Foursquare 和 Square 合作創(chuàng)建的。目前只支持 Python,后續(xù)還將支持更多的語言。

  • RushJS[23]:微軟用于 JavaScript 的可擴(kuò)展的單一代碼庫管理器,能夠從一個(gè)代碼庫構(gòu)建和部署多個(gè)包。

Monorepos 正在獲得越來越多的關(guān)注,尤其是在 JavaScript 方面,如下項(xiàng)目所示:

  • Lerna[24]:JavaScript 的單一代碼庫管理器,可以與 React、Angular 或 Babel 等流行框架集成。

  • Yarn workspace[25]:用一個(gè)命令在多個(gè)地方安裝和更新 Node.js 的依賴項(xiàng)。

  • ultra-runner[26]:JavaScript 單一代碼庫管理腳本,支持 Yarn、pnpm 和 Lerna 插件,支持并行構(gòu)建。

  • Monorepo builder[27]:通過單一代碼庫安裝和更新 PHP 包。

擴(kuò)展代碼庫

源代碼控制是單一代碼庫的另一個(gè)痛點(diǎn),這些工具可以幫助您擴(kuò)展存儲(chǔ)庫:

  • Virtual Filesystem for Git(VFS)[28]:為 Git 添加流支持。VFS 可以根據(jù)需要從 Git 倉庫下載對(duì)象。這個(gè)項(xiàng)目最初是為了管理 Windows 代碼庫(最大的 Git 倉庫)而創(chuàng)建的,只能在 Windows 上使用,但已經(jīng)宣布要支持 MacOS。

  • Large File Storage[29]:Git 的開源擴(kuò)展,為大文件提供了更好的支持。安裝了這個(gè)擴(kuò)展,就可以跟蹤任何類型的文件,并將它們無縫的上傳到云存儲(chǔ)中,從而釋放代碼庫的空間,使 push 和 pull 的速度更快。

  • Mercurial[30]:Git 的替代方案,Mercurial 是一個(gè)分布式版本控制工具,專注于速度。Facebook 使用 Mercurial,多年來已經(jīng)發(fā)布了許多提高速度的補(bǔ)丁[31]。

  • Git CODEOWNERS[32]:允許定義哪個(gè)團(tuán)隊(duì)擁有代碼庫中的子目錄,當(dāng)有人提交 pull request 或 push 代碼到受保護(hù)的分支時(shí),代碼所有者會(huì)自動(dòng)被要求進(jìn)行審查。GitHub 和 GitLab 都支持這一特性。

Monorepo 管理最佳實(shí)踐

基于這些單一代碼庫的故事,我們可以定義一套最佳實(shí)踐:

  • 定義一個(gè)便于探索的統(tǒng)一的目錄組織。

  • 維護(hù)分支整潔,保持較小的分支,考慮采用基于主干的開發(fā)。

  • 為每個(gè)項(xiàng)目使用固定的依賴項(xiàng),一次升級(jí)所有依賴項(xiàng),迫使每個(gè)項(xiàng)目都跟上依賴項(xiàng)。只為真正的例外情況保留例外。

  • 如果正在使用 Git,學(xué)習(xí)如何使用 shallow clone[33]和 filter-branch[34]來處理大容量代碼庫。

  • 貨比三家,尋找像 Bazel 或 Buck 這樣的智能構(gòu)建系統(tǒng),以加速構(gòu)建和測試。

  • 如果需要限制對(duì)某些項(xiàng)目的訪問,請(qǐng)使用 CODEOWERS。

  • 使用 Semaphore[35]等 CI/CD 云平臺(tái)來大規(guī)模測試和部署應(yīng)用程序。

你應(yīng)該使用單一代碼庫嗎?

看情況而定,沒有適合所有用例的答案,有些公司可能會(huì)暫時(shí)選擇單一代碼庫,然后決定需要轉(zhuǎn)向多代碼庫,或者相反,而其他人可能會(huì)選擇混合代碼庫。當(dāng)你有疑問的時(shí)候,考慮一下從單一代碼庫到多代碼庫通常比反過來要更容易。但千萬不要忽視這一點(diǎn),最終,這與技術(shù)無關(guān),而是與工作文化和溝通有關(guān)。所以,根據(jù)你想要的工作方式來做決定。

References:

[1] https://semaphoreci.com/blog/what-is-monorepo

[2] https://semaphoreci.com/blog/cicd-pipeline

[3] https://semaphoreci.com/continuous-integration

[4] https://semaphoreci.com/cicd

[5] https://semaphoreci.com/blog/build-stage

[6] https://docs.freebsd.org/en_US.ISO8859-1/articles/committers-guide/article.html

[7] https://laravel.com/

[8] https://symfony.com/

[9] https://github.com/NixOS/nixpkgs/

[10] https://github.com/babel/babel/blob/master/doc/design/monorepo.md

[11] https://github.com/facebook/react/tree/master/packages

[12] https://github.com/emberjs/ember.js/tree/master/packages

[13] https://github.com/meteor/meteor/tree/devel/packages

[14] https://segment.com/blog/goodbye-microservices/

[15] https://www.infoq.com/articles/microservices-from-trenches-lessons-challenges/

[16] https://www.youtube.com/watch?v=sakGeE4xVZs

[17] https://www.youtube.com/watch?v=lV8-1S28ycM

[18] https://www.youtube.com/watch?v=r5KHQnS6uP8

[19] https://trunkbaseddevelopment.com/

[20] https://bazel.build/

[21] https://buck.build/

[22] http://www.pantsbuild.org/

[23] https://rushjs.io/

[24] https://github.com/lerna/lerna

[25] https://classic.yarnpkg.com/en/docs/workspaces/

[26] https://github.com/folke/ultra-runner

[27] https://github.com/Symplify/MonorepoBuilder

[28] https://vfsforgit.org/

[29] https://git-lfs.github.com/

[30] https://www.mercurial-scm.org/

[31] https://engineering.fb.com/2014/01/07/core-data/scaling-mercurial-at-facebook/

[32] https://help.github.com/articles/about-codeowners/

[33] https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/

[34] https://git-scm.com/docs/git-filter-branch

[35] https://semaphoreci.com/

原文鏈接:【https://xie.infoq.cn/article/4f870ba6a7c8e0fd825295c92】。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言:作用是將應(yīng)用層序的請(qǐng)求傳遞給硬件,并充當(dāng)?shù)讓域?qū)動(dòng)程序,對(duì)系統(tǒng)中的各種設(shè)備和組件進(jìn)行尋址。目前支持模塊的動(dòng)態(tài)裝...
    玩轉(zhuǎn)Linux內(nèi)核閱讀 371評(píng)論 0 0
  • 2019 9月 [工具] TreeJS 在線編輯器: https://threejs.org/editor/[ht...
    halber閱讀 2,256評(píng)論 0 2
  • 所有的系統(tǒng) I/O 都分為兩個(gè)階段:等待就緒和操作.讀就是等待系統(tǒng)可讀和真正的讀;寫就是等待系統(tǒng)可寫和真正的寫. ...
    Java架構(gòu)師閱讀 328評(píng)論 0 0
  • 本期Jesse請(qǐng)到了CnosDB的實(shí)習(xí)生肖明浩,來和大家聊聊他的實(shí)習(xí)心的體會(huì)。肖同學(xué)本科畢業(yè)于北京理工大學(xué),研究生...
    CnosDB閱讀 389評(píng)論 0 0
  • 各位小伙伴們我們又見面了,伴隨著區(qū)塊鏈技術(shù)的推廣,很多企業(yè)也在思考是否將其應(yīng)用。其實(shí)TSDB與區(qū)塊鏈有很多共通之處...
    CnosDB閱讀 268評(píng)論 0 0

友情鏈接更多精彩內(nèi)容