源代碼分支管理模式

現(xiàn)代的源代碼控制系統(tǒng)提供了強(qiáng)大的工具匣距,可以非常輕松的在源代碼上創(chuàng)建分支。但最終分支還是要合并在一起,許多團(tuán)隊(duì)不得不花相當(dāng)多的時(shí)間去處理相互糾纏的分支柔袁。這里有幾種模式讓團(tuán)隊(duì)可以有效地使用分支唧喉,專(zhuān)注于集成多個(gè)開(kāi)發(fā)人員的工作并組織產(chǎn)品發(fā)布的路線(xiàn)捣卤。最重要的一點(diǎn)忍抽,分支應(yīng)該頻繁集成,盡力保持一個(gè)無(wú)需過(guò)多干預(yù)就可部署生產(chǎn)的健康主線(xiàn)董朝。

原作者:Martin Fowler
原文地址:https://martinfowler.com/articles/branching-patterns.html

對(duì)任何軟件開(kāi)發(fā)團(tuán)隊(duì)來(lái)說(shuō)鸠项,源代碼都是重要的資產(chǎn)。幾十年來(lái)子姜,已有一系列源代碼管理工具被開(kāi)發(fā)出來(lái)祟绊,用于維護(hù)代碼。這些工具可以跟蹤變更闲询,因此我們可以恢復(fù)軟件的歷史版本并查看它的演進(jìn)過(guò)程久免。這些工具還是開(kāi)發(fā)團(tuán)隊(duì)的協(xié)作中心,團(tuán)隊(duì)中的所有程序員都在一個(gè)公共的代碼庫(kù)上工作扭弧。通過(guò)記錄每位開(kāi)發(fā)人員所做的更改阎姥,這些系統(tǒng)可以一次跟蹤多行工作內(nèi)容,并幫助開(kāi)發(fā)人員解決如何把這些內(nèi)容合并到一起鸽捻。

將開(kāi)發(fā)活動(dòng)劃分為分解和合并的工作流呼巴,是軟件開(kāi)發(fā)團(tuán)隊(duì)工作流程的核心,并且已演化出多種模式幫助我們處理所有這些活動(dòng)御蒲。像大多數(shù)軟件模式一樣衣赶,幾乎沒(méi)有哪種模式是所有團(tuán)隊(duì)都應(yīng)遵循的黃金法則。軟件開(kāi)發(fā)工作流程依賴(lài)于具體環(huán)境厚满,特別是團(tuán)隊(duì)的社會(huì)結(jié)構(gòu)和團(tuán)隊(duì)遵循的其他實(shí)踐府瞄。

本文將詳述這些模式,并在模式描述中夾雜可以更好地說(shuō)明模式背景和相互關(guān)系的敘事部分碘箍。為便于區(qū)分遵馆,模式描述的章節(jié)將附以圖標(biāo)“?”。

基本模式

在思考這些代碼分支模式時(shí)丰榴,我發(fā)現(xiàn)它們可以分為兩大類(lèi)货邓。一類(lèi)模式著眼于集成,即多個(gè)開(kāi)發(fā)人員如何將他們的工作成果組合成一個(gè)連貫的整體四濒。另一類(lèi)則著眼于生產(chǎn)路徑换况,即使用分支幫助管理從集成代碼庫(kù)到生產(chǎn)環(huán)境運(yùn)行產(chǎn)品的路徑。一些模式為這兩大類(lèi)模式提供支撐盗蟆,我將它們歸類(lèi)為基本模式戈二,在本節(jié)中講述。還有一些模式既不基本也不適合于歸類(lèi)到集成和生產(chǎn)路徑這兩大類(lèi)模式喳资,我把它們留到最后來(lái)講挽拂。

? 源分支 ?

創(chuàng)建一個(gè)副本并記錄對(duì)該副本的所有更改。

如果幾個(gè)人在同一代碼基礎(chǔ)上工作骨饿,那么很快他們就無(wú)法在相同文件上工作亏栈。如果我想運(yùn)行一個(gè)編譯,而我的同事還正在敲入一個(gè)表達(dá)式宏赘,那么編譯將失敗绒北。我們不得不相互呼喊:“我正在編譯,什么都不要更改察署!”即使團(tuán)隊(duì)只有兩個(gè)人闷游,這也難以維持正常工作;如果是更大的團(tuán)隊(duì)贴汪,這種混亂場(chǎng)景會(huì)更加令人難以想象脐往。

對(duì)此場(chǎng)景案例的簡(jiǎn)單解決辦法是讓每個(gè)開(kāi)發(fā)人員都獲取一個(gè)代碼庫(kù)的副本,然后我們就可以輕松地進(jìn)行自己負(fù)責(zé)的功能開(kāi)發(fā)扳埂。但是又會(huì)出現(xiàn)一個(gè)新問(wèn)題:開(kāi)發(fā)完成后业簿,如何將兩個(gè)副本再次合并在一起?

源代碼控制系統(tǒng)使此過(guò)程更加容易阳懂。關(guān)鍵在于它會(huì)將每個(gè)分支上所有的更改都記錄為提交梅尤。這不僅可以確保沒(méi)有人忘記他們對(duì) utils.java 所做的微小更改,而且記錄更改使執(zhí)行合并更加容易岩调,尤其是當(dāng)幾個(gè)人更改了同一文件時(shí)巷燥。

這就引出了本文中使用的分支(branch)的定義。我將分支定義為對(duì)代碼庫(kù)的特定提交序列号枕。分支的 headtip 指向該序列中的最新提交缰揪。

image.png

分支是個(gè)名詞,但也有動(dòng)詞“ 創(chuàng)建分支”的意思葱淳。這里我的意思是創(chuàng)建一個(gè)新分支钝腺,我們也可以將其視為將原始分支分為兩個(gè)分支。當(dāng)來(lái)自一個(gè)分支的提交被應(yīng)用到另一分支時(shí)蛙紫,即為分支合并拍屑。
image.png

我用于“分支”的定義與我觀(guān)察大多數(shù)開(kāi)發(fā)人員談?wù)撍鼈兊姆绞较鄬?duì)應(yīng)痰驱。但是源代碼控制系統(tǒng)更傾向于以特定的方式使用“分支”办桨。

以一種常見(jiàn)情況來(lái)說(shuō)明這一點(diǎn),一個(gè)現(xiàn)代開(kāi)發(fā)團(tuán)隊(duì)控嗜,該團(tuán)隊(duì)將其源代碼保存在共享的 git 倉(cāng)庫(kù)中唁毒。一名開(kāi)發(fā)人員 Scarlett (以猩紅色表示) 需要進(jìn)行一些更改蒜茴,因此她克隆了 git 倉(cāng)庫(kù)并檢出了 master 分支。她做了幾處更改浆西,然后重新提交給她的 master 分支粉私。同時(shí),另一個(gè)開(kāi)發(fā)人員近零,Violet (以紫色表示) 將倉(cāng)庫(kù)克隆到自己桌面上诺核,并簽出 master 分支抄肖。那么 Scarlett 和 Violet 是在同一個(gè)分支上工作還是分別在另一個(gè)分支上工作?答案是:他們都在 “master” 上工作窖杀。但是他們的提交彼此獨(dú)立漓摩,并且當(dāng)他們將更改推回到共享倉(cāng)庫(kù)時(shí)都需要合并。如果 Scarlett 不確定自己所做的更改入客,會(huì)發(fā)生什么情況管毙,因此她標(biāo)記了最后的提交,并將她的 master 分支重置回 origin/master(她克隆共享倉(cāng)庫(kù)時(shí)的最后一次提交)桌硫。


image.png

根據(jù)我前文給出的分支定義夭咬,Scarlett 和 Violet 分別在單獨(dú)的分支上工作,這兩個(gè)分支彼此分開(kāi)铆隘,并且與共享倉(cāng)庫(kù)上的 master 分支隔離卓舵。當(dāng) Scarlett 放棄帶有標(biāo)簽的分支開(kāi)發(fā)時(shí),根據(jù)定義咖驮,它仍然是一個(gè)分支(并且她很可能將其視為分支)边器,但是在 git 看來(lái),這是一個(gè)帶標(biāo)簽的代碼行托修。

使用 git 這樣的分布式版本控制系統(tǒng)忘巧,這意味著每當(dāng)我們進(jìn)一步克隆倉(cāng)庫(kù)時(shí),就會(huì)獲得其他分支睦刃。如果 Scarlett 在回家的火車(chē)上克隆了自己的本地倉(cāng)庫(kù)到筆記本電腦上砚嘴,那么她將創(chuàng)建第 3 個(gè) master 分支。在 GitHub 中派生也會(huì)產(chǎn)生相同的效果 —— 每個(gè)派生的倉(cāng)庫(kù)都有自己額外的分支集涩拙。

當(dāng)我們遇到不同的版本控制系統(tǒng)時(shí)际长,這種術(shù)語(yǔ)的混亂會(huì)變得更糟,因?yàn)樗鼈儗?duì)分支的構(gòu)成都有自己的定義兴泥。Mercurial 中的分支與 git 中的分支完全不同工育,后者更接近 Mercurial 的書(shū)簽。Mercurial 也可以用未命名的 head 創(chuàng)建分支搓彻,使用 Mercurial 的人們經(jīng)常通過(guò)克隆倉(cāng)庫(kù)來(lái)創(chuàng)建分支如绸。

所有這些術(shù)語(yǔ)上的混亂導(dǎo)致一些人避免使用該術(shù)語(yǔ)。在這里更通用的術(shù)語(yǔ)是代碼線(xiàn)(CodeLine)旭贬。我將代碼線(xiàn)定義為代碼庫(kù)的一系列特定版本怔接。它可以以標(biāo)簽結(jié)尾,或是一個(gè)分支稀轨,又或者淹沒(méi)在 git 的 reflog 中扼脐。你會(huì)注意到我對(duì)分支和代碼線(xiàn)的定義是如此相似。代碼線(xiàn)在許多方面都是更有用的術(shù)語(yǔ)奋刽,我確實(shí)使用過(guò)瓦侮,但是在實(shí)踐中并未廣泛使用艰赞。因此,對(duì)于本文而言脏榆,除非我處于 git(或其他工具)術(shù)語(yǔ)的特定上下文中猖毫,否則我將交替使用分支和代碼線(xiàn)。

