原文網(wǎng)址:https:///articles/continuousIntegration.html Martin Fowler 目錄 2.5 每次提交都應(yīng)該在集成機(jī)上構(gòu)建主線2.8 在生產(chǎn)環(huán)境的克隆中測試2.9 任何人都能輕松獲得最新的可執(zhí)行文件2.10 每個(gè)人都可以看到正在發(fā)生什么延伸閱讀 持續(xù)集成是一種軟件開發(fā)實(shí)踐,團(tuán)隊(duì)成員頻繁地將他們的工作成果集成在一起,,通常每人每天至少提交一次,,這樣每天就會有多次集成。每次集成都通過自動構(gòu)建(包括測試)進(jìn)行驗(yàn)證,,以便盡可能快地檢測集成錯(cuò)誤,。許多團(tuán)隊(duì)發(fā)現(xiàn)這種方法可以顯著減少集成問題,并允許團(tuán)隊(duì)更快地開發(fā)內(nèi)聚軟件,。本文簡要介紹了持續(xù)集成技術(shù)及其應(yīng)用現(xiàn)狀,。我清楚地記得我第一次看到一個(gè)大型軟件項(xiàng)目。那時(shí)我在英國一家大型電子公司做暑期實(shí)習(xí),。我的經(jīng)理,,QA小組的一員,帶我參觀了一個(gè)地方,,我們進(jìn)入了一個(gè)令人沮喪的大倉庫,,里面堆滿了立方體。我被告知這個(gè)項(xiàng)目已經(jīng)開發(fā)了幾年,,目前正在集成,,并且已經(jīng)集成了幾個(gè)月。他告訴我,,沒有人真正知道完成集成需要多長時(shí)間,。從中我學(xué)到了軟件項(xiàng)目的一個(gè)共同的故事:集成是一個(gè)漫長而不可預(yù)測的過程。其實(shí)不需要這樣,。我在ThoughtWorks的同事和世界上許多其他人所做的大多數(shù)項(xiàng)目都不把集成當(dāng)成一個(gè)很嚴(yán)重的事兒,。任何單個(gè)開發(fā)人員的工作都離共享的項(xiàng)目狀態(tài)只有幾個(gè)小時(shí),并且可以在幾分鐘內(nèi)集成回去,。任何集成錯(cuò)誤都能被快速發(fā)現(xiàn)并得到迅速修正,。這種鮮明的對比并不是昂貴復(fù)雜工具的結(jié)果。它的本質(zhì)在于一個(gè)簡單的實(shí)踐,,也就是團(tuán)隊(duì)里的每個(gè)人都在頻繁的集成,,通常是每天對于一個(gè)受控的源代碼存儲庫進(jìn)行集成。當(dāng)我向人們描述這一做法時(shí),我通常會發(fā)現(xiàn)兩種反應(yīng):“這里不行”和“這樣做不會有多大區(qū)別”,。人們在嘗試的過程中發(fā)現(xiàn),,這比聽起來容易得多,而且對開發(fā)有著巨大的影響,。因此,,第三種常見的反應(yīng)是“是的,我們這樣做了——沒有它你怎么活,?”術(shù)語“持續(xù)集成”起源于Kent Beck的極限編程開發(fā)過程,,是最初的12個(gè)實(shí)踐之一。當(dāng)我開始在ThoughtWorks工作時(shí),,作為一名顧問,,我鼓勵(lì)在我工作的項(xiàng)目中使用這種技術(shù)。Matthew Foemmel把我模糊的建議變成了實(shí)際行動,,我們看到了這個(gè)項(xiàng)目從罕見而復(fù)雜的集成,,變?yōu)槲颐枋龅牟皇悄敲磭?yán)重的事情。Matthew和我在這篇論文的原始版本中寫下了我們的經(jīng)驗(yàn),,這篇論文一直是我網(wǎng)站上最受歡迎的論文之一,。盡管持續(xù)集成是一種不需要特殊工具來部署的實(shí)踐,但我們發(fā)現(xiàn)使用持續(xù)集成服務(wù)器是很有用的,。最著名的此類服務(wù)器是CruiseControl,,這是一個(gè)開源工具,最初由ThoughtWorks的幾個(gè)人構(gòu)建,,現(xiàn)在由一個(gè)很大的社區(qū)維護(hù),。從那時(shí)起,出現(xiàn)了其他一些CI服務(wù)器,,有開源的,,也有商用的——包括ThoughtWorks工作室的Cruise。對于我來說,,解釋什么是CI以及它是如何工作的最簡單的方法是展示一個(gè)快速的例子,說明它如何與一個(gè)小特性的開發(fā)一起工作,。假設(shè)我必須對一個(gè)軟件添加一點(diǎn)功能,,任務(wù)是什么并不重要,因?yàn)楝F(xiàn)在我假設(shè)它很小,,可以在幾個(gè)小時(shí)內(nèi)完成,。(稍后我們將探討更長的任務(wù)和其他問題。)首先,,我將當(dāng)前集成源代碼的副本復(fù)制到本地開發(fā)機(jī)器上,。我通過使用源代碼管理系統(tǒng),,從主干簽出一個(gè)工作副本來實(shí)現(xiàn)這一點(diǎn)。上面那段話對于使用源代碼控制系統(tǒng)的人來說是有意義的,,但是對于不使用源代碼控制系統(tǒng)的人來說是胡言亂語,。所以讓我快速地為后者解釋一下。源代碼控制系統(tǒng)將項(xiàng)目的所有源代碼保存在存儲庫中,。系統(tǒng)的當(dāng)前狀態(tài)通常稱為“主干”,。開發(fā)人員可以隨時(shí)在自己的機(jī)器上生成主干的受控副本,這稱為“簽出”,。開發(fā)人員機(jī)器上的副本稱為“工作副本”,。(大多數(shù)情況下,,你實(shí)際上是把你的工作副本更新到主干上——實(shí)際上和簽出也是一樣的,。)現(xiàn)在我拿著我的工作副本,做任何我需要做的事情來完成我的任務(wù),。這將包括修改產(chǎn)品代碼,,以及添加或更改自動化測試。持續(xù)集成假定軟件中有高度自動化的測試:我稱之為自測試代碼的工具,。它們通常使用流行的XUnit測試框架的一個(gè)版本,。當(dāng)我完成之后(通常在我工作的不同階段),我就在我的開發(fā)機(jī)器上執(zhí)行一個(gè)自動化的構(gòu)建,。這將獲取工作副本中的源代碼,,將其編譯并鏈接到可執(zhí)行文件中,然后運(yùn)行自動測試,。只有在所有的構(gòu)建和測試都沒有錯(cuò)誤的情況下,,整個(gè)構(gòu)建才被認(rèn)為是正確的。有了正確的構(gòu)建,,我就可以考慮將更改提交到存儲庫中,。當(dāng)然,問題是,,在我有機(jī)會提交我的更改之前,,其他人可能,而且通常已經(jīng)對主干進(jìn)行了更改,。因此,,首先我用他們的更改來更新我的工作副本,并重新構(gòu)建,。如果他們的更改與我的更改沖突,,在編譯或測試中將顯示為失敗。在這種情況下,,我的責(zé)任是修復(fù)這個(gè)問題,,并重復(fù)構(gòu)建,,直到我可以建立一個(gè)與主干正確同步的工作副本。一旦我自己構(gòu)建了一個(gè)正確同步的工作副本,,最終我就可以將我的更改提交到主干中,,之后會更新存儲庫。然而,,我的提交并沒有完成我的工作,。此時(shí),我們再次構(gòu)建,,但這次是在基于主干代碼的集成服務(wù)器上,。只有當(dāng)這個(gè)構(gòu)建成功時(shí),我們才能說我的更改已經(jīng)完成,。因?yàn)榭傆腥f一,,我可能會遺漏了我的機(jī)器上的東西,存儲庫沒有得到適當(dāng)?shù)母?。只有?dāng)我提交的更改在集成服務(wù)器上成功構(gòu)建時(shí),,我的工作才能完成。這個(gè)集成構(gòu)建可以由我手動執(zhí)行,,也可以由Cruise自動完成,。如果兩個(gè)開發(fā)人員之間發(fā)生沖突,通常會在第二個(gè)提交的開發(fā)人員構(gòu)建其更新的工作副本時(shí)捕獲沖突,。否則,,集成構(gòu)建將失敗。無論哪種方式,,錯(cuò)誤都會被快速檢測到,。此時(shí),最重要的任務(wù)是修復(fù)它,,并使構(gòu)建重新正常工作,。在持續(xù)集成環(huán)境中,不應(yīng)該讓失敗的集成構(gòu)建保持在失敗狀態(tài)太久,。一個(gè)好的團(tuán)隊(duì)一天應(yīng)該有很多正確的構(gòu)建,。不好的構(gòu)建時(shí)有發(fā)生,但應(yīng)該迅速被修復(fù),。這樣做的結(jié)果是,,有一個(gè)穩(wěn)定的軟件,工作正常,,包含很少的錯(cuò)誤,。每個(gè)人都是從這個(gè)共享的穩(wěn)定的基礎(chǔ)上開發(fā)的,從來沒有離開這個(gè)基礎(chǔ)太遠(yuǎn),,以至于需要很長時(shí)間才能集成回來,。尋找錯(cuò)誤會花更少的時(shí)間,,因?yàn)殄e(cuò)誤很快就會顯現(xiàn)出來。上面的故事是關(guān)于CI的概述,,以及它在日常生活中是如何工作的。顯然,,讓所有這些工作順利進(jìn)行并不僅僅是這些,。我現(xiàn)在將重點(diǎn)介紹構(gòu)成有效CI的關(guān)鍵實(shí)踐。軟件項(xiàng)目涉及許多需要組合在一起才能構(gòu)建產(chǎn)品的文件,。跟蹤所有這些文件,,是一項(xiàng)重要的工作,特別是當(dāng)有多人參與時(shí),。因此,,我們毫不意外的看到,多年來,,軟件開發(fā)團(tuán)隊(duì)已經(jīng)構(gòu)建了管理所有這些文件的工具,。這些工具稱為源代碼管理工具,、配置管理,、版本控制系統(tǒng)、存儲庫或各種其他名稱,,是大多數(shù)開發(fā)項(xiàng)目不可分割的一部分,。令人悲哀和驚訝的是,它們并不是所有項(xiàng)目的一部分,。盡管很少見,,但我確實(shí)遇到不使用這樣的系統(tǒng)的項(xiàng)目,項(xiàng)目使用一些混亂的本地和共享存儲器的組合,。因此,,作為一個(gè)簡單的基礎(chǔ),確保你要有一個(gè)像樣的源代碼管理系統(tǒng),。成本不是問題,,因?yàn)橛懈哔|(zhì)量的開源工具。當(dāng)前選擇的開源存儲庫是Subversion,。(較老的開源工具CVS仍然被廣泛使用,,雖然比什么都沒有要好得多,但是Subversion是更時(shí)髦的選擇,。)有趣的是,,當(dāng)我與開發(fā)人員交談時(shí),我了解到大多數(shù)商業(yè)源代碼管理工具比Subversion更受歡迎,。我一直聽到人們說唯一值得花錢的工具就是Perforce,。(譯者注:本文寫于2006年,,時(shí)至今日,Git更為流行)一旦你得到一個(gè)源代碼管理系統(tǒng),,確保它位于眾所周知的地方,,每個(gè)人都去獲取源代碼。沒有人會問“foo-whiffle文件在哪里,?”所有的東西都應(yīng)該在存儲庫里,。盡管許多團(tuán)隊(duì)都會使用存儲庫,但我發(fā)現(xiàn)一個(gè)常見的錯(cuò)誤是,,他們沒有將所有內(nèi)容都放在存儲庫中,。如果人們使用它,他們會把代碼放在那里,,但你的構(gòu)建需要做的一切都應(yīng)該在那里,,包括:測試腳本,屬性文件,,數(shù)據(jù)庫架構(gòu),,安裝腳本和第三方庫。我知道一些項(xiàng)目,,將編譯器檢入到存儲庫(對于早期的大量的C++編譯器很重要),。基本的經(jīng)驗(yàn)法則是,,你應(yīng)該能夠用一臺空白的機(jī)器開始項(xiàng)目,,做一個(gè)簽出,并且能夠完整的構(gòu)建系統(tǒng),。只有少量的東西應(yīng)該放在空白的機(jī)器上,,通常是大的、安裝復(fù)雜的和穩(wěn)定的東西,。操作系統(tǒng),、Java開發(fā)環(huán)境或基礎(chǔ)數(shù)據(jù)庫系統(tǒng)是典型的例子。你必須將構(gòu)建所需的所有內(nèi)容都放在源代碼管理系統(tǒng)中,,但是你也可以將人們通常使用的其他內(nèi)容放在其中,。IDE配置很適合放在那里,因?yàn)檫@樣人們就可以很容易地共享相同的IDE設(shè)置,。版本控制系統(tǒng)的一個(gè)特點(diǎn)是,,它們允許你能創(chuàng)建多個(gè)分支,以處理不同的開發(fā)流,。這是一個(gè)有用的,,但不必要的功能,但它經(jīng)常被過度使用,,并使人們陷入麻煩,。盡量少用分支,。特別是在有一條主干的情況:目前正在開發(fā)的項(xiàng)目的唯一分支。幾乎每個(gè)人大部分時(shí)間都應(yīng)該在這條主干上工作,。(合理的分支是修復(fù)先前生產(chǎn)版本的錯(cuò)誤和臨時(shí)的實(shí)驗(yàn),。)一般來說,你應(yīng)該在源代碼管理中存儲構(gòu)建所需的所有內(nèi)容,,但不存儲實(shí)際構(gòu)建出的內(nèi)容,。有些人確實(shí)將構(gòu)建的產(chǎn)品放在源代碼管理中,但我認(rèn)為這是一種壞味道——這意味著更深層次的問題,,通常是無法可靠地重新創(chuàng)建構(gòu)建,。將源代碼轉(zhuǎn)換為可以運(yùn)行的系統(tǒng),通常是一個(gè)復(fù)雜的過程,,它包括編譯,、移動文件、把數(shù)據(jù)庫模式加載到數(shù)據(jù)庫等等,。然而,,與軟件開發(fā)中的大多數(shù)任務(wù)一樣,它是可以被自動化的,。它也應(yīng)該是自動化的,。讓人們輸入奇怪的命令或點(diǎn)擊對話框是浪費(fèi)時(shí)間,也最容易產(chǎn)生錯(cuò)誤,。構(gòu)建自動化環(huán)境是系統(tǒng)中一個(gè)共同的特性,。Unix世界使用make作為工具已經(jīng)幾十年了,,Java社區(qū)發(fā)展了ANT(譯者注:后來JAVA的構(gòu)建工具發(fā)展為Maven和Gradle),,并且. net社區(qū)已經(jīng)有了Nant,現(xiàn)在又有了MSBuild,。確保你可以使用單個(gè)命令使用這些腳本構(gòu)建和運(yùn)行系統(tǒng),。一個(gè)常見的錯(cuò)誤是沒有在自動化構(gòu)建中包含所有內(nèi)容。構(gòu)建應(yīng)該包括從存儲庫中獲取數(shù)據(jù)庫模式,,并在執(zhí)行環(huán)境中啟動它,。我將詳細(xì)闡述我先前的經(jīng)驗(yàn)法則:任何人都應(yīng)該能夠引入一臺空白機(jī)器,簽出存儲庫中的源代碼,,發(fā)出一個(gè)命令,,之后在自己的機(jī)器上就擁有了一個(gè)正在運(yùn)行的系統(tǒng)。構(gòu)建腳本通常有不同的風(fēng)格,,通常是特定于平臺或社區(qū)的,,但他們大可不必如此。盡管我們的大多數(shù)Java項(xiàng)目都使用Ant,,有一些在使用Ruby(Ruby Rake是一個(gè)非常好用的構(gòu)建腳本的工具),。我們通過使用Ant自動化在早期的微軟 COM項(xiàng)目中獲得了某些價(jià)值,。一個(gè)大的構(gòu)建通常需要花費(fèi)很多精力,如果你僅僅做了一個(gè)小小的更改,,那么你不會想要執(zhí)行所有的步驟,。所以,一個(gè)好的構(gòu)建工具會分析在流程中需要更改的內(nèi)容,。通常的做法是檢查源文件和目標(biāo)文件的日期,,只有在源文件的日期較晚時(shí)才進(jìn)行編譯。依賴關(guān)系會變得更加棘手:如果一個(gè)對象文件改變了那些依賴于它的對象文件,,那么這些對象文件可能也需要重新構(gòu)建,。編譯器能夠處理這類事情,也可能不處理,。根據(jù)你的需要,,你可以構(gòu)建不同類型的東西。你可以通過是否使用測試代碼或者使用不同的測試集來構(gòu)建系統(tǒng),。有些組件可以獨(dú)立構(gòu)建,。構(gòu)建腳本應(yīng)該允許你為不同的情況構(gòu)建可選目標(biāo)。我們普遍使用IDE,,而且在使用IDE時(shí),,大多數(shù)的公司內(nèi)部都有一些構(gòu)建管理的過程。然而,,這些文件總是IDE專有的,,而且它們非常脆弱。不過,,他們這些公司需要通過IDE進(jìn)行工作,。用戶通過IDE設(shè)置自己的項(xiàng)目文件并將其用于單獨(dú)的開發(fā)是完全沒有問題的。然而,,有一個(gè)在服務(wù)器上可用并且可以從其他腳本運(yùn)行的主干是非常重要的,。所以在Java項(xiàng)目中,我們可以讓研發(fā)人員在IDE中構(gòu)建,,但是主干需要使用Ant來保證它可以在開發(fā)服務(wù)器上運(yùn)行,。從傳統(tǒng)意義上來講,構(gòu)建意味著編譯,,鏈接以及執(zhí)行程序所需的所有其他過程,。一個(gè)項(xiàng)目可能會運(yùn)行,但是,,這并不意味著它在做正確的事情?,F(xiàn)代靜態(tài)類型語言可以發(fā)現(xiàn)許多bug,但是更多的bug會成為“漏網(wǎng)之魚”。在構(gòu)建過程中包含自動化測試是更快,、更有效地發(fā)現(xiàn)bug的比較好的方法,。當(dāng)然,測試并不是完美的,,但它能夠發(fā)現(xiàn)很多Bug,,這就足夠有用了。特別是極限編程(XP)和測試驅(qū)動開發(fā)(TDD)的興起為自動化測試的普及做了大量工作,,因此許多人已經(jīng)看到了這種技術(shù)的價(jià)值,。經(jīng)常閱讀我作品的讀者會發(fā)現(xiàn)我是XP和TDD的忠實(shí)粉絲。然而,,我想要強(qiáng)調(diào)的是,,這兩種方式都不是構(gòu)建自動化測試的最佳途徑。這兩種方式都強(qiáng)調(diào)在使測試通過之前你要先編寫測試——在這種模式下,,測試不僅能夠用于發(fā)現(xiàn)錯(cuò)誤,,而且還涉及探索系統(tǒng)的設(shè)計(jì)。這是一件好事情,,但是,,對持續(xù)集成而言,這并不是必需的,。因?yàn)槲覀兺ǔψ詣踊瘻y試代碼的需求比較少,。(盡管TDD是我進(jìn)行自動化測試的首選)對于自測試代碼而言,你需要一套自動化測試體系它可以檢查大部分代碼庫中的Bug,。測試可以從一個(gè)簡單的指令中啟動并進(jìn)行自動檢測,。運(yùn)行測試套件的結(jié)果應(yīng)該可以指出是否有任何測試失敗。對于具備自測試的構(gòu)建,,測試的失敗應(yīng)該會導(dǎo)致構(gòu)建失敗,。在過去的幾年時(shí)間里,TDD的興起普及了XUnit開源工具家族,,這些工具對于這類測試非常理想,。Xunit 工具對我們 ThoughtWorks 來說非常有價(jià)值,我也總是建議人們使用這些工具,。這些由Kent Beck首創(chuàng)的工具,能夠幫你非常容易的構(gòu)建一個(gè)自動化測試環(huán)境,。XUnit工具當(dāng)然是讓代碼進(jìn)行自動化測試的起點(diǎn),。你也應(yīng)該尋找其他專注于更多端到端測試的工具。目前有很多這樣的工具,,包括FIT,、Selenium、Sahi,、Watir,、FITnesse,,還有很多其他工具,我在這里不打算都列出來,。當(dāng)然,,你不能指望測試能發(fā)現(xiàn)一切錯(cuò)誤。正如人們常說的那樣:測試并不能證明沒有缺陷,。雖然你從自動化測試的構(gòu)建中得到的反饋并不一定是完美的,,經(jīng)常運(yùn)行的不完美的測試也比根本不寫的完美測試要好得多。集成主要是關(guān)于溝通的,。集成允許開發(fā)人員將他們所做的更改告知其他開發(fā)人員,。頻繁的交流能讓人們在變化發(fā)生時(shí)迅速了解情況。開發(fā)人員遵守主干的一個(gè)先決條件是,,他們可以正確地構(gòu)建自己的代碼,。當(dāng)然,這包括通過構(gòu)建測試,。與任何提交周期一樣,,開發(fā)人員首先更新其工作分支以匹配主干,解決與主干的任何沖突,,然后在其本地上構(gòu)建,。如果構(gòu)建通過,那么他們可以自由地提交到主干上,。通過經(jīng)常這樣做,,開發(fā)人員可以快速發(fā)現(xiàn)兩個(gè)開發(fā)人員之間是否存在沖突??焖傩迯?fù)問題的關(guān)鍵是快速找到它們,。由于開發(fā)人員每隔幾小時(shí)就提交一次沖突,所以在沖突生后的幾小時(shí)內(nèi)就可以檢測到,,此刻沒有發(fā)生太多代碼修改,,所以容易解決。持續(xù)數(shù)周不被發(fā)現(xiàn)的沖突,,可能很難解決,。在更新工作分支時(shí)進(jìn)行構(gòu)建,這一事實(shí)意味著可以同時(shí)檢測到編譯沖突和文本沖突,。由于構(gòu)建是自測試的,,所以你還可以檢測代碼運(yùn)行中的沖突,如果后一種Bug在代碼中存在了很長時(shí)間而沒有被發(fā)現(xiàn),,那么它們是特別難以發(fā)現(xiàn)的錯(cuò)誤,。由于兩次提交之間只有幾個(gè)小時(shí)的更改,所以問題隱藏的地方也就只有那么多了。此外,,由于沒有太大的變化,,你可以使用差異調(diào)試來幫助你找到錯(cuò)誤。我的一般經(jīng)驗(yàn)法則是,,每個(gè)開發(fā)人員每天都應(yīng)該提交到代碼庫,。在實(shí)踐中,如果開發(fā)人員更頻繁地提交,,通常是有用的,。提交的頻率越高,尋找沖突錯(cuò)誤的地方就越少,,解決沖突的速度也就越快,。頻繁的提交會鼓勵(lì)開發(fā)人員將他們的工作分解成幾個(gè)小時(shí)的小塊。這有助于跟蹤進(jìn)度,,并提供一種進(jìn)度感,。通常人們一開始覺得他們不能在幾個(gè)小時(shí)內(nèi)做一些有意義的事情,但是我們發(fā)現(xiàn)指導(dǎo)和練習(xí)可以幫助他們學(xué)習(xí),。2.5 每次提交都應(yīng)該在集成機(jī)上構(gòu)建主線通過使用每日構(gòu)建,,團(tuán)隊(duì)可以得到頻繁的測試構(gòu)建。這應(yīng)該意味著主干保持在健康的狀態(tài),。然而,,在實(shí)踐中,依然有出錯(cuò)的情況,。一個(gè)原因是紀(jì)律,,人們沒有在提交之前進(jìn)行更新和構(gòu)建。另一個(gè)原因是開發(fā)人員使用的機(jī)器之間的環(huán)境差異,。因此,,你應(yīng)該確保常規(guī)構(gòu)建發(fā)生在集成機(jī)器上,并且只有在此集成構(gòu)建成功時(shí),,才應(yīng)該認(rèn)為提交已經(jīng)完成,。由于開發(fā)人員對提交的代碼負(fù)責(zé),所以該開發(fā)人員需要監(jiān)視主干構(gòu)建,,以便在它崩潰時(shí)進(jìn)行修復(fù),。這樣做的一個(gè)推論是,你如果在當(dāng)天晚些時(shí)候提交了一些改動,,那么在主干構(gòu)建通過之前你不能回家,。我見過兩種主要的方法來確保這一點(diǎn):使用手動構(gòu)建或持續(xù)集成服務(wù)器。手工構(gòu)建方法是描述起來最簡單的方法,。本質(zhì)上,它類似于開發(fā)人員在提交到存儲庫前進(jìn)行的本地構(gòu)建。開發(fā)人員走到集成計(jì)算機(jī),,簽出主線的頭部(現(xiàn)在存放他的最后提交),,并開始集成構(gòu)建。他會密切關(guān)注構(gòu)建的進(jìn)展,,如果構(gòu)建成功,,他就完成了提交。(參見Jim Shore的描述,。)持續(xù)集成服務(wù)器充當(dāng)存儲庫的監(jiān)視器,。每次完成對代碼庫的提交時(shí),服務(wù)器都會自動將源簽出到集成機(jī)器上,,啟動構(gòu)建并將構(gòu)建的結(jié)果通知提交者,。直到提交者收到通知——通常是一封電子郵件——它才算完成。在ThoughtWorks,,我們是持續(xù)集成服務(wù)器的忠實(shí)粉絲——事實(shí)上,,我們領(lǐng)導(dǎo)了CruiseControl和CruiseControl.NET的起初的開發(fā),他們是被廣泛使用的開源CI服務(wù)器,。從那時(shí)起,,我們還建立了商業(yè)版的Cruise CI服務(wù)器。我們幾乎在每個(gè)項(xiàng)目上都使用CI服務(wù)器,,并且對結(jié)果非常滿意,。并不是所有人都喜歡使用CI服務(wù)器。吉姆·肖爾(Jim Shore)對他為什么更喜歡手工方法給出了一個(gè)頗有爭議的描述,。我同意他的觀點(diǎn),,CI不僅僅是安裝一些軟件。這里的所有實(shí)踐都需要有效地進(jìn)行持續(xù)集成,。但是,,同樣有許多優(yōu)秀的CI團(tuán)隊(duì)發(fā)現(xiàn)CI服務(wù)器是一個(gè)有用的工具。許多組織按照定時(shí)計(jì)劃進(jìn)行定期構(gòu)建,,比如每天晚上,。這與持續(xù)構(gòu)建不同,對于持續(xù)集成來說還不夠,。持續(xù)集成的全部意義在于盡快發(fā)現(xiàn)問題,。夜間構(gòu)建意味著bug在被發(fā)現(xiàn)之前一整天都沒有被發(fā)現(xiàn)。一旦它們在系統(tǒng)中存在了那么長時(shí)間,,就需要花費(fèi)很長時(shí)間才能找到并修復(fù)它們,。做持續(xù)構(gòu)建非常關(guān)鍵的一點(diǎn)就是一旦主線構(gòu)建失敗就要立即修改它。使用CI的意義就在于,,你總是能在已知穩(wěn)定基礎(chǔ)上進(jìn)行開發(fā),。主線構(gòu)建失敗是常有的事,,且也并不是什么壞事,但這卻能夠表明人們在提交代碼之前對本地的更新和構(gòu)建不夠小心,。然而,,當(dāng)主線構(gòu)建確實(shí)中斷時(shí),快速修復(fù)它就變得極為重要,。我記得Kent Beck說過一句話:“沒有人擁有比修復(fù)失敗的構(gòu)建更高的優(yōu)先級任務(wù)”,。但也不必團(tuán)隊(duì)中的每個(gè)人都必須停止他們手頭的工作來修改失敗的構(gòu)建,通常只需要幾個(gè)人就可以成功修復(fù),。我們要有意識的將修復(fù)構(gòu)建作為一個(gè)緊急的,、高優(yōu)先級的任務(wù)進(jìn)行優(yōu)先排序。通常,,修復(fù)構(gòu)建的最快方法是從主線還原到最新一次已知的,、良好的構(gòu)建提交狀態(tài)。當(dāng)然,,團(tuán)隊(duì)不應(yīng)該在構(gòu)建失敗的主線上進(jìn)行任何調(diào)試,。除非失敗的原因很明顯,否則只需恢復(fù)主線然后在開發(fā)工作站上調(diào)試問題,。為了避免破壞主線,,你可以考慮使用pending head。當(dāng)團(tuán)隊(duì)引入CI時(shí),,這通常是最難解決的問題之一,。在早期,團(tuán)隊(duì)很難養(yǎng)成處理主線構(gòu)建的常規(guī)習(xí)慣,,特別是在處理現(xiàn)有代碼庫時(shí),。耐心和堅(jiān)持不懈的努力看起來確實(shí)經(jīng)常起作用,,,所以不要?dú)怵H,。持續(xù)集成的關(guān)鍵是提供快速的反饋。沒有什么比一個(gè)需要很長時(shí)間的構(gòu)建更能危害CI的活動了,。在這里我必須承認(rèn)有一些古怪的老員工把長時(shí)間的構(gòu)建當(dāng)成娛樂,。我的大多數(shù)同事認(rèn)為一個(gè)需要一個(gè)小時(shí)的構(gòu)建是完全不合理的。我記得一些團(tuán)隊(duì)夢想著他們能以如此之快的速度完成任務(wù)——但是有時(shí)候我們?nèi)匀粫龅竭@樣的情況:很難以這樣的速度完成任務(wù),。然而,,對于大多數(shù)項(xiàng)目來說,十分鐘構(gòu)建的XP指導(dǎo)方針是完全合理的?,F(xiàn)在我們的大多數(shù)項(xiàng)目都實(shí)現(xiàn)了這一點(diǎn),。這值得我們集中精力來實(shí)現(xiàn)它,因?yàn)槊繙p少一分鐘的構(gòu)建時(shí)間,,那么對于每個(gè)開發(fā)人員的每次提交都會節(jié)省一分鐘的時(shí)間,。由于CI需要頻繁提交,,這就會增加很多的時(shí)間。如果你一開始就需要一個(gè)小時(shí)的構(gòu)建時(shí)間,,那么獲得更快的構(gòu)建可能會讓人畏懼,。甚至在一個(gè)新的項(xiàng)目上工作,,并思考如何讓事情保持快速也會讓人畏懼,。至少對于企業(yè)應(yīng)用程序,我們發(fā)現(xiàn)通常的瓶頸是測試——特別是涉及外部服務(wù)(如數(shù)據(jù)庫)的測試,。最關(guān)鍵的一步是開始建立部署流水線,。部署流水線(也稱為構(gòu)建流水線或分階段構(gòu)建)背后的思想是,實(shí)際上有多個(gè)按順序完成的構(gòu)建,。對主線的提交觸發(fā)了第一個(gè)構(gòu)建——我稱之為提交構(gòu)建,。提交構(gòu)建是當(dāng)有人需要提交到主線時(shí)所需的構(gòu)建。提交構(gòu)建是必須快速完成的構(gòu)建,,因此它將采用許多快捷方式,,這將降低檢測錯(cuò)誤的能力。關(guān)鍵在于平衡發(fā)現(xiàn)bug的能力和速度的需求,,這樣一個(gè)好的提交構(gòu)建就足夠穩(wěn)定,,可以供其他人工作使用一旦有良好的提交構(gòu)建,其他人就可以滿懷信心地處理代碼,。不過,,你可以開始做更多、更漫長的測試,。其他機(jī)器可以在構(gòu)建上運(yùn)行需要更長時(shí)間的測試程序,。這是一個(gè)簡單的兩階段部署流水線例子。第一階段將進(jìn)行編譯并運(yùn)行更本地化的單元測試,,數(shù)據(jù)庫通過“打樁”的方式完全被隔離掉,。這樣測試可以運(yùn)行得非常快,,保持在10分鐘的范圍內(nèi),。但是,任何涉及大規(guī)模交互的bug,,特別是涉及真實(shí)數(shù)據(jù)庫的bug,,都很難被發(fā)現(xiàn)。第二階段構(gòu)建運(yùn)行一組不同的測試,,這些測試確實(shí)會測試真實(shí)的數(shù)據(jù)庫,,并涉及更多的端到端行為。這一次運(yùn)行可能需要幾個(gè)小時(shí)的時(shí)間,。在這個(gè)場景中,,人們使用第一個(gè)階段作為提交構(gòu)建,,并使用它作為主CI周期。第二階段構(gòu)建在可能的情況下運(yùn)行,,從最近的良好提交構(gòu)建中獲取可執(zhí)行文件以進(jìn)行進(jìn)一步的測試,。如果這個(gè)二次構(gòu)建失敗,那么它可能沒有 “停止一切”的質(zhì)量目標(biāo),,但是團(tuán)隊(duì)的目標(biāo)是在保持提交構(gòu)建運(yùn)行的同時(shí),,盡快修復(fù)此類錯(cuò)誤。在這個(gè)例子中,,后面的構(gòu)建通常是純測試,,因?yàn)楝F(xiàn)在通常是測試導(dǎo)致了緩慢。如果第二級構(gòu)建檢測到一個(gè)bug,,這表明提交構(gòu)建可能使用另一套測試,。盡可能地確保任何后期階段的失敗,都會觸發(fā)在提交構(gòu)建中引入新的測試,,這樣在提交構(gòu)建中就能捕獲bug,,從而在提交構(gòu)建中就能解決掉這些bug。這樣,,每當(dāng)有錯(cuò)誤漏過提交時(shí),,提交測試就會加強(qiáng)。在某些情況下,,沒有一種快速運(yùn)行的測試來暴露bug,,因此你可能決定只在第二級構(gòu)建中測試該條件。幸運(yùn)的是,,大多數(shù)情況下,,你可以向提交構(gòu)建添加適當(dāng)?shù)臏y試。這個(gè)例子是一個(gè)兩級流水線,,但是基本原理可以擴(kuò)展到任何后期階段,。提交構(gòu)建之后的構(gòu)建也可以并行完成,因此如果有兩個(gè)小時(shí)的第二階段測試,,則可以通過讓兩臺機(jī)器分別運(yùn)行一半的測試來提高響應(yīng)速度,。通過使用類似這樣的并行第二階段構(gòu)建,你可以將進(jìn)一步的自動化測試(包括性能測試)引入到常規(guī)構(gòu)建過程中,。2.8 在生產(chǎn)環(huán)境的克隆中測試測試的重點(diǎn)是在受控條件下,,清除系統(tǒng)在生產(chǎn)中可能出現(xiàn)的任何問題。其中很重要的一部分是生產(chǎn)系統(tǒng)運(yùn)行的環(huán)境,。如果你在不同的環(huán)境中進(jìn)行測試,,每一個(gè)差異都會導(dǎo)致風(fēng)險(xiǎn),即在測試中發(fā)生的事情不會在生產(chǎn)中發(fā)生,。因此,,你希望將測試環(huán)境設(shè)置為盡可能精確地模擬生產(chǎn)環(huán)境,。使用相同版本的數(shù)據(jù)庫軟件,使用相同版本的操作系統(tǒng),。將生產(chǎn)環(huán)境中的所有適用的庫放入測試環(huán)境中,,即使系統(tǒng)實(shí)際上沒有使用它們。使用相同的IP地址和端口,,在相同的硬件上運(yùn)行它,。事實(shí)上,這是有限度的,。如果你正在編寫桌面軟件,,使用不同人員運(yùn)行的、并安裝了所有的第三方軟件的桌面克隆中,,進(jìn)行測試那是不現(xiàn)實(shí)的。類似地,,有些生產(chǎn)環(huán)境的復(fù)制成本可能高得令人望而卻步(盡管我經(jīng)常遇到不復(fù)制中等成本的環(huán)境而產(chǎn)生的浪費(fèi)成本),。盡管存在這些限制,你的目標(biāo)仍然應(yīng)該是盡可能多地復(fù)制生產(chǎn)環(huán)境,,并理解測試和生產(chǎn)之間的每一個(gè)差異所帶來的風(fēng)險(xiǎn),。如果你有一個(gè)非常簡單的設(shè)置,而沒有許多笨拙的通信,,那么你可以在模擬的環(huán)境中運(yùn)行提交構(gòu)建,。但是,通常需要使用測試替身(test double),,因?yàn)橄到y(tǒng)響應(yīng)緩慢或不夠穩(wěn)定,。因此,通常都有一個(gè)非常人工的環(huán)境來進(jìn)行提交測試,,以提高速度,,并使用生產(chǎn)克隆進(jìn)行輔助測試。我注意到越來越多的人對使用虛擬化來簡化測試環(huán)境的組合越來越感興趣,。虛擬化的機(jī)器可以保存在虛擬化中的所有必要元素中,。安裝最新的構(gòu)建和運(yùn)行測試相對簡單。此外,,這還允許你在一臺計(jì)算機(jī)上運(yùn)行多個(gè)測試,,或者在一臺計(jì)算機(jī)上模擬網(wǎng)絡(luò)中的多臺計(jì)算機(jī)。隨著虛擬化的性能損失降低,,這個(gè)選項(xiàng)變得越來越有意義,。2.9 任何人都能輕松獲得最新的可執(zhí)行文件軟件開發(fā)中最困難的部分之一是確保你構(gòu)建了正確的軟件。我們發(fā)現(xiàn),,很難事先明確自己想要什么,,也很難做到正確,;人們更容易看到不太正確的東西,并說出需要如何改變,。敏捷開發(fā)過程明確地期望并利用人類行為的這一部分,。為了幫助實(shí)現(xiàn)這一點(diǎn),任何參與軟件項(xiàng)目的人都應(yīng)該能夠獲得最新的可執(zhí)行文件并能夠運(yùn)行它:用于演示,、探索性測試,,或者只是看看本周發(fā)生了什么變化。.這樣做非常簡單:確保有一個(gè)眾所周知的地方,,人們可以找到最新的可執(zhí)行文件,。在這樣的存儲中放置幾個(gè)可執(zhí)行文件可能是有用的。對于最新的可執(zhí)行文件,,你應(yīng)該放置最新的可執(zhí)行文件以通過提交測試—如果提交套件相當(dāng)強(qiáng)大,,那么這樣的可執(zhí)行文件應(yīng)該相當(dāng)穩(wěn)定。如果你正在使用定義良好的迭代來跟蹤一個(gè)流程,,那么通常明智的做法是將迭代構(gòu)建的結(jié)束也放在流程里,。特別是演示,需要的是讓人熟悉功能的軟件,,因此通常為演示者知道如何操作的,,而犧牲最新的一些事是值得的。2.10 每個(gè)人都可以看到正在發(fā)生什么持續(xù)集成就是為了溝通,,所以你希望保證每個(gè)人都能很方便的看到系統(tǒng)當(dāng)前的狀態(tài)以及在系統(tǒng)上所做的改變,。其中用于溝通的最重要的一個(gè)途徑是主線構(gòu)建的狀態(tài)。如果你使用Cruise,,那么有一個(gè)網(wǎng)站可以展示給你看是否有個(gè)構(gòu)建正在進(jìn)行,,以及最后一次主線構(gòu)建的狀態(tài)。很多團(tuán)隊(duì)喜歡將持續(xù)顯示和構(gòu)建系統(tǒng)連接起來,,從而使它更顯而易見,。通常用燈來表示:綠燈表示構(gòu)建成功,當(dāng)失敗的時(shí)候則亮紅燈,。常見的一個(gè)設(shè)計(jì)是紅色和綠色的熔巖燈—不僅僅給出這些構(gòu)建狀態(tài)的指示,,而且還指示已經(jīng)處于該種狀態(tài)多久了。紅色熔巖燈上的氣泡表明此次構(gòu)建已經(jīng)失敗很久了,。每個(gè)團(tuán)隊(duì)都可以自己選擇構(gòu)建傳感器—當(dāng)然你的選擇也可以是很好玩的(最近我看到有人在用一只跳舞的兔子),。即使你使用手動的持續(xù)集成,可視化依然很重要,。物理構(gòu)建機(jī)器的監(jiān)視器可以展現(xiàn)主線構(gòu)建的狀態(tài),。通常可以給正在構(gòu)建的人的桌子上放一個(gè)構(gòu)建令牌(再說一次,像橡膠雞這樣的蠢東西也是一個(gè)很好的選擇),。人們常常喜歡給構(gòu)建成功加上一點(diǎn)簡單的噪音,,比如說鈴聲。當(dāng)然,,持續(xù)集成服務(wù)器的網(wǎng)頁可以承載更多的信息,。Cruise不僅能提供誰正在構(gòu)建,而且還能提供他們做了什么改變,。Cruise還可以提供改變的歷史,,以使得團(tuán)隊(duì)成員可以對當(dāng)前項(xiàng)目中最近的行為有更好的了解。我知道團(tuán)隊(duì)的領(lǐng)導(dǎo)喜歡用這些信息來了解團(tuán)隊(duì)成員在做什么以及保持對系統(tǒng)改變的感知,。使用網(wǎng)站的另外一個(gè)優(yōu)勢是那些異地辦公的成員可以獲知項(xiàng)目狀態(tài),。一般來說,我傾向于積極緊密工作在同一個(gè)項(xiàng)目上的成員坐在一起,,但是經(jīng)常還會有一些其他人員關(guān)心項(xiàng)目的相關(guān)事宜,。同時(shí)對于組織將多個(gè)項(xiàng)目的構(gòu)建信息聚合在一起也是很有用的—可以為不同的項(xiàng)目提供一個(gè)簡單并且自動的狀態(tài)。好的信息展示不僅僅是電腦屏幕上那些,。我最喜歡的一個(gè)信息展示來自一個(gè)使用持續(xù)集成的項(xiàng)目上,。它在一個(gè)很長的時(shí)間里不能穩(wěn)定構(gòu)建。我們把一整年的日歷放到墻上,,用一個(gè)方框表示一年中的一天。如果QA團(tuán)隊(duì)得到一個(gè)通過了提交測試的穩(wěn)定的構(gòu)建,,他們會把一個(gè)綠色的貼紙放到這一天的方框里,,反之則放一個(gè)紅的貼紙。隨著時(shí)間過去,,日歷揭示了構(gòu)建過程持續(xù)改善的情況,,直到綠色的方框越來越多的時(shí)候,日歷就消失了—因?yàn)檫_(dá)到了它的目的,。為了做持續(xù)集成,,你需要多個(gè)環(huán)境,一個(gè)用來運(yùn)行提交測試,,一個(gè)或者多個(gè)用來運(yùn)行第二階段測試,。因?yàn)槟忝刻於家谶@些環(huán)境之間移動可執(zhí)行文件很多次,所以你希望能自動的做這些事兒,。很重要的是你要有一些腳本可以很容易的把應(yīng)用部署到任意環(huán)境中去,。這么做很自然的結(jié)果就是你還應(yīng)該有腳本,以使得你可以同樣簡單的部署到生產(chǎn)環(huán)境,。你可能不會每天都部署到生產(chǎn)環(huán)境(雖然我曾經(jīng)參與過這樣的項(xiàng)目),,但是自動化部署可以幫助你提高速度并且減少錯(cuò)誤。這同樣是一個(gè)便宜的選項(xiàng),因?yàn)檫@只是使用了你用于部署到測試環(huán)境的相同資源,。如果你在生產(chǎn)環(huán)境自動部署,,那么你需要考慮的一個(gè)額外的功能是自動回滾。糟糕的事情隨時(shí)會發(fā)生,,如果發(fā)臭的棕色物質(zhì)撞到旋轉(zhuǎn)的金屬上(情況不妙),,最好能夠很快的回到最后一個(gè)已知的好的狀態(tài)。能夠自動回滾,,可以大大減少部署產(chǎn)生的緊張感,,鼓勵(lì)人們更頻繁的部署,因此我們可以更快的把新特性提供給用戶,。(Ruby on Rails社區(qū)開發(fā)了一款名為Capistrano的工具,,是做此類事情的工具的一個(gè)好例子)在集群環(huán)境中我看到滾動部署,新的軟件會一次部署到一個(gè)節(jié)點(diǎn),,然后在幾個(gè)小時(shí)內(nèi)慢慢將整個(gè)應(yīng)用替換掉,。對于面向大眾的網(wǎng)站應(yīng)用,我接觸到的一個(gè)很有趣的部署方式是:將一個(gè)試用版本部署給部分用戶,。接著團(tuán)隊(duì)可以觀察該試用版本是如何被使用的,,然后決定是否將其部署給全量用戶。這使得你可以在做出最終選擇之前測試新特性和用戶接口,。自動化部署結(jié)合良好的持續(xù)集成原則,,是此工作的重要基礎(chǔ)。總起來說我認(rèn)為持續(xù)集成最顯著也是最寬泛的好處在于減少了風(fēng)險(xiǎn),。我又想起了在本文第一段中提到的軟件項(xiàng)目。團(tuán)隊(duì)已經(jīng)處于一個(gè)漫長項(xiàng)目的末期(起碼他們是這么認(rèn)為的),,但是還沒法判斷距離項(xiàng)目真正做完還有多久,。 推遲集成的麻煩在于很難預(yù)測到底需要多久來做這件事兒,并且更糟糕的是甚至很難看到你在這個(gè)過程中的進(jìn)展,。結(jié)果就是,,即使你是少數(shù)幾個(gè)還沒延遲的案例之一,你也會在項(xiàng)目最緊張的部分陷入完全的盲區(qū),。 持續(xù)集成完全可以解決這個(gè)問題,。沒有了長期的集成,你就可以完全的消除盲區(qū),。在所有的時(shí)間點(diǎn)上你都會知道你身處何處,,什么可以工作,什么不能工作,,你的系統(tǒng)里有什么最大的bug,。 Bug—也就是那些討厭的東西,會摧毀我們的信心,破壞我們的計(jì)劃和聲譽(yù),。如果已經(jīng)發(fā)布的軟件中有缺陷,,用戶就會對你很生氣。而正在進(jìn)行的工作中有bug,,則會擋住你的道路,,會使得接下來的工作變得更加困難。 持續(xù)集成不會避免bug,,但是它可以讓你非常容易找到并消除bug,。從這個(gè)角度看它很像自測代碼。如果你引入了一個(gè)bug后能很快檢測到它,,那么就能很容易改正它,。因?yàn)槟阒皇菍ο到y(tǒng)做了一個(gè)很小的改變,你不用往回追溯太遠(yuǎn),。因?yàn)橄到y(tǒng)的那一部分正是你剛剛工作過的那一部分,,它還很清楚的存在于你的記憶里—這使得查找bug很容易。你還可以使用diff debugging,,來對系統(tǒng)的當(dāng)前版本和沒有bug的更早的版本作比較,。 Bug也是累積的。你的bug越多,,消除每一個(gè)bug也就越不容易,。一定程度上是因?yàn)閎ug互相影響,表現(xiàn)出來的失敗可能是很多錯(cuò)誤共同疊加的結(jié)果—這就導(dǎo)致很難找到每一個(gè)錯(cuò)誤,。這也是心理上的,,當(dāng)有很多bug的時(shí)候,人們自然就缺少激情去找到并解決這些bug—這是一種在《Pragmatic Programmer》一書中被稱為“破窗效應(yīng)”的現(xiàn)象,。 因此,使用了持續(xù)集成的項(xiàng)目,,無論是在生產(chǎn)環(huán)境里,,還是在開發(fā)過程中,其bug的數(shù)量將會極大的減少,。但是需要強(qiáng)調(diào)的是,,受益的程度直接取決于你的測試套件的好壞。你應(yīng)該可以看到,,構(gòu)建一個(gè)帶來顯著差異的測試套件并不是那么困難,。當(dāng)然,通常一個(gè)團(tuán)隊(duì)真正達(dá)到他們可能達(dá)成的較低水平的bug程度,,需要一定的時(shí)間,。要達(dá)到這個(gè)水平意味著需要持續(xù)的改善你的工作。 如果你使用了持續(xù)集成,它會消除掉頻繁部署的一個(gè)最大障礙,。對于能夠?yàn)樾绿匦愿斓墨@得反饋來說,,頻繁部署是有價(jià)值的,因?yàn)樗梢允鼓愕挠脩艉芸煊蒙线@些新的特性,,而且頻繁部署可以使他們(開發(fā)團(tuán)隊(duì)和用戶)在開發(fā)周期中更加協(xié)作,。這將有利于打破客戶和開發(fā)之間的障礙—我認(rèn)為該障礙是成功的軟件開發(fā)中最大的障礙。 所以如果你想要嘗試下持續(xù)集成的話,,從哪兒開始呢?上面我提到的所有的實(shí)踐可以帶給你全部的收益,,但是你并不需要一開始就把它們都用上,。 這兒其實(shí)并沒有固定的套路,而是很大程度上依賴于你的配置和團(tuán)隊(duì)的現(xiàn)狀,。但是我們也學(xué)到下面的一些經(jīng)驗(yàn)可以幫助我們把接下來要做的事情搞清楚,。 第一步就是把構(gòu)建自動化。把你所有需要用到的東西都放到源碼控制系統(tǒng)中,,從而你可以用一條指令來構(gòu)建整個(gè)系統(tǒng),。對于很多項(xiàng)目來說,這并不是一件小事——但是它是其他所有的事情的基礎(chǔ),。一開始的時(shí)候你可能只是偶爾按需構(gòu)建,,或者只是做自動的每夜構(gòu)建。當(dāng)還不是持續(xù)集成的時(shí)候,,自動的每夜構(gòu)建也是一個(gè)很好的開始,。 在你的構(gòu)建中引入一些自動化測試。嘗試著識別出問題最多的部分,,并且加入自動化測試以暴露這些問題,。需要特別指出,在一個(gè)現(xiàn)存的項(xiàng)目上非??斓膶?shí)現(xiàn)一個(gè)真正好的測試套件是很困難的—因?yàn)樾枰獣r(shí)間來構(gòu)建測試,。你必須從某個(gè)地方開始—畢竟羅馬不是一天建成的。 盡量加速提交的構(gòu)建,。構(gòu)建需要幾個(gè)小時(shí)的持續(xù)集成也比沒有持續(xù)集成要強(qiáng),,但是如果能將這個(gè)時(shí)間降到10分鐘那就太好了。這通常需要對你的代碼基礎(chǔ)做一些相當(dāng)大的手術(shù),,因?yàn)槟阈枰蚱茖τ谙到y(tǒng)中較慢部分的依賴,。 如果你開始一個(gè)新的項(xiàng)目,那么從一開始就用持續(xù)集成,。一直關(guān)注構(gòu)建時(shí)間,,一旦構(gòu)建速度變慢,,超過10分鐘的時(shí)候,馬上采取行動,。通過很快的采取行動,,你可以在代碼變得太大以至于成為主要的痛點(diǎn)之前進(jìn)行必要的重構(gòu)。
上面所有的這些都可以給我們提供一些幫助,。找到一個(gè)以前做過持續(xù)集成的人來幫助你,。和任何新技術(shù)一樣,當(dāng)你還不知道最終結(jié)果會是什么樣子的時(shí)候,,很難引入該技術(shù),。找一個(gè)導(dǎo)師來可能會花一些錢,但是如果不這樣做,,你將不得不付出大量的時(shí)間和生產(chǎn)效率,。(免責(zé)聲明/廣告—是的,我們ThoughtWorks在該領(lǐng)域提供顧問工作,,畢竟我們已經(jīng)犯過大多數(shù)你們將要犯的錯(cuò)誤,。) 在Matt和我寫完這個(gè)網(wǎng)站上最初的論文后的幾年中,,持續(xù)集成變成了軟件開發(fā)的一個(gè)主流技術(shù),。ThoughtWorks的項(xiàng)目中很少有不用它的—并且我們可以看到遍布全球的很多人也在使用持續(xù)集成。不像一些有爭議的極限編程實(shí)踐一樣,,我很少聽到關(guān)于持續(xù)集成的負(fù)面反饋,。 如果你沒有正在使用持續(xù)集成,那么我強(qiáng)烈建議你嘗試一下,。如果你正在使用持續(xù)集成,,那么這篇文章里的一些想法可以幫助你做得更有效率。在過去的幾年里,,關(guān)于持續(xù)集成我們已經(jīng)學(xué)習(xí)了很多,,我希望依然有更多的東西可以學(xué)習(xí)和改善。
|