|
英文原文:On DVCS, continuous integration, and feature branches
翻譯:?jiǎn)塘?/p>
為了吸引大家的注意力,我想說:“特性分支是邪惡的化身”。
自2008年起,Mercurial (最近是Git)就成了我日常工作的工具,而且我喜歡使用分布式版本控制系統(tǒng)。正如《持續(xù)交付》一書中討論的那樣(英文版第393頁(yè)和394頁(yè)),有很多理由說明,與之前已存在的同類工具相比,DVCS代表了一種巨大的轉(zhuǎn)變。但正如所有強(qiáng)大的工具一樣,你會(huì)有很多種方法來使用它們,但并不是所有的方法都是好的。這里所有的討論不是想說DVCS不好:使用特性分支和使用DVCS完全是正交的。而且,我認(rèn)為,DVCS的支持者用這種工具的功能分支來推銷DVCS,是對(duì)DVCS的一種傷害。
首先看幾個(gè)定義。請(qǐng)注意,一些人以不同的方式使用這些術(shù)語(yǔ),所以你需要暫時(shí)從你的大腦中把先把其它的定義擦去,否則我的討論就沒有什么意義了。
持續(xù)集成是一種實(shí)踐,用于確保你的軟件一直是可以工作的,并且在幾分鐘內(nèi)你就能夠得到關(guān)于 “你的修改是否破壞了系統(tǒng)”的反饋。
特性分支是一種實(shí)踐, 使用它的人直到他所開發(fā)的特性“完成”后才合并回主干(這里的“完成“不是“done done”原則中的那個(gè)“完成”)。
主干是指開發(fā)主線 —— 通常是指版本控制庫(kù)中的。你的某次構(gòu)建就是從這個(gè)主干中的一個(gè)版本創(chuàng)建的,并會(huì)被放到你的部署流水線中。 注意,這些定義對(duì)DVCS和其它開源項(xiàng)目,甚至是GitHub,也完全適用。
先讓我們解決一個(gè)象稻草人一樣的爭(zhēng)論。當(dāng)使用版本控制工具時(shí),你就使用一個(gè)分支來工作:即本地工作目錄。使用DVCS,只是多了一個(gè)層次而已,因?yàn)槟愕谋镜貛?kù)就是一個(gè)有效分支。我并不反對(duì)創(chuàng)建分支。我所不贊同的是:讓你最終要發(fā)布的代碼在分支上堆積起來。
下面是我所看到的狀況。當(dāng)你將最終要發(fā)布的代碼大量地堆積在分支上,會(huì)有幾個(gè)糟糕的事情發(fā)生:
- 這種狀態(tài)持續(xù)時(shí)間越長(zhǎng),就越難合并。因?yàn)殡S著其他人向主干提交,主干的代碼與你的分支上的代碼之間的差異會(huì)越來越大。強(qiáng)大的合并工具的確會(huì)在一定程度上幫助你,但是任何一個(gè)做過很多開發(fā)工作的人都會(huì)有過這樣的經(jīng)歷,即:代碼的確成功合并了,但是應(yīng)用程序運(yùn)行不起來。隨著需要合并的那些代碼數(shù)量的增加,以及初始分支與最終合并時(shí)間的增長(zhǎng),發(fā)生這種事情的可能性是遞增的,而且不僅僅是線性遞增。
- 在分支上的工作越多,在合并到主干時(shí)越有可能破壞系統(tǒng)。每個(gè)人都有這樣的經(jīng)歷:有個(gè)問題出現(xiàn)了,你似乎找到了一個(gè)非常聰明的解決方案。可是,幾個(gè)小時(shí)(或者幾天)后,你才發(fā)現(xiàn)需要廢棄所有的東西,或者(更常見地)那些引起未預(yù)期后果的代碼提交。
- 當(dāng)多個(gè)開發(fā)人員同時(shí)在這個(gè)代碼庫(kù)上工作,并使用特性分支開發(fā)時(shí),使代碼重構(gòu)變得很難。如果我重構(gòu)并且提交到主干了,而其他人在其分支上已經(jīng)修改了很多東西的話,當(dāng)他們向主干合并時(shí)就更難了。這就會(huì)驅(qū)使我不做重構(gòu)。而不充分的重構(gòu)就等于劣質(zhì)代碼。
當(dāng)大家頻繁將其代碼合并到主干時(shí),就沒有這些問題了。如果不這么做的話,隨著團(tuán)隊(duì)人數(shù)的增加,這些問題的痛苦會(huì)成指數(shù)級(jí)增加。而且,還有一個(gè)惡性循環(huán): 這種痛苦的自然反應(yīng)是:更少做合并。正如我喜歡說的,當(dāng)某些事情令你很痛苦時(shí),解決方案是更頻繁地做,并將痛苦提前。在這里,解決方案是每個(gè)人都更頻繁地合并到主干。
然而,如果你正在做的一個(gè)功能中需要做很多工作,或者你對(duì)系統(tǒng)進(jìn)行大面積修改時(shí),這么做就比較困難了。下面是一些解決方案:
- 將Story分解成一些更小塊的工作(有時(shí)被叫做Task)。我還沒有遇到過一大塊工作無法分解成更小塊的工作 —— 通常少于1小時(shí),并且肯定用不了一天時(shí)間 —— 這樣讓我既能達(dá)到目標(biāo),又能保證系統(tǒng)可工作且可發(fā)布。這需要細(xì)心的分析、討論、思考和嚴(yán)格的紀(jì)律。當(dāng)我無法找到在增量開發(fā)方式下幾小時(shí)內(nèi)可以完成的工作時(shí),我就會(huì)先對(duì)某些想法做一些試驗(yàn)(敏捷中叫做Spike)。至關(guān)重要的點(diǎn)在于:我能盡早地對(duì)我的方案進(jìn)行快速驗(yàn)證:是可以解決問題,還是會(huì)對(duì)系統(tǒng)造成不良后果,影響了其他人的工作,或者引出了回歸缺陷(這也是持續(xù)集成的動(dòng)機(jī)所在)。
- 在實(shí)現(xiàn)Story時(shí),最后再做面向用戶那部分接口。先從業(yè)務(wù)邏輯及以下部分下手。使用TDD完善你的代碼。頻繁提交,與主干合并。最后再完成界面部分。
- 使用抽象分支方法來對(duì)復(fù)雜或大范圍變更進(jìn)行增量開發(fā),同時(shí)保證系統(tǒng)一直處于可工作狀態(tài)。
你怎么知道什么時(shí)候沒有合并的東西太多了呢?想像一下,假如你是一個(gè)開源項(xiàng)目的維護(hù)者,有一個(gè)你不認(rèn)識(shí)的剛剛提交了一個(gè)補(bǔ)丁。你會(huì)馬上合并它嗎?你能記住它與主干之間的所有diff嗎?你能保證在不需要問你的前提下,團(tuán)隊(duì)其他人員能在一分鐘之內(nèi)就能清楚地理解這些diff嗎?如果你對(duì)上述所有問題的回答不是“Yes”, 那么你就要停止工作,將其分成更小的工作單元。
很明顯,只要 “特性”足夠小,我并不真正反對(duì)特性分支。然而,通常使用特性分支的人大多無法通過上一段中的問題測(cè)試。真正有經(jīng)驗(yàn)的開發(fā)人員能理解使用特性分支與為了高效使用它而建立的紀(jì)律之間的平衡,但仍舊有一定的危險(xiǎn) —— GitHub就被Forks搞得很亂,這些Forks很多是不可合并的,因?yàn)樗鼈兣c主干的差異太大了。
我更想強(qiáng)調(diào)的是下面這個(gè)觀點(diǎn),即:讓“盡早地持續(xù)地交付有價(jià)值的軟件” 中最重要的實(shí)踐之一就是確保你的系統(tǒng)一直是可工作的。
對(duì)于開發(fā)人員來說,達(dá)到這一目標(biāo)最好的方法是:確保他們可以將“提交可能對(duì)系統(tǒng)造成破壞”的風(fēng)險(xiǎn)最小化。我們可以通過“保持小步提交、在主干持續(xù)集成,并且有全面的自動(dòng)化測(cè)試套件來驗(yàn)收本次修改的預(yù)期行為,且不會(huì)引發(fā)回歸缺陷”,從而達(dá)到這一目標(biāo)。
it知識(shí)庫(kù):特性分支是邪惡的?!,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。