此定義的結(jié)果是须喂,無(wú)論你使用的是哪種版本控制系統(tǒng),一旦有開(kāi)發(fā)人員在進(jìn)行本地更改后趁蕊,每個(gè)開(kāi)發(fā)人員在本地的工作副本中都至少具有一條個(gè)人代碼線(xiàn)坞生。如果我克隆一個(gè)項(xiàng)目的 git 庫(kù),檢出 master 分支并更新一些文件 —— 這就是一條新的代碼線(xiàn)掷伙,即使我還沒(méi)有提交任何內(nèi)容是己。同樣,如果我從 subversion 庫(kù)的主干建了自己的工作副本任柜,即使不涉及任何 subversion 分支卒废,該工作副本也是獨(dú)立的代碼線(xiàn)。

適用場(chǎng)景

一個(gè)老話(huà)說(shuō)宙地,如果你從高樓上摔下來(lái)摔认,墜落不會(huì)傷害到你,但是著陸會(huì)宅粥。對(duì)源代碼來(lái)說(shuō)也是一樣的道理:創(chuàng)建分支容易参袱,但合并困難。

記錄提交中所有更改的源代碼控制系統(tǒng)確實(shí)讓合并過(guò)程更加容易秽梅,但并沒(méi)有使合并過(guò)程不再重要抹蚀。如果 Scarlett 和 Violet 都將變量的名稱(chēng)更改為不同的名稱(chēng),則存在沖突企垦,如果沒(méi)有人工干預(yù)环壤,源管理系統(tǒng)將無(wú)法自行處理。為了凸顯這種文本沖突的尷尬钞诡,源代碼控制系統(tǒng)至少還可以發(fā)現(xiàn)并提醒人們看一下郑现。但是在文本合并沒(méi)有問(wèn)題的地方也經(jīng)常會(huì)出現(xiàn)沖突,系統(tǒng)仍然無(wú)法正常工作臭增。想象一下懂酱,Scarlett 更改了函數(shù)的名稱(chēng),而 Violet 向其分支添加了一些代碼誊抛,以其舊名稱(chēng)調(diào)用該函數(shù)列牺。這就是我所說(shuō)的語(yǔ)義沖突。當(dāng)發(fā)生此類(lèi)沖突時(shí)拗窃,系統(tǒng)可能無(wú)法構(gòu)建瞎领,也可能會(huì)構(gòu)建成功但在運(yùn)行時(shí)失敗泌辫。

Jonny LeRoy 喜歡指出人們(包括我)繪制分支圖的這個(gè)瑕疵


image.png

任何有并行計(jì)算或分布式計(jì)算工作經(jīng)驗(yàn)的人都熟悉的問(wèn)題是:當(dāng)多個(gè)開(kāi)發(fā)人員同時(shí)更新時(shí),代碼倉(cāng)會(huì)處于某個(gè)共享狀態(tài)九默。我們需要通過(guò)將這些更新序列化為某個(gè)共識(shí)更新的方式震放,把這些開(kāi)發(fā)人員的更新結(jié)合起來(lái) 。事實(shí)上驼修,使系統(tǒng)正確執(zhí)行和運(yùn)行意味著該共識(shí)狀態(tài)的有效性標(biāo)準(zhǔn)非常復(fù)雜殿遂,這使我們的任務(wù)也變得更加復(fù)雜。無(wú)法創(chuàng)建確定性算法來(lái)找到共識(shí)乙各。人們需要尋求共識(shí)墨礁,并且共識(shí)可能涉及混合不同更新的選擇部分。通常耳峦,只有通過(guò)原始更新解決沖突才能達(dá)成共識(shí)恩静。

我說(shuō):“如果沒(méi)有分支該怎么辦”。每個(gè)人都將實(shí)時(shí)編輯代碼蹲坷,考慮不周的更改會(huì)使系統(tǒng)崩潰驶乾,人們會(huì)互相踩踏。因此循签,我們給個(gè)人一種時(shí)間凍結(jié)的錯(cuò)覺(jué)级乐,認(rèn)為他們是唯一更改系統(tǒng)的人,這些變更可以等到他們對(duì)系統(tǒng)風(fēng)險(xiǎn)考慮充分后才變更懦底。但這是一種錯(cuò)覺(jué)唇牧,最終代價(jià)還是該來(lái)的會(huì)來(lái)。誰(shuí)買(mǎi)單聚唐?什么時(shí)候丐重?代價(jià)是多少?這些模式正在討論的就是:選擇如何支付代價(jià)杆查“绲耄—— Kent Beck

因此,在下文中我將列出各種模式亲桦,這些模式支持友好的隔離崖蜜,就像當(dāng)你從高處落下時(shí),風(fēng)穿過(guò)發(fā)絲客峭,同時(shí)又把不可避免的與堅(jiān)硬地面的碰撞后果降到最低豫领。

? 主線(xiàn) ?

單一、共享舔琅、代表產(chǎn)品當(dāng)前狀態(tài)的分支

主線(xiàn)(mainline)是一個(gè)特殊的代碼線(xiàn)等恐,代表團(tuán)隊(duì)代碼的當(dāng)前狀態(tài)。當(dāng)我想開(kāi)始一項(xiàng)新工作,我會(huì)從主線(xiàn)中拉取代碼到我的本地版本庫(kù)课蔬,在本地版本庫(kù)上工作囱稽。當(dāng)我要與團(tuán)隊(duì)的其他成員分享我的工作成果時(shí),我會(huì)用我的工作成果更新主線(xiàn)二跋,理想狀態(tài)下將應(yīng)用后面要討論的主線(xiàn)集成模式战惊。

不同的團(tuán)隊(duì)使用不同的名稱(chēng)稱(chēng)呼這一特殊分支,通常會(huì)受使用的版本控制系統(tǒng)慣例的影響扎即。Git 用戶(hù)通常稱(chēng)之為 “master”, subversion 用戶(hù)通常稱(chēng)之它為 “主干”吞获。

在這里必須強(qiáng)調(diào),主線(xiàn)是一個(gè)單一的谚鄙、共享的代碼線(xiàn)衫哥。當(dāng)人們?cè)?git 中談?wù)?“master” 時(shí),他們可能在說(shuō)幾件不同的事情襟锐,因?yàn)槊總€(gè)代碼庫(kù)的克隆都有自己的本地 master。通常膛锭,團(tuán)隊(duì)會(huì)有一個(gè)中央倉(cāng)庫(kù) —— 一個(gè)作為項(xiàng)目單一記錄點(diǎn)的共享倉(cāng)庫(kù)粮坞,并且是大多數(shù)克隆的起源。從頭開(kāi)始一項(xiàng)新工作意味著克隆該中央倉(cāng)庫(kù)初狰。如果已經(jīng)有了一個(gè)克隆莫杈,我會(huì)首先從中央倉(cāng)庫(kù)拉取 master 分支,以保持與主線(xiàn)同步奢入。在這種情況下筝闹,主線(xiàn)就是中央倉(cāng)庫(kù)的 master 分支。

當(dāng)我在開(kāi)發(fā)自己的功能時(shí)腥光,我在使用自己的開(kāi)發(fā)分支关顷,這個(gè)分支可以是我本地版本庫(kù)的 master 分支,也可以是其他本地分支武福。如果需要在自己的開(kāi)發(fā)分支上工作較長(zhǎng)時(shí)間议双,我可以每隔一段時(shí)間拉取主線(xiàn)的更改,并把這些更改合并到我自己的開(kāi)發(fā)分支上捉片,以獲取主線(xiàn)上最新的更改平痰。

同樣,如果我想創(chuàng)建產(chǎn)品發(fā)布的新版本伍纫,我可以從當(dāng)前主線(xiàn)開(kāi)始宗雇。如果我需要修復(fù)錯(cuò)誤,以發(fā)布足夠穩(wěn)定的產(chǎn)品莹规,我可以使用某一發(fā)布分支赔蒲。

適用場(chǎng)景

我記得在 21 世紀(jì)初常和一個(gè)客戶(hù)端構(gòu)建工程師討論。他的工作是集成團(tuán)隊(duì)正在開(kāi)發(fā)的產(chǎn)品。他會(huì)給團(tuán)隊(duì)的每個(gè)成員發(fā)一封電子郵件嘹履,團(tuán)隊(duì)成員則會(huì)發(fā)回各自代碼庫(kù)中等待集成的各種不同文件腻扇。這位構(gòu)建工程師就把這些文件復(fù)制到他的集成樹(shù)中,并嘗試編譯代碼庫(kù)砾嫉。創(chuàng)建一個(gè)能夠編譯幼苛,并可供某種形式進(jìn)行測(cè)試的構(gòu)建,通常需要耗費(fèi)這位構(gòu)建工程師幾周的時(shí)間焕刮。

相比之下舶沿,通過(guò)主線(xiàn),任何人都可以從主線(xiàn)的一部分快速開(kāi)始產(chǎn)品最新的構(gòu)建配并。更重要的是括荡,主線(xiàn)不僅僅使得觀(guān)察代碼庫(kù)狀態(tài)更容易,它還是許多其他模式的基礎(chǔ)溉旋,這些模式將后文中描述畸冲。

主線(xiàn)的一個(gè)替代方案是發(fā)布火車(chē)

? 健康的分支 ?

在每次提交時(shí)執(zhí)行自動(dòng)檢查观腊,以確保分支沒(méi)有缺陷邑闲,自動(dòng)檢查通常包括構(gòu)建和運(yùn)行測(cè)試。

由于主線(xiàn)具有共享的并且是已被認(rèn)可的狀態(tài)梧油,因此保持主線(xiàn)處于穩(wěn)定狀態(tài)非常重要苫耸。還是在 21 世紀(jì)初,我記得曾和某一組織的一個(gè)開(kāi)發(fā)團(tuán)隊(duì)一起討論儡陨,這個(gè)組織因?qū)λ挟a(chǎn)品執(zhí)行每日構(gòu)建而廣為人知褪子。在當(dāng)時(shí),每日構(gòu)建被認(rèn)為是相當(dāng)先進(jìn)的做法骗村,這個(gè)組織也因此而獲得贊譽(yù)嫌褪。在這些贊揚(yáng)的文章中沒(méi)有提到的是,那些每日構(gòu)建并不總是成功的叙身。實(shí)際上渔扎,一些團(tuán)隊(duì)的日常構(gòu)建連續(xù)數(shù)月都無(wú)法編譯成功,這在當(dāng)年并不罕見(jiàn)信轿。

