XP提倡代碼歸屬集體所有,這樣做的理由是每個(gè)人都可以修改代碼,而不是等待別人來(lái)修改代碼。這種做法可以有效避免形成代碼之間的鴻溝。但集體代碼所有權(quán)也有它的問(wèn)題。
我們嘗試了由多人共享代碼的做法,其目的是為了加強(qiáng)交流,避免出現(xiàn)一段代碼只有一個(gè)人了解的情況。這種方法一開始工作的很好,但很快我們發(fā)現(xiàn)出現(xiàn)了很多的問(wèn)題,類的定義變得不清晰了,某些類變得臃腫,我們聞到了"Large Class"的味道。更為糟糕的是,這些類的清理和重構(gòu)相當(dāng)?shù)睦щy,因?yàn)檫@些類的客戶太多了。正是因?yàn)檫@些類被廣泛的使用,因此大家都對(duì)其進(jìn)行修改和擴(kuò)充,導(dǎo)致了代碼的混亂。于是,我們加大了重構(gòu)的力度,對(duì)這些類不斷的進(jìn)行審查和重構(gòu),但是新的問(wèn)題又出現(xiàn)了,很難找到一個(gè)平衡點(diǎn),既能夠保持團(tuán)隊(duì)的敏捷性,又保證類的高度可用性。更糟糕的是,不同的人對(duì)這些類有著不同的了解和期望,導(dǎo)致了這些類的設(shè)計(jì)風(fēng)格有些怪異。
在本文中,我們不只一次的強(qiáng)調(diào)過(guò),XP中所有的實(shí)踐是配合使用的。項(xiàng)目中采用集體代碼所有權(quán)不是不行,但有前提。在上面的例子中,我們至少犯了幾個(gè)錯(cuò)誤:
a. 沒(méi)有考慮到團(tuán)隊(duì)成員之間配合的默契程度。只有團(tuán)隊(duì)成員之間知識(shí)程度相近,或是具有相近的思維觀,例如都熟悉面向?qū)ο?,都熟悉設(shè)計(jì)模式。這樣他們才能夠共同保證代碼的質(zhì)量。在項(xiàng)目中,我們嘗試了一種新的做法,將集體所有權(quán)的范圍縮小到組,這個(gè)組由三到四個(gè)資深的開發(fā)人員組成,稱為核心組,負(fù)責(zé)構(gòu)建基礎(chǔ)的框架。這個(gè)組之間采用高效的共享代碼機(jī)制。其他的開發(fā)人員利用核心組的成果進(jìn)行工作,他們并不修改核心代碼,但可以提供修改意見。
b. 忽視了代碼質(zhì)量。不同人修改代碼要比單人修改代碼更容易導(dǎo)致代碼質(zhì)量的下降。必須考慮這個(gè)成本,并找出恢復(fù)代碼質(zhì)量的辦法。審查是非常有效的手段,F(xiàn)DD(Feature Driven Development 特征驅(qū)動(dòng)開發(fā))方法就非常強(qiáng)調(diào)審查在項(xiàng)目中的作用。只要成本能夠接受,再多的審查都是不過(guò)分的。
c. 代碼標(biāo)準(zhǔn)化。這里說(shuō)的代碼標(biāo)準(zhǔn)化不僅僅指代碼規(guī)范。還包括代碼是不是具有可讀性,代碼的結(jié)構(gòu)是否足夠的清晰。這些都可能導(dǎo)致團(tuán)隊(duì)集體擁有代碼時(shí)形成混亂。
此外,還應(yīng)該注意到集體代碼要求能夠頻繁的集成代碼。代碼必須要快速的同步和集成,共享代碼往往意味著同一個(gè)包,同一個(gè)類都有可能被同時(shí)修改。這樣大大增加了引入bug的可能。盡快的同步代碼時(shí)非常有必要的。
如果一個(gè)軟件組織不能夠解決這些問(wèn)題,冒然采用集體代碼所有權(quán)的實(shí)踐是比較危險(xiǎn)的。相反,可以考慮采用個(gè)人代碼所有權(quán),或是微團(tuán)隊(duì)代碼所有權(quán)。前者說(shuō)的是個(gè)人對(duì)個(gè)人的代碼負(fù)責(zé),后者說(shuō)的是兩到四個(gè)人對(duì)某部分代碼負(fù)責(zé)。
不論是個(gè)人代碼所有權(quán)還是微團(tuán)隊(duì)代碼所有權(quán),其立足的根本是有明確的開發(fā)人員對(duì)代碼負(fù)責(zé),他保證代碼的統(tǒng)一設(shè)計(jì)思路和風(fēng)格,負(fù)責(zé)代碼的客戶端接口,負(fù)責(zé)維護(hù)和改進(jìn)代碼,負(fù)責(zé)代碼的相關(guān)文檔,負(fù)責(zé)解釋代碼的運(yùn)行機(jī)理。個(gè)人代碼所有權(quán)是最清晰的做法,但其壞處和集體所有權(quán)正好相反。某個(gè)人的代碼可能造成進(jìn)度的瓶頸,任何一個(gè)人離開團(tuán)隊(duì)都會(huì)造成損失。
個(gè)人代碼所有權(quán)很容易理解,但微團(tuán)隊(duì)代碼所有權(quán)就需要特別做解釋了。他的組織思路非常類似于我們?cè)诮Y(jié)對(duì)編程中提倡的組織風(fēng)格:
不同的人負(fù)責(zé)不同的代碼,人員之間形成交叉。這樣的組織比較靈活,和結(jié)對(duì)編程有著異曲同工之妙。
在Martin Fowler的持續(xù)集成(在Agilechina網(wǎng)站上可以找到該文的中文譯本)一文中,對(duì)持續(xù)集成有著這樣的描述:
在軟件開發(fā)的領(lǐng)域里有各種各樣的"最佳實(shí)踐",它們經(jīng)常被人們談起,但是似乎很少有真正得到實(shí)現(xiàn)的。這些實(shí)踐最基本、最有價(jià)值的就是:都有一個(gè)完全自動(dòng)化的創(chuàng)建、測(cè)試過(guò)程,讓開發(fā)團(tuán)隊(duì)可以每天多次創(chuàng)建他們的軟件。"日創(chuàng)建"也是人們經(jīng)常討論的一個(gè)觀點(diǎn),McConnell在他的《快速軟件開發(fā)》中將日創(chuàng)建作為一個(gè)最佳實(shí)踐來(lái)推薦,同時(shí)日創(chuàng)建也是微軟很出名的一項(xiàng)開發(fā)方法。但是,我們更支持XP社群的觀點(diǎn):日創(chuàng)建只是最低要求。一個(gè)完全自動(dòng)化的過(guò)程讓你可以每天完成多次創(chuàng)建,這是可以做到的,也是完全值得的。
和本文提到的其它實(shí)踐一樣,持續(xù)集成的主要思路是將軟件過(guò)程末期的軟件集成分?jǐn)偟杰浖娜^(guò)程。雖然沒(méi)有辦法評(píng)判兩種持續(xù)方式的成本,但是持續(xù)集成可以獲得很多額外的好處。單次集成最要命的地方是除bug的過(guò)程,尤其是那些隱藏的很深,讓人覺(jué)得無(wú)從下手的bug。如果說(shuō)寫代碼是一種享受,那修復(fù)bug的過(guò)程絕對(duì)是一種煎熬。在這個(gè)過(guò)程中花費(fèi)的時(shí)間有時(shí)候是驚人的,更糟糕的問(wèn)題是,這部分的時(shí)間根本無(wú)法估計(jì),這令項(xiàng)目管理者頭疼不已。向編碼者詢問(wèn)進(jìn)度時(shí)得到的回復(fù)永遠(yuǎn)都是"還差一點(diǎn)兒"。
持續(xù)集成避免了這種尷尬處境,由于間隔的時(shí)間很短,集成中出現(xiàn)的問(wèn)題可以很輕易的發(fā)現(xiàn)。即便無(wú)法定位錯(cuò)誤,最差的情況也可以不把代碼集成到軟件中。這樣,軟件的質(zhì)量就會(huì)比較高。此外,持續(xù)集成的另一個(gè)重要任務(wù)是運(yùn)行自動(dòng)化測(cè)試,保證所有的代碼都是經(jīng)過(guò)測(cè)試的,沒(méi)有發(fā)生問(wèn)題的。這里的測(cè)試源自于單元測(cè)試,在本文的測(cè)試一章,可以找到更為詳細(xì)的討論。
對(duì)一些沒(méi)有持續(xù)集成經(jīng)驗(yàn)的團(tuán)隊(duì)來(lái)說(shuō),持續(xù)集成像是一塊吊的很高的餅,看得見卻摸不著。要做好持續(xù)繼承并不容易,但我們可以使用持續(xù)集成的思路,來(lái)接近持續(xù)集成的目標(biāo)。
持續(xù)集成最好的做法是自動(dòng)化的構(gòu)建和測(cè)試。這要求軟件組織擁有很好的配置管理機(jī)制,以及豐富的測(cè)試腳本編寫經(jīng)驗(yàn)。對(duì)于一個(gè)企業(yè)應(yīng)用軟件來(lái)說(shuō)(抱歉,我只有這方面的經(jīng)驗(yàn)),軟件設(shè)計(jì)包括很多的因素,要把這些因素都考慮到持續(xù)集成的過(guò)程中并不是一件容易的事情。因此很多組織都可能缺少這兩個(gè)因素,但沒(méi)有關(guān)系,我們可以利用半手工的方式來(lái)完成持續(xù)集成,然后再慢慢的將持續(xù)集成的過(guò)程自動(dòng)化。這里提供一個(gè)半自動(dòng)持續(xù)集成的思路和改進(jìn)過(guò)程。
首先是定義職責(zé)。如果你采用了代碼非集體所有權(quán)的形式,那么,請(qǐng)明確的指定各個(gè)類或包(對(duì)于面向過(guò)程語(yǔ)言來(lái)說(shuō)是函數(shù)和模塊)的負(fù)責(zé)團(tuán)隊(duì)(或個(gè)人)。同樣,你還需要指定數(shù)據(jù)庫(kù)模式的負(fù)責(zé)人。這些代碼之間可能會(huì)有交叉的地方,但沒(méi)有關(guān)系,只要保證溝通,少量的交叉職責(zé)沒(méi)什么特別的。最好還必須指定一個(gè)專門負(fù)責(zé)持續(xù)集成的人,可以讓項(xiàng)目中的不同人交替擔(dān)任該職責(zé)。
其次是定義自動(dòng)化代碼。所有的測(cè)試都必須寫成測(cè)試代碼的形式,并能夠運(yùn)行。所有的數(shù)據(jù)庫(kù)模式定義也必須編寫為DDL的形式,而不是使用數(shù)據(jù)庫(kù)工具。千萬(wàn)別偷這個(gè)懶??偟脑瓌t是,能夠?qū)懗纱a的都寫成代碼,只有代碼才是可以執(zhí)行的。我承認(rèn),工作量是很大的,但這是必須的。
再次是定義集成的頻度。頻度的制定取決于團(tuán)隊(duì)的規(guī)模和溝通質(zhì)量。對(duì)于小的團(tuán)隊(duì)而言,一小時(shí)一次的集成也是可行的。對(duì)于大的項(xiàng)目,可以劃分子團(tuán)隊(duì),子團(tuán)隊(duì)中采用頻繁集成(一小時(shí)一次),子團(tuán)隊(duì)之間采用日集成(每天集成一次)。有了子團(tuán)隊(duì)的集成保證,整個(gè)團(tuán)隊(duì)的集成一般不會(huì)有什么問(wèn)題。
接下來(lái)是使用工具。最需要的是版本控制工具,可以選用正式的,例如ClearCase。也可以選用簡(jiǎn)單的,例如SourceSafe,還可以選用免費(fèi)的(CVS)。都沒(méi)有關(guān)系,關(guān)鍵在于是否合用。代碼和文檔的集成都通過(guò)這個(gè)工具,數(shù)據(jù)庫(kù)的集成則通過(guò)數(shù)據(jù)庫(kù)管理員(就是第一步指定的負(fù)責(zé)數(shù)據(jù)庫(kù)模式的人)。而集成負(fù)責(zé)人負(fù)責(zé)協(xié)調(diào)集成過(guò)程,保證集成的成功。
最后是發(fā)現(xiàn)問(wèn)題,這只是一個(gè)開始,你在集成的過(guò)程一定會(huì)遇到各種各樣的問(wèn)題的。例如集成的時(shí)間,數(shù)據(jù)的相關(guān)性,設(shè)計(jì)的耦合度,測(cè)試代碼的變化等。沒(méi)有關(guān)系,這里存在一個(gè)自適應(yīng)的過(guò)程。一開始的持續(xù)集成過(guò)程一定是錯(cuò)誤百出的,慢慢的就會(huì)穩(wěn)定下來(lái),這時(shí)候就是改進(jìn)的時(shí)候了,改進(jìn)的主要目標(biāo)始終都是過(guò)程自動(dòng)化。有時(shí)候,為了保證集成的質(zhì)量,我們要求出現(xiàn)錯(cuò)誤的人請(qǐng)吃冰淇淋,不要笑,這也是過(guò)程的一部分,實(shí)踐證明,這可是非常有效的,所有人在提交代碼之前都會(huì)很認(rèn)真的保證測(cè)試成功。
代碼標(biāo)準(zhǔn)是非常基礎(chǔ)的管理常識(shí)。但是我們這里并不打算再贅述代碼標(biāo)準(zhǔn)的問(wèn)題。我們將重點(diǎn)討論開發(fā)標(biāo)準(zhǔn)。開發(fā)標(biāo)準(zhǔn)包括各種各樣的標(biāo)準(zhǔn)。例如過(guò)程的標(biāo)志、文檔的標(biāo)準(zhǔn)、設(shè)計(jì)模型的標(biāo)準(zhǔn)、代碼風(fēng)格的標(biāo)準(zhǔn)、變量命名的標(biāo)準(zhǔn)、大小寫標(biāo)準(zhǔn)等等。在XP中,其實(shí)并不非常強(qiáng)調(diào)標(biāo)準(zhǔn),因?yàn)閄P提倡簡(jiǎn)單的做法,但有時(shí)候標(biāo)準(zhǔn)往往是違背這一準(zhǔn)則的,因?yàn)樗鼤?huì)帶來(lái)額外的標(biāo)準(zhǔn)化成本。而重量級(jí)方法之所以笨重,過(guò)分遵循標(biāo)準(zhǔn)正是一大原因。
但在實(shí)際的過(guò)程中,我認(rèn)為寧可多投入一些資源在標(biāo)準(zhǔn)制定的執(zhí)行上。這和國(guó)內(nèi)目前的軟件開發(fā)實(shí)際情況有關(guān)系。國(guó)外的輕量級(jí)方法出現(xiàn)在重量級(jí)方法之后,大部分的程序員都經(jīng)歷過(guò)強(qiáng)制性的標(biāo)準(zhǔn)化過(guò)程。但是國(guó)內(nèi)不同,雖然輕量級(jí)方法很好,但是理解或執(zhí)行不當(dāng)?shù)脑?,卻常常導(dǎo)致畫虎不成反類犬的后果。國(guó)內(nèi)很多軟件組織的開發(fā)過(guò)程仍然處于無(wú)序的狀態(tài),而開發(fā)人員也鮮有標(biāo)準(zhǔn)過(guò)程的經(jīng)驗(yàn),在這樣一種情況下,盲目的推崇敏捷的做法,其實(shí)骨子里仍然是一種混沌的狀態(tài)。
因此,標(biāo)準(zhǔn)的制定和執(zhí)行總是值得的,雖然會(huì)需要一定的成本,但這個(gè)成本同它的帶來(lái)的改善溝通、促進(jìn)積累等效益比起來(lái)不算什么。
但是標(biāo)準(zhǔn)的制定絕對(duì)不是要把程序員上洗手間的時(shí)間都規(guī)范起來(lái),如何保持標(biāo)準(zhǔn)的平衡是敏捷方法的重點(diǎn)。XP認(rèn)為代碼規(guī)范就已經(jīng)足夠了。但我認(rèn)為至少還有幾種標(biāo)準(zhǔn)是需要重視的:
文檔標(biāo)準(zhǔn):這是一個(gè)大的話題,最好的文檔標(biāo)準(zhǔn)是UML。一幅圖勝過(guò)千言萬(wàn)語(yǔ)。當(dāng)UML2.0出世之后,UML將會(huì)越來(lái)越強(qiáng)大。因此,使用UML來(lái)代替部分的文檔是必要的。UML的相關(guān)資料有很多,這里不做過(guò)多的討論,但關(guān)于UML的一句忠告是,不要試圖在一開始就利用UML的所有類型的圖,也不要一開始就利用UML所有特性。這種做法,和擺弄文字處理器的特性,與不寫文檔的做法沒(méi)啥區(qū)別。
設(shè)計(jì)標(biāo)準(zhǔn):同樣是一個(gè)大的話題。設(shè)計(jì)標(biāo)準(zhǔn)包括如何進(jìn)行設(shè)計(jì),如何表示設(shè)計(jì),如何設(shè)計(jì)架構(gòu),如何設(shè)計(jì)各個(gè)層次等等問(wèn)題。在一個(gè)組織中貫徹設(shè)計(jì)標(biāo)準(zhǔn)是一個(gè)長(zhǎng)期的過(guò)程。但仍然是那句話,做總是比不做的好。
代碼風(fēng)格標(biāo)準(zhǔn):XP非??粗写a的可讀性,代碼可讀性好代表了程序員的水平,設(shè)計(jì)人員的努力。提高代碼可讀性的最佳實(shí)踐是重構(gòu)和復(fù)審,這兩項(xiàng)實(shí)踐在本文的其它位置都有詳細(xì)的討論。
界面標(biāo)準(zhǔn):界面標(biāo)準(zhǔn)有時(shí)候只是一個(gè)很小的問(wèn)題,但對(duì)于現(xiàn)代的軟件來(lái)說(shuō),界面正扮演越來(lái)越重要的角色。而在軟工領(lǐng)域,優(yōu)秀界面的關(guān)鍵是一致性。同樣的按鈕必須有同樣的大小、位置和字體。制定一份界面標(biāo)準(zhǔn)是非常關(guān)鍵的。
如何制定標(biāo)準(zhǔn):標(biāo)準(zhǔn)的制定其實(shí)并不需要大量的說(shuō)明文檔,這些文檔晦澀難懂,也不會(huì)有多少人會(huì)看,就算看了也未必會(huì)懂。只能是浪費(fèi)時(shí)間。最好的方式是示例和培訓(xùn)。文檔標(biāo)準(zhǔn)的說(shuō)明就編寫一份文檔范例,并加以簡(jiǎn)短的說(shuō)明。再輔以面對(duì)面的講解,其效果要遠(yuǎn)遠(yuǎn)超過(guò)說(shuō)明文檔。
林星,辰訊軟件工作室項(xiàng)目管理組資深項(xiàng)目經(jīng)理,有多年項(xiàng)目實(shí)施經(jīng)驗(yàn)。辰訊軟件工作室致力于先進(jìn)軟件思想、軟件技術(shù)的應(yīng)用,主要的研究方向在于軟件過(guò)程思想、Linux集群技術(shù)、OO技術(shù)和軟件工廠模式。您可以通過(guò)電子郵件 iamlinx@21cn.com和他聯(lián)系。
聯(lián)系客服