為了解決這個(gè)問(wèn)題晃痴,我們可以努力去保持一個(gè)分支是健康的——也就是這個(gè)分支是可以成功構(gòu)建并且運(yùn)行時(shí)幾乎沒(méi)有 bug 的。為了確保這一點(diǎn)财忽,我發(fā)現(xiàn)編寫(xiě)自測(cè)代碼是至關(guān)重要的倘核。這種開(kāi)發(fā)實(shí)踐是指我們?cè)诰帉?xiě)生產(chǎn)代碼時(shí),還要編寫(xiě)一套全面的自動(dòng)化測(cè)試即彪,讓我們可以確信紧唱,如果這些測(cè)試通過(guò)活尊,那么這些代碼就不會(huì)有 bug。如果我們這樣做漏益,就可以通過(guò)每次提交運(yùn)行一個(gè)構(gòu)建來(lái)保持分支健康蛹锰,這個(gè)構(gòu)建過(guò)程也包括運(yùn)行這套測(cè)試。如果系統(tǒng)無(wú)法編譯绰疤,或者測(cè)試失敗铜犬,那么我們的第一要?jiǎng)?wù)就是在我們對(duì)該分支進(jìn)行任何其他操作之前就先對(duì)其進(jìn)行修復(fù)。通常這意味著我們“凍結(jié)”了這個(gè)分支——除為了修復(fù)以使其恢復(fù)正常的提交之外轻庆,不會(huì)允許在這個(gè)分支進(jìn)行任何提交癣猾。

為了給保持分支健康提供足夠的信心,在測(cè)試的程度上存在一定矛盾余爆。許多更徹底的測(cè)試需要大量的時(shí)間去運(yùn)行纷宇,這就會(huì)延遲對(duì)提交是否正常的反饋。一些團(tuán)隊(duì)通過(guò)將測(cè)試分散到部署流水線(xiàn)的多個(gè)階段來(lái)解決這個(gè)問(wèn)題蛾方。這些測(cè)試的第一個(gè)階段應(yīng)運(yùn)行快速像捶,一般不超過(guò)十分鐘,但仍應(yīng)相當(dāng)全面桩砰。我將這樣的測(cè)試集稱(chēng)為提交套件 (不過(guò)它通常會(huì)被稱(chēng)為“單元測(cè)試”作岖,因?yàn)檫@樣的提交套件中的測(cè)試大多數(shù)是單元測(cè)試)。

理想情況下五芝,應(yīng)在每次提交時(shí)運(yùn)行全方位的測(cè)試。但是辕万,如果測(cè)試執(zhí)行很慢枢步,例如需要占用服務(wù)器幾個(gè)小時(shí)的性能測(cè)試,那就有點(diǎn)不切實(shí)際渐尿。如今醉途,團(tuán)隊(duì)通常會(huì)構(gòu)建一個(gè)提交套件,在每次提交時(shí)運(yùn)行砖茸,而對(duì)部署流水線(xiàn)后續(xù)的階段隘擎,會(huì)盡可能頻繁地運(yùn)行。
代碼運(yùn)行沒(méi)有錯(cuò)誤并不足以說(shuō)明就是好的代碼凉夯。為了保持穩(wěn)定的交付節(jié)奏货葬,我們需要保持足夠高的代碼內(nèi)建質(zhì)量。一種流行的方法是使用提交審核(Reviewed Commits)劲够,然而我們也要看到還有其他選擇震桶。

適用場(chǎng)景

每個(gè)團(tuán)隊(duì)都應(yīng)當(dāng)在他們的開(kāi)發(fā)工作流程中明確每個(gè)分支的健康狀況標(biāo)準(zhǔn)。保持主線(xiàn)健康有無(wú)比重要的價(jià)值征绎。如果主線(xiàn)是健康的蹲姐,那么開(kāi)發(fā)人員只要從當(dāng)前的主線(xiàn)拉取代碼就可以開(kāi)始新的工作,而不會(huì)糾結(jié)于那些可能會(huì)妨礙他們工作的缺陷。我們經(jīng)常聽(tīng)說(shuō)有人在開(kāi)始新的工作前要花幾天時(shí)間去嘗試修復(fù)或繞過(guò)他們拉取代碼中的問(wèn)題柴墩。

健康的主線(xiàn)也可以簡(jiǎn)化生產(chǎn)路徑忙厌。可以隨時(shí)從主線(xiàn)的最新版本構(gòu)建新的生產(chǎn)候選對(duì)象江咳。最好的團(tuán)隊(duì)發(fā)現(xiàn)他們幾乎不需要做任何工作來(lái)穩(wěn)定這樣的代碼庫(kù)逢净,這些代碼庫(kù)通常能夠直接從主線(xiàn)發(fā)布到生產(chǎn)環(huán)境。

主線(xiàn)健康的關(guān)鍵是自測(cè)代碼扎阶,以及一個(gè)可在幾分鐘內(nèi)運(yùn)行完成的提交套件汹胃。建設(shè)這樣的能力會(huì)是很有意義的投入,一旦我們可以在幾分鐘之內(nèi)確保我的提交不會(huì)搞砸任何東西东臀,那將徹底改變我們的整個(gè)開(kāi)發(fā)過(guò)程着饥。我們可以更快地進(jìn)行更改,自信地重構(gòu)我們的代碼讓它更好用惰赋,并大大減少?gòu)钠谕δ艿缴a(chǎn)中運(yùn)行代碼的交付周期宰掉。

保持個(gè)人開(kāi)發(fā)分支的健康是明智的做法,因?yàn)檫@樣可以啟用差異調(diào)試赁濒。但是轨奄,這種期望和頻繁提交當(dāng)前狀態(tài)為檢查點(diǎn)是背道而馳的。如果我要嘗試一個(gè)不同的路徑拒炎,那么即使編譯失敗可能也會(huì)去創(chuàng)建一個(gè)檢查點(diǎn)挪拟。解決這種矛盾的方法是,一旦完成我最近的工作击你,就去除所有不健康的提交玉组。這樣,只有健康的提交會(huì)在我的分支上保留超過(guò)幾個(gè)小時(shí)丁侄。

如果我保持個(gè)人分支的健康惯雳,這也能使提交到主線(xiàn)變得更加容易——我會(huì)知道任何在主線(xiàn)集成(Mainline Integration)中突然出現(xiàn)的錯(cuò)誤都純粹是由于集成問(wèn)題引起的,而不單單是我代碼庫(kù)中的錯(cuò)誤鸿摇。這將使查找和修復(fù)錯(cuò)誤變得更快也更容易石景。

集成模式

分支開(kāi)發(fā)涉及到在管理分離和合并時(shí)的相互影響。由于所有人始終使用同一套共享代碼庫(kù)拙吉,如果你正在輸入變量名潮孽,我這邊就無(wú)法編譯程序,這是行不通的筷黔。因此恩商,至少在某種程度上,我們需要有一個(gè)私有工作區(qū)的概念必逆,讓我可以暫時(shí)在這個(gè)私有工作區(qū)里工作〉】埃現(xiàn)代的源代碼控制工具使得創(chuàng)建分支和監(jiān)視這些分支的變更變得很容易揽乱。然而,在某些時(shí)候粟矿,我們還需要合并分支凰棉。考慮分支開(kāi)發(fā)策略實(shí)際上就是決定我們合并分支的方式和時(shí)機(jī)陌粹。

? 主線(xiàn)集成 ?

開(kāi)發(fā)人員通過(guò)從主線(xiàn)中拉取撒犀、合并,以及(在健康的情況下)推回主線(xiàn)來(lái)集成他們的工作掏秩。

主線(xiàn)清晰定義了團(tuán)隊(duì)軟件當(dāng)前的狀態(tài)或舞。使用主線(xiàn)的最大好處之一是簡(jiǎn)化了集成。如果沒(méi)有主線(xiàn)蒙幻,這就是我前面描述的要與團(tuán)隊(duì)中每個(gè)人進(jìn)行協(xié)調(diào)的復(fù)雜任務(wù)映凳。然而,有了主線(xiàn)邮破,每個(gè)開(kāi)發(fā)人員都可以自己集成诈豌。

我將通過(guò)一個(gè)例子來(lái)說(shuō)明它的工作原理。有一個(gè)名為 Scarlett 的開(kāi)發(fā)人員抒和,通過(guò)將主線(xiàn)克隆到自己的倉(cāng)庫(kù)中開(kāi)始某項(xiàng)工作矫渔。在 git 中,如果她還沒(méi)有中央倉(cāng)庫(kù)的克隆摧莽,她將會(huì)克隆中央倉(cāng)庫(kù)庙洼,檢出 master 分支。如果她已經(jīng)有了中央倉(cāng)庫(kù)的克隆镊辕,她將拉取主線(xiàn)到她的本地 master 分支送膳。然后,她就可以在本地工作丑蛤,在她的本地 master 分支上進(jìn)行提交。


image.png

當(dāng)她工作的時(shí)候撕阎,她的同事 Violet 把一些變更推送到了主線(xiàn)上受裹。由于 Scarlett 是在自己的代碼線(xiàn)上工作,所以當(dāng)她在做自己的事情時(shí)虏束,可以忽略這些變化棉饶。

image.png

在某個(gè)時(shí)間點(diǎn),Scarlett 達(dá)到了可以集成的程度镇匀。第一步照藻,是將當(dāng)前的主線(xiàn)狀態(tài)提取(fetch)到本地主分支中汗侵,這將拉取到 Violet 的變更幸缕。當(dāng)她在本地分支工作時(shí)群发,提交將在 origin/master (本地主分支名)上作為一個(gè)單獨(dú)的代碼線(xiàn)顯示。

image.png

現(xiàn)在她需要把她的變更和 Violet 的變更合并起來(lái)发乔。有些團(tuán)隊(duì)喜歡通過(guò) merge(合并)來(lái)做到這一點(diǎn)熟妓,而另一些團(tuán)隊(duì)則喜歡通過(guò) rebase(變基)來(lái)實(shí)現(xiàn)。通常栏尚,人們?cè)谡劦綄⒎种诤显谝黄饡r(shí)起愈,無(wú)論是實(shí)際使用 git merge 還是 rebase 操作,都會(huì)使用“merge(合并)”一詞译仗。我將遵循這種用法抬虽,因此,除非我實(shí)際上正在討論合并和變基之間的區(qū)別纵菌,否則請(qǐng)考慮將“merge(合并)”作為可以以?xún)烧咧腥我庖粋€(gè)方法實(shí)現(xiàn)的邏輯操作阐污。

關(guān)于是使用普通的合并,還是使用或避免 fast-forward 快速合并产艾,或者是使用 rebase 疤剑,另外還有一些其他的討論。這超出了本文的范圍闷堡,但是如果人們寄給我足夠多的 Tripel Karmeliet(卡美里特啤酒)的話(huà)隘膘,我可能會(huì)寫(xiě)一篇關(guān)于這個(gè)問(wèn)題的文章,畢竟如今比較流行“投桃報(bào)李”嘛杠览。

如果 Scarlett 幸運(yùn)的話(huà)弯菊,合并 Violet 的代碼將是一個(gè)清晰的過(guò)程,否則踱阿,她將會(huì)遇到一些沖突管钳。這些可能是文本沖突,大部分源代碼控制系統(tǒng)可以自動(dòng)處理這些沖突软舌。但是語(yǔ)義沖突更難處理才漆,這就是有“自測(cè)代碼”的方便之處四康。(由于沖突會(huì)產(chǎn)生很多的工作量客蹋,而且總是會(huì)引入許多工作中的風(fēng)險(xiǎn)锨推,所以我用一塊醒目的黃色來(lái)標(biāo)記它們笋庄。)

image.png

此時(shí)墨闲,Scarlett 需要驗(yàn)證合并的代碼滿(mǎn)足主線(xiàn)的健康標(biāo)準(zhǔn) (假設(shè)主線(xiàn)是一個(gè)健康分支)协屡。這通常意味著構(gòu)建代碼并運(yùn)行構(gòu)成主線(xiàn)提交套件的所有測(cè)試喻频。即使這是一個(gè)干凈的合并托酸,她也需要做這些工作演闭,因?yàn)楸M管是一個(gè)干凈的合并也可能隱藏語(yǔ)義沖突不跟。提交套件中的任何故障都應(yīng)該完全歸因于這次合并,因?yàn)橛糜诤喜⒌膬蓚€(gè)父版本都應(yīng)該是綠色的(譯者注:即沒(méi)有故障米碰,在套件中測(cè)試通過(guò)顯示為綠色)窝革。知道這一點(diǎn)將有助于她追蹤問(wèn)題购城,因?yàn)樗梢圆榭床町愐詫ふ揖€(xiàn)索。

通過(guò)這個(gè)構(gòu)建和測(cè)試聊闯,她已經(jīng)成功地把主線(xiàn)拉到了她的代碼線(xiàn)工猜,但是——還有一件既重要又常常被人忽略的事——她還沒(méi)有完成與主線(xiàn)的集成。要完成集成菱蔬,她必須將所做的更改推入主線(xiàn)篷帅。如果她不這么做,團(tuán)隊(duì)中的其他人都將與她的變更隔離開(kāi)來(lái)——本質(zhì)上沒(méi)有集成拴泌。集成既是拉取也是推送——只有在 Scarlett 把更改推入主線(xiàn)之后魏身,她的工作內(nèi)容才與項(xiàng)目中的其余部分集成。


image.png

現(xiàn)在許多團(tuán)隊(duì)在將代碼提交添加到主線(xiàn)之前蚪腐,需要一個(gè)代碼評(píng)審的步驟——我稱(chēng)之為“提交評(píng)審”模式箭昵,后面會(huì)進(jìn)行討論。

有時(shí)候回季,在 Scarlett 進(jìn)行推送前家制,其他人會(huì)和主線(xiàn)集成。在這種情況下泡一,她必須再次拉取和合并分支颤殴。通常,這只是一個(gè)偶然的事件鼻忠,在不需要任何進(jìn)一步協(xié)調(diào)的情況下就可以被解決涵但。我見(jiàn)過(guò)長(zhǎng)時(shí)間構(gòu)建的團(tuán)隊(duì)使用集成接力棒,這樣只有持有接力棒的開(kāi)發(fā)人員才能集成帖蔓。但是近年來(lái)矮瘟,隨著構(gòu)建時(shí)間的縮短,我還沒(méi)有聽(tīng)到太多這樣的情況塑娇。

適用場(chǎng)景

顧名思義澈侠,只有當(dāng)我們?cè)诋a(chǎn)品上使用主線(xiàn)時(shí),我才能使用主線(xiàn)集成埋酬。

使用主線(xiàn)集成的一個(gè)替代方法是從主線(xiàn)拉取這些變更哨啃,合并到個(gè)人開(kāi)發(fā)分支中。這可能是有用的——至少拉取時(shí)可以讓 Scarlett 意識(shí)到其他人已經(jīng)集成了變更奇瘦,并發(fā)現(xiàn)她的工作和主線(xiàn)之間的沖突。但是劲弦,在 Scarlett 推送上傳之前耳标, Violet 將無(wú)法發(fā)現(xiàn)她的工作內(nèi)容與 Scarlett 的變更之間有任何沖突。

當(dāng)人們使用“integrate(集成)”這個(gè)詞時(shí)邑跪,他們往往忽略了一個(gè)要點(diǎn)次坡。經(jīng)常聽(tīng)到有人說(shuō)呼猪,他們正在集成主線(xiàn)到他們的分支,而實(shí)際上他們只是在從主線(xiàn)拉取砸琅。我已經(jīng)學(xué)會(huì)了對(duì)此保持警惕宋距,并進(jìn)一步確認(rèn),看看它們是指拉取還是真正的主線(xiàn)集成症脂。兩者的結(jié)果是有很大差異的谚赎,所以不要混淆術(shù)語(yǔ)是很重要的。

另一種選擇是诱篷,當(dāng) Scarlett 在做的一些工作還沒(méi)有準(zhǔn)備好與團(tuán)隊(duì)其他成員的工作完全集成壶唤,但和 Violet 的有重疊之處,并想和她一起共享棕所。在這種情況下闸盔,他們可以開(kāi)啟一個(gè)協(xié)作分支

? 特性分支開(kāi)發(fā) ?

為某個(gè)功能特性建立獨(dú)立的分支琳省,在該分支上完成與該特性相關(guān)的所有工作迎吵,在功能特性完成后集成到主線(xiàn)中。

按照特性分支開(kāi)發(fā)這種模式针贬,當(dāng)開(kāi)發(fā)人員要開(kāi)始開(kāi)發(fā)某個(gè)功能特性時(shí)击费,他們會(huì)開(kāi)啟一個(gè)分支,并持續(xù)在這個(gè)分支上工作直到功能特性完成坚踩,然后再與主線(xiàn)集成荡灾。

例如,讓我們來(lái)看下 Scarlett瞬铸。她領(lǐng)取的是一個(gè)給他們的網(wǎng)站中增加本地營(yíng)業(yè)稅集合的功能批幌。她從產(chǎn)品最新的穩(wěn)定版本開(kāi)始,從主線(xiàn)拉取到她的本地倉(cāng)庫(kù)嗓节,然后從當(dāng)前主線(xiàn)的頂端創(chuàng)建一個(gè)新的分支荧缘。不管多久,她會(huì)為完成這個(gè)功能拦宣,在這個(gè)本地分支上進(jìn)行一系列提交截粗。


image.png

她可能會(huì)將該分支推送到項(xiàng)目倉(cāng)庫(kù),以便其他人可以查看她的更改鸵隧。

當(dāng)她在工作時(shí)绸罗,主線(xiàn)上也會(huì)有其他提交。因此豆瘫,她可能要不時(shí)地從主線(xiàn)拉取版本珊蟀,以便獲知是否有任何改變可能會(huì)影響她正在開(kāi)發(fā)的功能。


image.png

請(qǐng)注意外驱,這不是我們上文說(shuō)過(guò)的集成育灸,因?yàn)樗龥](méi)有推送回主線(xiàn)腻窒。在這個(gè)點(diǎn)上,只有她在看自己的工作內(nèi)容磅崭,其他人則沒(méi)有儿子。

一些團(tuán)隊(duì)希望確保所有代碼都保存在中央倉(cāng)庫(kù)中,無(wú)論這些代碼是否已被集成砸喻。在這種情況下柔逼,Scarlett 會(huì)將她的特性分支推送到中央倉(cāng)庫(kù)中。這將允許其他團(tuán)隊(duì)成員查看她正在進(jìn)行的工作恩够,即使該工作尚未集成到其他人的工作中卒落。

當(dāng)她完成了這個(gè)功能特性的開(kāi)發(fā)后,她將執(zhí)行主線(xiàn)集成蜂桶,將這個(gè)功能特性集成到產(chǎn)品中儡毕。

image.png

如果 Scarlett 同時(shí)進(jìn)行多個(gè)功能特性的工作,那她將為每個(gè)特性開(kāi)啟一個(gè)獨(dú)立的分支扑媚。

適用場(chǎng)景

特性分支開(kāi)發(fā)是如今業(yè)界一種流行的模式腰湾。要討論何時(shí)使用它,我需要介紹它的主要替代方案——持續(xù)集成疆股。但是首先我要談?wù)劶深l率的作用费坊。

? 集成頻率 ?

我們進(jìn)行集成的頻率對(duì)團(tuán)隊(duì)的運(yùn)作有著顯著的影響⊙裕《DevOps現(xiàn)狀調(diào)查報(bào)告》的研究表明附井,精英開(kāi)發(fā)團(tuán)隊(duì)的集成頻率要比績(jī)效低下的團(tuán)隊(duì)高得多 —— 這一觀(guān)察結(jié)果符合我和眾多業(yè)界同行的經(jīng)驗(yàn)。我將通過(guò)由 Scarlett 和 Violet 為主角的兩個(gè)集成頻率的案例來(lái)說(shuō)明這一點(diǎn)两残。

低頻集成

我先從低頻集成的示例開(kāi)始永毅。在這里,我們的兩個(gè)主人公從克隆主線(xiàn)到各自的本地分支展開(kāi)工作人弓,然后各自執(zhí)行了幾個(gè)還不愿推送的本地提交沼死。


image.png

當(dāng)他們工作時(shí),另外有人向主線(xiàn)進(jìn)行了一個(gè)提交崔赌。(我不能很快想出另一個(gè)人名意蛀,那是一種顏色,就叫Grayham健芭?)

image.png

這個(gè)團(tuán)隊(duì)通過(guò)保持一個(gè)健康分支县钥,并在每次提交后拉取主線(xiàn)代碼進(jìn)行團(tuán)隊(duì)協(xié)作。Scarlett 的前兩個(gè)提交沒(méi)有任何新代碼可拉取慈迈,因?yàn)楫?dāng)時(shí)主線(xiàn)沒(méi)有變化若贮,但現(xiàn)在她需要拉取標(biāo)記為 M1 的代碼。

image.png

我用黃色框標(biāo)記了此次合并。這次是將 S1 到 S3 與 M1 合并兜看。很快,Violet 需要做同樣的事情狭瞎。

image.png

這時(shí)细移,兩個(gè)開(kāi)發(fā)人員的本地代碼都已跟上主線(xiàn)的變化,但由于他們的本地代碼彼此隔離熊锭,所以他們尚未彼此集成弧轧。Scarlett 不知道 Violet 在 V1 到 V3 的更改。

Scarlett 進(jìn)行了更多的本地提交碗殷,準(zhǔn)備好了進(jìn)行主線(xiàn)集成精绎。對(duì)她來(lái)說(shuō),這是一個(gè)輕松的推送锌妻,因?yàn)樗^早拉取了 M1代乃。


image.png

而 Violet 的操作則更為復(fù)雜。當(dāng)她進(jìn)行主線(xiàn)集成時(shí)仿粹,她現(xiàn)在需要集成 S1..5 與 V1..6搁吓。

image.png

我已經(jīng)根據(jù)涉及的提交個(gè)數(shù)科學(xué)地計(jì)算了合并工作量的大小。然而吭历,即使你沒(méi)注意到上圖的那些舌狀凸起堕仔,你也會(huì)意識(shí)到 Violet 的合并很有可能比較困難。

高頻集成

在前面的示例中晌区,我們兩個(gè)多彩的開(kāi)發(fā)人員是在進(jìn)行了幾個(gè)本地提交之后集成的摩骨。讓我們看看如果他們?cè)诿總€(gè)本地提交之后進(jìn)行主線(xiàn)集成會(huì)發(fā)生什么。

當(dāng) Violet 在第一個(gè)本地提交后就立即集成到主線(xiàn)時(shí)朗若,第一個(gè)變更是顯而易見(jiàn)的恼五。由于主線(xiàn)沒(méi)有任何更改,因此這就是一個(gè)簡(jiǎn)單的推送捡偏。


image.png

Scarlett 的第一個(gè)提交也需要主線(xiàn)集成唤冈,但是由于 Violet 先進(jìn)行了集成,因此 Scarlett 需要做一次合并银伟。但是由于她只需合并 V1 與 S1你虹,所以合并的工作量很小。

image.png

Scarlett 的下一個(gè)集成是一個(gè)簡(jiǎn)單的推送彤避,這意味著 Violet 的下一個(gè)提交也將需要與 Scarlett 的最近兩個(gè)提交合并傅物。但這仍然是一個(gè)很小的合并,僅僅是 Violet 的一個(gè)提交和 Scarlett 的兩個(gè)提交的合并琉预。

image.png

當(dāng)有外部的提交推送到主線(xiàn)時(shí)董饰,它會(huì)按照 Scarlett 和 Violet 正常的集成節(jié)奏被提取過(guò)來(lái)。

image.png

盡管它與以前發(fā)生的情況相似,但集成難度較小卒暂。Scarlett 這次只需要將 S3 與 M1 集成在一起啄栓,因?yàn)?S1 和 S2 已經(jīng)在主線(xiàn)上了。這意味著 Grayham 在推 M1 之前就必須集成主線(xiàn)上已經(jīng)存在的內(nèi)容(S1..2也祠,V1..2)昙楚。

開(kāi)發(fā)人員繼續(xù)進(jìn)行剩余的工作,并在每次提交時(shí)進(jìn)行集成诈嘿。


image.png

集成頻率對(duì)比

讓我們?cè)僬w看一下這兩張圖

低頻

image.png

高頻
image.png

這里有兩個(gè)非常明顯地區(qū)別堪旧。首先,顧名思義奖亚,高頻集成意味著做更多的集成——在這個(gè)小例子中淳梦,后者集成次數(shù)是前者的兩倍。但更重要的是昔字,這些集成比低頻例子中的集成要小得多爆袍。較小的集成意味著更少的工作量,因?yàn)榭赡芤饹_突的代碼更改會(huì)更少作郭。但是比減少工作量更重要的是螃宙,它也降低了風(fēng)險(xiǎn)。大規(guī)模合并的問(wèn)題與其認(rèn)為是處理合并產(chǎn)生的工作量所坯,還不如說(shuō)是這里面的不確定性谆扎。多數(shù)情況下,大規(guī)模的合并也會(huì)很順利芹助,但有時(shí)候堂湖,大規(guī)模合并會(huì)非常非常糟糕。偶爾的痛苦最后會(huì)比常態(tài)化的痛苦更糟状土。如果比較兩種情況无蜂,一種是每次集成需要額外花費(fèi) 10 分鐘,另一種是有 1/50 的概率需要花費(fèi) 6 小時(shí)做一次集成修復(fù)——我更喜歡哪個(gè)蒙谓?如果僅看花費(fèi)工作量斥季,那么 1/50 看起來(lái)更好,因?yàn)樗?6 小時(shí)而不是 8 小時(shí) 20 分鐘累驮。但是不確定性使 1/50 的案例變得更加糟糕酣倾,這種不確定性會(huì)導(dǎo)致集成恐懼。

集成恐懼

當(dāng)團(tuán)隊(duì)獲得一些糟糕的合并體驗(yàn)時(shí)谤专,他們往往會(huì)更謹(jǐn)慎地進(jìn)行集成躁锡。這很容易變成一種正反饋回路——像許多正反饋回路一樣,有著非常消極的后果置侍。(譯者注:正反饋回路也叫自增強(qiáng)回路映之,是一種疊加增強(qiáng)的過(guò)程)

最明顯的結(jié)果是拦焚,團(tuán)隊(duì)進(jìn)行集成的頻率降低了,這會(huì)導(dǎo)致更嚴(yán)重的合并沖突杠输,而合并沖突會(huì)導(dǎo)致更低的集成頻率……從而陷入惡性循環(huán)赎败。

一個(gè)更加不易察覺(jué)的問(wèn)題是,團(tuán)隊(duì)會(huì)停止執(zhí)行那些他們認(rèn)為會(huì)使集成變得更加困難的事情蠢甲。尤其是螟够,這會(huì)讓他們抗拒重構(gòu)。但是減少重構(gòu)會(huì)導(dǎo)致代碼庫(kù)變得越來(lái)越不健康峡钓,難以理解和修改,從而降低了團(tuán)隊(duì)的功能特性交付速度若河。由于完成功能特性所需的時(shí)間更長(zhǎng)能岩,因此進(jìn)一步增加了集成頻率(譯者注:原文可能有誤,這里應(yīng)該是降低集成頻率)萧福,從而使這種正反饋環(huán)路變得更不堪一擊拉鹃。

這個(gè)問(wèn)題有個(gè)反直覺(jué)的答案——“如果一件事令人痛苦……那就更頻繁地去做它”

讓我們從另一個(gè)角度來(lái)看這些頻率之間的差異。如果 Scarlett 和 Violet 在第一次提交時(shí)發(fā)生沖突鲫忍,會(huì)發(fā)生什么膏燕?他們將在何時(shí)發(fā)現(xiàn)出現(xiàn)了沖突?在低頻的例子中悟民,直到 Violet 最后一次合并坝辫,他們才發(fā)現(xiàn)沖突,因?yàn)槟鞘?S1 和 V1 第一次放到一起射亏。但是在高頻的例子中近忙,在 Scarlett 的第一次合并中就會(huì)發(fā)現(xiàn)它們。

低頻

image.png

高頻
image.png

頻繁的集成會(huì)增加合并的頻率智润,但可以降低合并的復(fù)雜性和風(fēng)險(xiǎn)及舍。頻繁的集成還可以提醒團(tuán)隊(duì)更快地解決沖突。當(dāng)然窟绷,這兩件事是聯(lián)系在一起的锯玛。糟糕的合并通常是團(tuán)隊(duì)工作中隱藏著沖突的結(jié)果,只有在進(jìn)行集成時(shí)才浮現(xiàn)出來(lái)兼蜈。

比如 Violet 正在看賬單計(jì)費(fèi)功能攘残,并且看到代碼的編寫(xiě)者有按一種特定的稅收制度評(píng)估稅額。而她的功能特性需要用不同的方式處理稅額为狸,因此最直接的方法是將稅額從賬單的計(jì)算中剔除肯腕,一會(huì)兒再把它作為獨(dú)立的功能進(jìn)行開(kāi)發(fā)。計(jì)費(fèi)功能僅在少數(shù)的幾個(gè)地方被調(diào)用钥平,因此使用“ 搬移語(yǔ)句到調(diào)用者”(譯者注:《重構(gòu):改善既有代碼的設(shè)計(jì)》8.4 )進(jìn)行重構(gòu)很容易——這讓程序在未來(lái)的演進(jìn)中更為合理实撒。然而姊途,Scarlett 不知道 Violet 正在做這件事,她按賬單函數(shù)處理稅款的假定實(shí)現(xiàn)她的功能特性知态。

自測(cè)代碼是我們的救命稻草捷兰。如果我們有一個(gè)強(qiáng)大的測(cè)試套件,把它作為健康分支的一部分使用负敏,將可以發(fā)現(xiàn)那些沖突贡茅,從而讓問(wèn)題進(jìn)入生產(chǎn)環(huán)境的可能性大大降低。但是其做,即使有強(qiáng)大的測(cè)試套件充當(dāng)了主線(xiàn)的看門(mén)人顶考,大規(guī)模集成依然令人頭疼。我們需要集成的代碼越多妖泄,發(fā)現(xiàn)問(wèn)題的難度就越大驹沿。我們也會(huì)有更大的概率遇到各種各樣妨礙運(yùn)行且難以理解的問(wèn)題。除了通過(guò)較小的提交來(lái)降低影響蹈胡,我們還可以使用“差異調(diào)試”來(lái)幫助定位哪一次變更導(dǎo)致問(wèn)題渊季。

很多人沒(méi)有意識(shí)到的是,源代碼控制系統(tǒng)其實(shí)是一種交流工具罚渐。它使 Scarlett 可以看到團(tuán)隊(duì)中其他人在做什么却汉。通過(guò)頻繁的集成,她不僅會(huì)在出現(xiàn)沖突時(shí)立即得到警告荷并,而且她還能更了解每個(gè)人都在干什么合砂,以及代碼庫(kù)是如何演進(jìn)的。我們不是一個(gè)人在沖鋒源织,而是和團(tuán)隊(duì)在一起工作既穆。

增加集成頻率是縮減功能特性大小的重要原因,同時(shí)這還有其他優(yōu)點(diǎn)雀鹃。功能越小幻工,構(gòu)建速度越快,投生速度越快黎茎,價(jià)值交付的啟動(dòng)也就越迅速囊颅。此外,較小的功能特性減少了反饋時(shí)間傅瞻,使團(tuán)隊(duì)可以在更加了解客戶(hù)后做出更好的功能決策踢代。

? 持續(xù)集成 ?

一旦有可共享的健康提交,開(kāi)發(fā)人員就進(jìn)行主線(xiàn)集成嗅骄,這樣的工作量通常是不到一天胳挎。

一旦團(tuán)隊(duì)在體驗(yàn)到高頻集成既高效又輕松后,很自然地就會(huì)問(wèn)“我們的集成頻率能有多快溺森?”慕爬。特性分支意味著變更集粒度的下限 —— 不可能有比內(nèi)聚的特性更小的粒度窑眯。

持續(xù)集成為集成提供了一種不同的觸發(fā)方式——只要在特性功能開(kāi)發(fā)上取得了大的進(jìn)展,并且分支仍然健康医窿,就可以集成磅甩。我們不指望功能特性已經(jīng)完整實(shí)現(xiàn),只要對(duì)代碼庫(kù)有足夠的修改就行姥卢。經(jīng)驗(yàn)法則是“每個(gè)人每天都要提交到主線(xiàn)”卷要,或者更確切地說(shuō),“本地代碼庫(kù)中永遠(yuǎn)不要存放超過(guò) 1 天未經(jīng)集成的代碼”独榴。實(shí)際上僧叉,大多數(shù)持續(xù)集成的踐行者每天會(huì)多次集成,他們樂(lè)于集成 1 小時(shí)或更少的工作棺榔。

要了解更多關(guān)于如何有效持續(xù)集成的詳細(xì)信息瓶堕,請(qǐng)查看我的詳細(xì)文章。欲了解更多細(xì)節(jié)掷豺,請(qǐng)查閱 Paul Duvall, Steve Matyas 和 Andrew Glover 的著作。Paul Hammant 維護(hù)了trunkbaseddevelopment.com薄声,其中有很多持續(xù)集成的技術(shù)当船。

使用持續(xù)集成的開(kāi)發(fā)人員需要習(xí)慣集成半成品達(dá)成頻繁集成的想法。他們還要考慮如何在運(yùn)行的系統(tǒng)中不暴露半成品來(lái)做到這一點(diǎn)默辨。通常這并不復(fù)雜——如果我正在實(shí)現(xiàn)一個(gè)依賴(lài)優(yōu)惠碼的折扣算法德频,而這個(gè)優(yōu)惠碼還不在有效列表中,那么我的代碼就不會(huì)被調(diào)用缩幸,即使已經(jīng)是生產(chǎn)版本壹置。同樣,如果我添加了一個(gè)功能表谊,詢(xún)問(wèn)保險(xiǎn)索賠人是否是吸煙者钞护,我可以構(gòu)建和測(cè)試代碼背后的邏輯,并通過(guò)將詢(xún)問(wèn)問(wèn)題的用戶(hù)界面留到構(gòu)建這個(gè)特性的最后一天再做爆办,來(lái)確保它不會(huì)在生產(chǎn)中被使用难咕。通過(guò)最后連接接口映射(Keystone Interface)來(lái)隱藏半成品通常是一種有效的技術(shù)。

如果沒(méi)法輕松地隱藏掉半成品距辆,我們可以使用 特性開(kāi)關(guān)余佃。除了隱藏半成品之外,特性開(kāi)關(guān)還可以有選擇地向其中一部分用戶(hù)顯示某一功能特性——這通常便于逐步推出一個(gè)新的功能特性跨算。

集成半成品尤其會(huì)引起那些擔(dān)心主線(xiàn)中有錯(cuò)誤代碼者的憂(yōu)心爆土。因此,使用持續(xù)集成需要自測(cè)代碼诸蚕,這樣就有信心把半成品合并到主線(xiàn)步势,而不會(huì)增加出現(xiàn)錯(cuò)誤的幾率氧猬。這種方法要求開(kāi)發(fā)人員在編寫(xiě)功能代碼時(shí),為半成品編寫(xiě)測(cè)試立润,并將功能代碼和測(cè)試一起提交到主線(xiàn)中 (或許可以用測(cè)試驅(qū)動(dòng)開(kāi)發(fā))狂窑。

就本地代碼庫(kù)而言,大多數(shù)使用持續(xù)集成的人不會(huì)想要在單獨(dú)的本地分支上工作桑腮。通常是直接在本地 master 分支上提交泉哈,工作完成后進(jìn)行主線(xiàn)集成。然而破讨,如果開(kāi)發(fā)人員喜歡的話(huà)丛晦,開(kāi)一個(gè)特性分支并在上面工作,每隔一段時(shí)間就集成回本地 master 分支和主線(xiàn)提陶,那也相當(dāng)不錯(cuò)烫沙。特性分支開(kāi)發(fā)與持續(xù)集成之間的區(qū)別,不在于是否有特征分支隙笆,而是在于開(kāi)發(fā)人員何時(shí)與主線(xiàn)集成锌蓄。

適用場(chǎng)景

持續(xù)集成是特性分支開(kāi)發(fā)的另一種選擇。兩者之間的權(quán)衡值得在本文中用單獨(dú)的章節(jié)描述撑柔,下面將對(duì)這兩者進(jìn)行對(duì)比瘸爽。

持續(xù)集成和基于主干開(kāi)發(fā)

在 ThoughtWorks 于 2000 年開(kāi)始使用持續(xù)集成時(shí),我們編寫(xiě)了 CruiseControl, 這是一個(gè)守護(hù)程序铅忿,每當(dāng)有代碼提交到主線(xiàn)后剪决,就會(huì)自動(dòng)構(gòu)建軟件產(chǎn)品。從那時(shí)起檀训,許多這樣的工具 (如 Jenkins柑潦、TeamCity、Travis CI峻凫、Circle CI渗鬼、Bamboo 等等) 被開(kāi)發(fā)出來(lái)。但是大多數(shù)使用這些工具的組織都是在提交時(shí)自動(dòng)構(gòu)建特性分支——這雖然有用荧琼,但也意味著這些組織并非真正在實(shí)踐持續(xù)集成乍钻。(還不如叫它們持續(xù)構(gòu)建工具。)

因?yàn)檫@樣的語(yǔ)義擴(kuò)散铭腕,有些人開(kāi)始使用 “主干開(kāi)發(fā)” 一詞來(lái)代替“持續(xù)集成”银择。(部分人確實(shí)對(duì)這兩個(gè)術(shù)語(yǔ)進(jìn)行了細(xì)微的區(qū)分,但是沒(méi)有一致的用法)累舷。雖然在語(yǔ)言方面我通常是描述派浩考,但我更喜歡使用 “持續(xù)集成”。一部分原因是我不認(rèn)為試圖不斷提出新術(shù)語(yǔ)是對(duì)抗語(yǔ)義擴(kuò)散的可行方法被盈。然而析孽,或許更主要的原因是搭伤,我認(rèn)為改變術(shù)語(yǔ)將粗暴地抹殺早期的極限編程先驅(qū)者們的貢獻(xiàn),尤其是 Kent Beck 的袜瞬,他在 20 世紀(jì) 90 年代創(chuàng)造并明確定義了持續(xù)集成的實(shí)踐怜俐。

? 對(duì)比特性分支開(kāi)發(fā)和持續(xù)集成 ?

目前,特性分支看起來(lái)是業(yè)界最常見(jiàn)的分支策略邓尤,但是一些實(shí)踐者強(qiáng)烈認(rèn)為持續(xù)集成是一種更好的方法拍鲤。持續(xù)集成的主要優(yōu)勢(shì)是支持更高的集成頻率,而且通常是高很多的集成頻率汞扎。

集成頻率的差異取決于團(tuán)隊(duì)能夠把功能拆分到多小季稳。如果團(tuán)隊(duì)拆分的所有功能特性都可以在一天之內(nèi)完成,那么他們既可以實(shí)行特性分支開(kāi)發(fā)澈魄,也可實(shí)行持續(xù)集成景鼠。但是大多數(shù)團(tuán)隊(duì)的特性持續(xù)時(shí)間都比這更長(zhǎng)——特性持續(xù)的時(shí)間越長(zhǎng),這兩種模式之間的差異就越大痹扇。

正如我已經(jīng)指出的那樣铛漓,更高的集成頻率可以減少?gòu)?fù)雜的集成,并減少對(duì)集成的恐懼鲫构。這通常是一件很難溝通的事情浓恶。如果你生活在每隔幾周或幾個(gè)月進(jìn)行集成的世界中,那么集成很可能是一項(xiàng)令人焦慮的活動(dòng)芬迄。很難相信一天可以進(jìn)行很多次集成问顷。但集成是可通過(guò)加快頻率降低難度的事情之一昂秃。這是一種違反直覺(jué)的想法——“如果一件事令人痛苦——那就更頻繁地去做它”禀梳。集成的規(guī)模越小,集成就越不可能變成充滿(mǎn)痛苦和絕望的史詩(shī)般的合并肠骆。對(duì)于特性分支開(kāi)發(fā)算途,高頻集成鼓勵(lì)更小的特性規(guī)模:幾天而不是幾周(幾個(gè)月根本行不通)。

持續(xù)集成使團(tuán)隊(duì)可以從高頻集成中受益蚀腿,同時(shí)將特性規(guī)模與集成頻率解耦嘴瓤。如果團(tuán)隊(duì)更喜歡一兩個(gè)星期的特性粒度,持續(xù)集成支持這樣的粒度拆分莉钙,同時(shí)仍讓團(tuán)隊(duì)獲得最高集成頻率的所有好處廓脆。合并規(guī)模越小,所需的工作越少磁玉。更重要的是停忿,正如我在上文中所解釋的,更頻繁地進(jìn)行合并可以減少出現(xiàn)極為糟糕的合并的風(fēng)險(xiǎn)蚊伞,這既消除了這種合并帶來(lái)的驚嚇席赂,也減少了合并的整體恐懼感吮铭。如果代碼中出現(xiàn)沖突叠蝇,則高頻集成會(huì)在導(dǎo)致這些討厭的集成問(wèn)題之前迅速發(fā)現(xiàn)它們栅表。持續(xù)集成可為團(tuán)隊(duì)帶來(lái)極強(qiáng)的效益,以至于有的團(tuán)隊(duì)蚕涤,有些功能只需幾天完成癞揉,還依舊在做持續(xù)集成纸肉。

持續(xù)集成的明顯缺點(diǎn)是,缺乏向主線(xiàn)進(jìn)行最重要的集成的封閉烧董。如果一個(gè)團(tuán)隊(duì)不善于保持健康的分支毁靶,這不僅是一個(gè)對(duì)失敗的慶祝(譯者注:慶祝失敗是為了改進(jìn)),更是一個(gè)風(fēng)險(xiǎn)逊移。將一個(gè)功能特性的所有提交聚在一起预吆,還可以在后期決定是否在即將發(fā)布的版本中包含一個(gè)特性。雖然功能開(kāi)關(guān)允許從用戶(hù)角度打開(kāi)或關(guān)閉功能胳泉,但該功能的代碼仍在產(chǎn)品中拐叉。對(duì)這一點(diǎn)的擔(dān)憂(yōu)通常會(huì)被過(guò)分夸大,畢竟代碼不會(huì)太重要扇商,但這確實(shí)意味著想要實(shí)行持續(xù)集成的團(tuán)隊(duì)必須開(kāi)發(fā)一組強(qiáng)大的測(cè)試集凤瘦,以便他們可以確信主線(xiàn)能保持健康,即使每天進(jìn)行多次集成案铺。有些團(tuán)隊(duì)覺(jué)得這種技能是難以想象的蔬芥,但另一些團(tuán)隊(duì)則認(rèn)為這不僅是可能的而且游刃有余。此先決條件確實(shí)意味著控汉,特性分支開(kāi)發(fā)這種方式更適合那些不強(qiáng)制保持健康分支笔诵、并且需要用發(fā)布分支在發(fā)布之前穩(wěn)定代碼的團(tuán)隊(duì)。

雖然合并的規(guī)模大小和不確定性是特性分支開(kāi)發(fā)最明顯的問(wèn)題姑子,但最大的問(wèn)題可能是特性分支開(kāi)發(fā)遏制重構(gòu)乎婿。定期進(jìn)行且?guī)缀鯖](méi)有沖突的重構(gòu)最為有效。重構(gòu)會(huì)引入沖突街佑,如果這些沖突沒(méi)有被發(fā)現(xiàn)并迅速解決谢翎,合并就會(huì)變得困難重重。因此沐旨,重構(gòu)在高頻集成中效果最好森逮,所以重構(gòu)作為極限編程 (Extreme Programming)的一部分流行起來(lái)也不足為奇,而且持續(xù)集成也是極限編程最初的實(shí)踐之一磁携。特性分支開(kāi)發(fā)也不鼓勵(lì)開(kāi)發(fā)人員做當(dāng)前特性外的更改褒侧,這會(huì)破壞團(tuán)隊(duì)的重構(gòu)能力,影響代碼庫(kù)穩(wěn)定性的提升。

當(dāng)我遇到有關(guān)軟件開(kāi)發(fā)實(shí)踐的科學(xué)研究時(shí)璃搜,由于他們的方法學(xué)存在嚴(yán)重問(wèn)題拖吼,通常我并不買(mǎi)賬。但《DevOps 現(xiàn)狀調(diào)查報(bào)告》是一個(gè)例外这吻,該報(bào)告揭露了軟件交付效能的度量指標(biāo)吊档,并將其與更廣泛的組織績(jī)效度量相關(guān)聯(lián),而組織績(jī)效又與投資回報(bào)率和盈利能力等業(yè)務(wù)度量指標(biāo)相關(guān)唾糯。在2016年怠硼,他們首先評(píng)估了持續(xù)集成,發(fā)現(xiàn)它有助于提高軟件開(kāi)發(fā)效能移怯,此后的每項(xiàng)調(diào)查中都重復(fù)印證了這一發(fā)現(xiàn)香璃。

我們發(fā)現(xiàn)在合并到主干之前,具有極短生命周期(少于一天)的分支或派生舟误,并且總共少于三個(gè)活動(dòng)分支葡秒,是持續(xù)交付的重要特征,并且所有這些都有助于提高績(jī)效嵌溢。每天將代碼合并到主干或 master 中也是如此眯牧。

——《 2016 年 DevOps 現(xiàn)狀調(diào)查報(bào)告》

使用持續(xù)集成并不會(huì)消除保持功能粒度小的其他優(yōu)勢(shì)。頻繁發(fā)布小的功能特性可提供快速的反饋周期赖草,從而為改進(jìn)產(chǎn)品創(chuàng)造奇跡学少。許多使用持續(xù)集成的團(tuán)隊(duì)還在努力構(gòu)建產(chǎn)品的分層,并盡可能頻繁地發(fā)布新功能秧骑。

特性分支開(kāi)發(fā) 持續(xù)集成
√ 一個(gè)特性的所有代碼可以作為一個(gè)單元質(zhì)量評(píng)估 √ 支持比功能特性粒度更高頻的集成
√ 功能特性代碼僅在功能完成后才添加到產(chǎn)品中 √ 縮短發(fā)現(xiàn)沖突的時(shí)間
× 較低頻率的合并 √ 較小的合并
√ 鼓勵(lì)重構(gòu)
× 需要維持健康分支(和相關(guān)自測(cè)代碼)的投入
√ 科學(xué)證據(jù)表明它有助于提高軟件交付績(jī)效

特性分支和開(kāi)源

許多人將特性分支開(kāi)發(fā)的流行歸因于github 和起源于開(kāi)源開(kāi)發(fā)的拉取請(qǐng)求模型版确。有鑒于此,有必要了解一下開(kāi)源工作與許多商業(yè)軟件開(kāi)發(fā)之間截然不同的環(huán)境乎折。開(kāi)源項(xiàng)目的結(jié)構(gòu)有許多不同的方式绒疗,但是一個(gè)常見(jiàn)的結(jié)構(gòu)是一個(gè)人或一小群人作為開(kāi)源項(xiàng)目的維護(hù)者,承擔(dān)大部分編程工作笆檀。維護(hù)者與更多的開(kāi)發(fā)貢獻(xiàn)者一起工作忌堂。維護(hù)者通常不了解貢獻(xiàn)者盒至,因此對(duì)他們貢獻(xiàn)的代碼的質(zhì)量一無(wú)所知酗洒。維護(hù)者還不確定貢獻(xiàn)者將在開(kāi)源項(xiàng)目中實(shí)際投入多少時(shí)間,更不用說(shuō)他們的工作成效枷遂。

在這種情況下樱衷,特性分支開(kāi)發(fā)非常有意義。如果有人要添加一個(gè)或大或小的功能酒唉,而我不知道這項(xiàng)功能什么時(shí)候(或者是否)會(huì)被完成矩桂,那么對(duì)我來(lái)說(shuō),等到它完成后再集成是有意義的痪伦。另外侄榴,更為重要的是要能夠?qū)徍舜a雹锣,以確保它通過(guò)我為代碼庫(kù)設(shè)置的任何質(zhì)量門(mén)禁。

但是許多商業(yè)軟件團(tuán)隊(duì)的工作環(huán)境截然不同癞蚕。有一個(gè)全職的團(tuán)隊(duì)蕊爵,他們?nèi)紴檐浖_(kāi)發(fā)投入大量時(shí)間,通常是全職的桦山。項(xiàng)目負(fù)責(zé)人非常了解這些人(除了剛開(kāi)始的時(shí)候)攒射,并且可以對(duì)代碼質(zhì)量和交付能力有可靠的預(yù)期。由于他們是帶薪雇員恒水,項(xiàng)目負(fù)責(zé)人對(duì)項(xiàng)目投入的時(shí)間会放,編碼標(biāo)準(zhǔn)和團(tuán)隊(duì)習(xí)慣也有更好的掌控。

在這迥然不同的環(huán)境下钉凌,應(yīng)該清楚地知道咧最,此類(lèi)商業(yè)團(tuán)隊(duì)的分支策略不必與在開(kāi)源世界運(yùn)用的分支策略相同。持續(xù)集成幾乎不可能適合偶爾為開(kāi)源工作做出貢獻(xiàn)的人御雕,但是對(duì)于商業(yè)工作而言窗市,這是一個(gè)現(xiàn)實(shí)的選擇方案。團(tuán)隊(duì)不應(yīng)假定那些在開(kāi)源環(huán)境行得通的做法可以自動(dòng)適應(yīng)他們與之不同的工作環(huán)境饮笛。

? 對(duì)提交評(píng)審 ?

每個(gè)對(duì)主線(xiàn)的提交都要先經(jīng)同行評(píng)審才會(huì)被接納咨察。

長(zhǎng)期以來(lái),代碼審查一直被推薦用于提升代碼質(zhì)量福青,提高模塊化和可讀性摄狱,以及消除缺陷。盡管如此无午,商業(yè)機(jī)構(gòu)往往發(fā)現(xiàn)很難把代碼審查融入到軟件開(kāi)發(fā)工作流程中媒役。然而,開(kāi)源世界廣泛采用了這樣的信念:在項(xiàng)目貢獻(xiàn)被接受納入項(xiàng)目主線(xiàn)之前宪迟,應(yīng)先對(duì)其進(jìn)行評(píng)審酣衷,并且這種方式近年來(lái)在開(kāi)發(fā)組織中廣泛傳播,尤其是在硅谷次泽。這樣的工作流程特別適合 GitHub 的拉取請(qǐng)求機(jī)制穿仪。

類(lèi)似這樣的流程會(huì)在 Scarlett 完成希望被集成的工作內(nèi)容時(shí)開(kāi)始。一旦她成功完成構(gòu)建意荤,就要進(jìn)行主線(xiàn)集成(如果她的團(tuán)隊(duì)有這樣的慣例)啊片,但是在推送到主線(xiàn)前,她要先發(fā)送她的提交進(jìn)行評(píng)審玖像。團(tuán)隊(duì)的其他成員紫谷,例如 Violet,接著對(duì)這個(gè)提交進(jìn)行代碼審核。如果她認(rèn)為提交有問(wèn)題笤昨,會(huì)反饋一些意見(jiàn)祖驱,然后會(huì)有一些反復(fù),直到 Scarlett 和 Violet 都滿(mǎn)意為止瞒窒。提交只有在通過(guò)評(píng)審后才會(huì)被納入主線(xiàn)羹膳。

對(duì)提交評(píng)審(Reviewed Commits)在開(kāi)源中越來(lái)越受歡迎,它非常適合由提交維護(hù)者和臨時(shí)貢獻(xiàn)者組成這樣模式的組織根竿。對(duì)提交評(píng)審使得維護(hù)人員可以密切關(guān)注任何一個(gè)貢獻(xiàn)陵像,也非常適合特性分支開(kāi)發(fā),因?yàn)橐粋€(gè)完成的特性清晰地標(biāo)記出需要代碼評(píng)審的節(jié)點(diǎn)寇壳。如果您不確定貢獻(xiàn)者是否完成了功能醒颖,為什么還要評(píng)審他們的半成品?最好還是等功能完成時(shí)再做壳炎。這種做法在更大的互聯(lián)網(wǎng)公司中也廣泛傳播泞歉,Google 和 Facebook 都開(kāi)發(fā)有專(zhuān)用工具支持平滑開(kāi)展對(duì)提交評(píng)審。

約定及時(shí)對(duì)提交評(píng)審的行為準(zhǔn)則非常重要匿辩。如果開(kāi)發(fā)人員完成了某項(xiàng)工作腰耙,并花了幾天時(shí)間進(jìn)行其他工作,那么當(dāng)他們收到返回的評(píng)審意見(jiàn)時(shí)铲球,他們對(duì)被評(píng)審工作的印象已經(jīng)不再清晰挺庞。如果被評(píng)審的提交是已經(jīng)完成的功能,這會(huì)令人沮喪稼病,但對(duì)于部分完成的功能选侨,情況會(huì)嚴(yán)重得多,因?yàn)樵诖_認(rèn)評(píng)審?fù)ㄟ^(guò)之前然走,工作可能很難進(jìn)一步開(kāi)展援制。理論上,可以結(jié)合對(duì)提交評(píng)審來(lái)進(jìn)行持續(xù)集成芍瑞,而且實(shí)踐上也確實(shí)是有可能的—— Google 就遵循這個(gè)方法晨仑。但是,盡管可能拆檬,但很難執(zhí)行洪己,而且相對(duì)罕見(jiàn)。對(duì)提交評(píng)審和特性分支開(kāi)發(fā)是更為常見(jiàn)的組合秩仆。

適用場(chǎng)景

將開(kāi)源軟件和私有軟件開(kāi)發(fā)團(tuán)隊(duì)的需求混為一談就像是當(dāng)前軟件開(kāi)發(fā)儀式的原罪码泛』猓—— Camille Fournier

盡管在過(guò)去十年中澄耍,對(duì)提交評(píng)審已成為一種流行的做法,但仍有弊端和替代方案。即使做得很好齐莲,對(duì)提交評(píng)審也總是會(huì)在集成過(guò)程中引入一些延遲痢站,從而導(dǎo)致了更低的集成頻率。結(jié)對(duì)編程提供了持續(xù)的代碼審核過(guò)程选酗,帶來(lái)比等待代碼評(píng)審更快的反饋周期阵难。(就像持續(xù)集成和重構(gòu)一樣,結(jié)對(duì)編程是極限編程最初的實(shí)踐之一)芒填。

許多使用對(duì)提交評(píng)審的團(tuán)隊(duì)并沒(méi)有做到足夠迅速呜叫。他們能夠提供有價(jià)值的反饋往往因?yàn)閬?lái)得太遲而不再有效。那時(shí)就會(huì)面臨一個(gè)令人尷尬的選擇殿衰,要么大量返工朱庆,要么接受能行得通但損害代碼庫(kù)質(zhì)量的工作。

代碼評(píng)審并不局限于只在代碼合入主線(xiàn)前進(jìn)行闷祥。許多技術(shù)領(lǐng)導(dǎo)者發(fā)現(xiàn)在提交后評(píng)審代碼會(huì)很有用娱颊,當(dāng)他們發(fā)現(xiàn)問(wèn)題時(shí),就可以及時(shí)與開(kāi)發(fā)人員聯(lián)系凯砍。重構(gòu)文化在這里是非常有價(jià)值箱硕,做得好可以形成一種社區(qū)氛圍,團(tuán)隊(duì)中的每個(gè)人都將定期評(píng)審代碼庫(kù)中的代碼并修復(fù)他們看到的問(wèn)題悟衩。

圍繞對(duì)提交評(píng)審的利弊權(quán)衡主要取決于團(tuán)隊(duì)的社會(huì)結(jié)構(gòu)剧罩。正如我已經(jīng)提到的,開(kāi)源項(xiàng)目通常具有一些受信任的維護(hù)者和許多不受信任的貢獻(xiàn)者的結(jié)構(gòu)座泳。商業(yè)團(tuán)隊(duì)通常都是全職的斑响,但結(jié)構(gòu)可能相似。項(xiàng)目負(fù)責(zé)人(類(lèi)似于一個(gè)維護(hù)者)信賴(lài)一小組(也可能是某個(gè))維護(hù)者钳榨,并且對(duì)團(tuán)隊(duì)其他成員貢獻(xiàn)的代碼保持警惕舰罚。團(tuán)隊(duì)成員可能同時(shí)分配到多個(gè)項(xiàng)目中,使他們更像開(kāi)源貢獻(xiàn)者薛耻。如果存在這樣的社會(huì)結(jié)構(gòu)营罢,那么對(duì)提交評(píng)審和特性分支開(kāi)發(fā)將具有很大的意義。但是饼齿,團(tuán)隊(duì)在互相具有較高信任度時(shí)饲漾,通常能找到機(jī)制來(lái)保持代碼高質(zhì)量,且不會(huì)增加集成過(guò)程的沖突缕溉。

因此考传,盡管對(duì)提交評(píng)審可以是一種有價(jià)值的實(shí)踐,但并不是通向健康代碼庫(kù)的必要途徑证鸥。如果你希望團(tuán)隊(duì)平衡成長(zhǎng)僚楞,而不過(guò)度依賴(lài)其最初的領(lǐng)導(dǎo)者時(shí)尤其如此勤晚。

集成阻力

對(duì)提交評(píng)審的問(wèn)題之一,是它往往讓集成變得更加麻煩泉褐。這是集成阻力(Integration Friction)的一個(gè)例子——這些活動(dòng)讓集成耗時(shí)或費(fèi)力赐写。集成阻力越多,開(kāi)發(fā)人員就越傾向于降低集成頻率膜赃。想象某個(gè) (功能不健全的) 組織堅(jiān)持認(rèn)為所有對(duì)主線(xiàn)的提交都要填寫(xiě)一份需要耗時(shí)半小時(shí)的表格挺邀。這樣的制度會(huì)阻礙人們頻繁集成。無(wú)論你對(duì)特性分支開(kāi)發(fā)和持續(xù)集成的態(tài)度如何跳座,審視任何增加這種沖突的東西都是有價(jià)值的端铛。任何這樣的沖突都應(yīng)該被移除,除非它有明顯的增值作用疲眷。

拉取請(qǐng)求增加了額外的開(kāi)銷(xiāo)以應(yīng)對(duì)低信任度情景沦补,例如,允許你不認(rèn)識(shí)的人為你的項(xiàng)目做出貢獻(xiàn)咪橙。而把拉取請(qǐng)求強(qiáng)加給你自己團(tuán)隊(duì)中的開(kāi)發(fā)人員夕膀,就像讓你的家人通過(guò)機(jī)場(chǎng)安檢進(jìn)入你家一樣∶勒欤—— Kief Morris

手動(dòng)過(guò)程是這里常見(jiàn)的沖突源产舞,尤其是當(dāng)涉及與不同組織的協(xié)調(diào)時(shí)。這種摩擦通巢な#可以通過(guò)使用自動(dòng)化流程易猫、加強(qiáng)開(kāi)發(fā)人員培訓(xùn) (以消除需求) 以及將步驟推到部署流水線(xiàn)或生產(chǎn)中質(zhì)量保證的后續(xù)步驟來(lái)減少。您可以在關(guān)于持續(xù)集成和持續(xù)交付的資料中找到更多消除這種沖突的方法具壮。這種沖突也會(huì)在生產(chǎn)的路徑中出現(xiàn)准颓,有著同樣的困難和處理方法。

讓人們不愿意考慮持續(xù)集成的原因之一是棺妓,設(shè)想他們只在集成阻力嚴(yán)重的環(huán)境中工作過(guò)攘已。如果做一次集成需要一個(gè)小時(shí),那么一天做幾次集成顯然是荒謬的怜跑。而如果加入一個(gè)團(tuán)隊(duì)样勃,在那里集成是一個(gè)分分鐘可以完成的小事,就會(huì)感覺(jué)像是一個(gè)完全不同的世界性芬。關(guān)于特征分支開(kāi)發(fā)和持續(xù)集成優(yōu)點(diǎn)的許多爭(zhēng)論是混亂復(fù)雜的峡眶,我懷疑就是因?yàn)槿藗儧](méi)有經(jīng)歷過(guò)這兩個(gè)世界,因此不能完全理解這兩種觀(guān)點(diǎn)植锉。

文化因素影響集成阻力——尤其是團(tuán)隊(duì)成員之間的信任辫樱。如果我是一個(gè)團(tuán)隊(duì)的領(lǐng)導(dǎo)者,而我不信任我的同事會(huì)做得很好俊庇,那么我很可能會(huì)想要阻止損害代碼庫(kù)的提交狮暑。這自然也是對(duì)提交評(píng)審的驅(qū)動(dòng)因素之一鸡挠。但如果我在信任同事判斷的一個(gè)團(tuán)隊(duì)里,那么我可能會(huì)更愿意接受提交后的審查心例,或者完全砍掉審查宵凌,而去依靠集體重構(gòu)來(lái)解決問(wèn)題鞋囊。在這種環(huán)境下止后,我的收獲是消除了提交前評(píng)審所帶來(lái)的摩擦,從而鼓勵(lì)了更高頻率的集成溜腐。團(tuán)隊(duì)信任通常是特性分支與持續(xù)集成爭(zhēng)論的最重要因素译株。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挺益,隨后出現(xiàn)的幾起案子歉糜,更是在濱河造成了極大的恐慌,老刑警劉巖望众,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匪补,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡烂翰,警方通過(guò)查閱死者的電腦和手機(jī)夯缺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)甘耿,“玉大人踊兜,你說(shuō)我怎么就攤上這事〖烟瘢” “怎么了捏境?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)毁葱。 經(jīng)常有香客問(wèn)我垫言,道長(zhǎng),這世上最難降的妖魔是什么倾剿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任骏掀,我火速辦了婚禮,結(jié)果婚禮上柱告,老公的妹妹穿的比我還像新娘截驮。我一直安慰自己,他們只是感情好际度,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布葵袭。 她就那樣靜靜地躺著,像睡著了一般乖菱。 火紅的嫁衣襯著肌膚如雪坡锡。 梳的紋絲不亂的頭發(fā)上蓬网,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音鹉勒,去河邊找鬼帆锋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛禽额,可吹牛的內(nèi)容都是我干的锯厢。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼脯倒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼实辑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起藻丢,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤剪撬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后悠反,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體残黑,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年斋否,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了梨水。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡如叼,死狀恐怖冰木,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笼恰,我是刑警寧澤踊沸,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站社证,受9級(jí)特大地震影響逼龟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜追葡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一腺律、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宜肉,春花似錦匀钧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至遣铝,卻和暖如春佑刷,著一層夾襖步出監(jiān)牢的瞬間莉擒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工瘫絮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涨冀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓麦萤,卻偏偏與公主長(zhǎng)得像鹿鳖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子频鉴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350