作者:Amr Elssamadisy 著,simaetin 譯
本文選自:UMLCHINA??
2002年05月13日?
我們?cè)赥houghtWorks這樣的大型項(xiàng)目中應(yīng)用XP方法的時(shí)間超過(guò)了15個(gè)月。這個(gè)項(xiàng)目開(kāi)始于三年前,那時(shí)它有大量的需求文檔和幾個(gè)獨(dú)立的功能小組。從2000年1月起,我們決定應(yīng)用XP,雖然當(dāng)時(shí)我們已經(jīng)知道XP并不適用于大型項(xiàng)目。那時(shí),我們需要向客戶提交功能演示,并要通過(guò)提交一個(gè)工作子集而不僅僅是原型來(lái)贏得他們的信心(我們?cè)趫F(tuán)隊(duì)中有超過(guò)25名開(kāi)發(fā)人員和大約15名分析員)。但我們并未準(zhǔn)備好功能,各個(gè)組都使用自己的代碼框架,一個(gè)完整的應(yīng)用無(wú)從談起。簡(jiǎn)短地說(shuō),就是客戶想要看到點(diǎn)什么,但我們還沒(méi)有真正的功能。應(yīng)該說(shuō)開(kāi)發(fā)的第一迭代期是成功的:我們給客戶提交了一個(gè)真正的應(yīng)用子集。成功的首要原因是我們讓來(lái)自Asset, AR 和GUI組從事不同工作的開(kāi)發(fā)者融合在一起。當(dāng)然,其間也有"成長(zhǎng)的煩惱":在分析員和開(kāi)發(fā)者之間發(fā)生某些個(gè)人沖突,但我們成功跨越了這些障礙。那時(shí)一切都很美好:我們以飛快的速度成功地提交了功能演示,但隨著時(shí)間的流逝和項(xiàng)目的繼續(xù),有些事情卻不如我們所愿。這就是這篇文章要講述的內(nèi)容:我們真正的收獲在"蜜月"期后,關(guān)于我們?nèi)绾卧谝荒臧胫泄芾硪粋€(gè)50人的項(xiàng)目開(kāi)發(fā)并及時(shí)完成階段性的提交(盡管有時(shí)并不成功),關(guān)于我們經(jīng)歷的痛苦,我們后續(xù)的工作,我們最終學(xué)到的以及作為改進(jìn)要用到下一個(gè)項(xiàng)目中的經(jīng)驗(yàn)。
首先,我們給出我們開(kāi)始的狀況:15個(gè)月前我們是如何應(yīng)用XP的,然后展示我們現(xiàn)在的XP應(yīng)用情況。也就是說(shuō),下面將討論我們改變技術(shù)的原因。這是一個(gè)自然選擇的過(guò)程,它剔除了許多不好的想法,或把它們改造成更有效的方法,使之更適于大型項(xiàng)目。最后我們將總結(jié)并給出在大型項(xiàng)目中使用XP的謹(jǐn)慎建議。
大型項(xiàng)目中XP的元素
好吧,讓我們來(lái)進(jìn)入正題。我們開(kāi)發(fā)和分析的隊(duì)伍由大約35名開(kāi)發(fā)人員、15名分析人員和10名QA組成。開(kāi)發(fā)人員依賴分析人員作為項(xiàng)目的客戶。盡管有實(shí)際的客戶,分析人員還是要協(xié)同工作以便有選擇地做出客戶決策。下表演示了[1]所討論的XP基本元素并簡(jiǎn)要介紹了這些方面如何應(yīng)用到項(xiàng)目的各個(gè)階段。我們用這個(gè)表來(lái)分析我們團(tuán)隊(duì)自然選擇的實(shí)踐,以及書(shū)本上的XP如何應(yīng)用到一個(gè)超過(guò)50人的大型項(xiàng)目中。
|
計(jì)劃
|
提交周期
|
比喻
|
設(shè)計(jì)的簡(jiǎn)單性
|
測(cè)試
|
重構(gòu)
|
1/2000
|
大跨度的迭代計(jì)劃會(huì)議。開(kāi)發(fā)和分析組的全部成員整天在一起討論新的故事卡片和預(yù)估。大多數(shù)開(kāi)發(fā)者簽入(sign up)新的功能。
|
1個(gè)月
|
無(wú)
|
轉(zhuǎn)變已有的代碼基準(zhǔn)。已有的代碼依然復(fù)雜,但新的代碼要盡可能簡(jiǎn)單。這個(gè)階段包括拋棄老的代碼和重寫(xiě)那些"今后可能會(huì)被用到"的功能代碼。
|
單元測(cè)試開(kāi)始于一些新的代碼。推動(dòng)建立一個(gè)大的測(cè)試基準(zhǔn)。QA做所有的功能測(cè)試并有權(quán)留棄故事卡片。
|
對(duì)于舊有代碼,如有必要就進(jìn)行重構(gòu)。
|
7/2000
|
基本同1/2000,但確實(shí)感覺(jué)十分低效。多數(shù)與會(huì)者沒(méi)能很好地參與。拖沓的討論,50人的會(huì)議是無(wú)法忍受的。
|
1個(gè)月
|
無(wú)
|
以盡可能簡(jiǎn)單的原則繼續(xù)設(shè)計(jì)。完成對(duì)已有設(shè)計(jì)的重構(gòu)。完成代碼會(huì)審,旨在全體范圍中討論新設(shè)計(jì)以便整個(gè)團(tuán)隊(duì)了解代碼和設(shè)計(jì)的發(fā)展趨勢(shì)。
|
更好的單元測(cè)試覆蓋更大的范圍,盡管沒(méi)有完全覆蓋。代碼功能測(cè)試幫助覆蓋測(cè)試范圍。
|
多數(shù)開(kāi)發(fā)人員埋頭于新功能的開(kāi)發(fā),很少會(huì)去做重構(gòu)。代碼基準(zhǔn)在迭代期末向QA組提交卡片時(shí)變得糟糕起來(lái)。
|
1/2001
|
希望能盡快做出每個(gè)迭代期間的計(jì)劃-我們的辦法是在正式會(huì)議前以小組為單位進(jìn)行更多的準(zhǔn)備工作。
|
2 周
|
無(wú)
|
多數(shù)的設(shè)計(jì)基于已有的設(shè)計(jì):保留標(biāo)準(zhǔn),代碼會(huì)審逐漸停止,因?yàn)樾碌脑O(shè)計(jì)和重構(gòu)尚未完成。
|
試圖去掉代碼功能測(cè)試而代之以屏幕搜刮(Screen Scraper),若失敗就回到代碼功能測(cè)試 。加入新的單元測(cè)試但仍然沒(méi)有全部覆蓋。QA組開(kāi)始結(jié)合界面測(cè)試使用自動(dòng)功能測(cè)試。
|
重構(gòu)開(kāi)始更頻繁,因?yàn)椴糠执a開(kāi)始變得凌亂不堪。需要清理的原因主要是實(shí)現(xiàn)簡(jiǎn)單和迭代期限使得代碼在沒(méi)有重要重構(gòu)的情況下增長(zhǎng)。
|
6/2001
|
舉行一些討論卡片或相關(guān)卡片組的會(huì)議,參加者包括對(duì)這些功能感興趣或有經(jīng)驗(yàn)的開(kāi)發(fā)者和負(fù)責(zé)這些卡片的分析人員。
|
2 周
|
無(wú)
|
隊(duì)伍中的大多數(shù)人及整個(gè)QA組準(zhǔn)備提交1.0版本給客戶。代碼的基準(zhǔn)分離以便加入沒(méi)有測(cè)試的新功能。對(duì)單元測(cè)試有更多的依賴。
|
盡管仍有遺漏,測(cè)試范圍已經(jīng)基本穩(wěn)定。QA組不再測(cè)試新功能,因?yàn)榻裹c(diǎn)是1.0版本的提交。
|
在發(fā)布版上做的重構(gòu)很少,而在繼續(xù)開(kāi)發(fā)的版本上,開(kāi)發(fā)人員會(huì)很盡責(zé)地進(jìn)行重構(gòu),特別是在年初被迫做了更大更痛苦的重構(gòu)以后.
|
|
結(jié)對(duì)編程
|
集體所有
|
持續(xù)集成
|
40小時(shí)周
|
在場(chǎng)客戶
|
代碼規(guī)范
|
1/2000
|
由于我們決定采用XP,整個(gè)團(tuán)隊(duì)讀了[1]。每個(gè)人都做了嘗試,多數(shù)人被吸引。
|
由于最初階段的分組是面向功能,因此此時(shí)我們并未意識(shí)到集體所有 (collective ownership) ,也沒(méi)有意識(shí)到代碼的保護(hù)。
|
從第一迭代期開(kāi)始,進(jìn)行在線集成,參見(jiàn)[2]
|
這是一個(gè)概念工作時(shí)間:因?yàn)槲覀兿M蛻粼趫?chǎng)。于是我們會(huì)花另外的時(shí)間來(lái)滿足最后期限。
|
事務(wù)分析人員是在場(chǎng)的客戶,他們15人一組。真正的客戶是不在的,由分析人員同他們溝通。 基本上是JAVA的一般語(yǔ)法。
|
7/2000
|
結(jié)對(duì)編程依舊盛行:開(kāi)發(fā)者對(duì)新功能進(jìn)行結(jié)對(duì)編程,但改錯(cuò)和維護(hù)工作由單個(gè)人來(lái)做。也有一些開(kāi)發(fā)者停止了結(jié)對(duì)編程。
|
當(dāng)開(kāi)發(fā)者越來(lái)越多地接觸
系統(tǒng)
不同的部分的時(shí)候,代碼的所有者逐漸顯現(xiàn)出來(lái)。成員間通過(guò)閑談,代碼互審和短小的站立會(huì)議(Stand up meeting)進(jìn)行很好的溝通。
|
代碼功能測(cè)試加入構(gòu)造過(guò)程。
|
為了通過(guò)故事卡片,在每個(gè)迭代期的最后工作時(shí)間達(dá)到50至60小時(shí)。
|
同上
|
兩周一次的代碼互審給開(kāi)發(fā)人員一個(gè)機(jī)會(huì)討論不同子
系統(tǒng)
的實(shí)現(xiàn)方式。我們可以接受某些子
系統(tǒng)
代碼的非正式方式。
|
1/2001
|
結(jié)對(duì)編程更少了,因?yàn)檫@一階段編碼更直接,而有些人在進(jìn)行重構(gòu)。
|
因?yàn)樾实停玖?huì)議被棄用,但代碼的所有顯現(xiàn)得更加清晰。開(kāi)發(fā)人員開(kāi)始專職負(fù)責(zé)
系統(tǒng)
的某部分。
|
穩(wěn)定_同上。
|
以2周為迭代期,開(kāi)發(fā)人員的工作時(shí)間更接近于40小時(shí)…
|
同上
|
代碼互審逐漸減少,設(shè)計(jì)及編碼規(guī)范趨于飽和。
|
6/2001
|
定下了規(guī)則:所有新功能要應(yīng)用結(jié)對(duì)編程,而改錯(cuò)和維護(hù)則由一人完成。
|
隨著專業(yè)化分工的繼續(xù),不同的開(kāi)發(fā)小組人員擁有不同部分代碼的知識(shí),于是我們將使他們?cè)诮酉聛?lái)相應(yīng)模塊的設(shè)計(jì)中起更活躍的作用,但代碼仍是集體共有。
|
穩(wěn)定同上。
|
同上
|
同上
|
同上
|
結(jié)對(duì)編程
首先我們說(shuō)說(shuō)結(jié)對(duì)編程的體會(huì)。多數(shù)情況下,我們?cè)谀硞€(gè)迭代期間有兩個(gè)開(kāi)發(fā)人員同時(shí)為一個(gè)故事卡片(或幾個(gè)迭代期的相關(guān)卡片)工作。在大型項(xiàng)目中開(kāi)發(fā)人員需要投入更多的關(guān)注,因?yàn)殚_(kāi)始新領(lǐng)域的編碼的熱身時(shí)間是不容忽視的。開(kāi)發(fā)人員間良好的溝通和周期性的計(jì)劃會(huì)議讓每個(gè)人都具有誰(shuí)在做什么的整體概念。這使得經(jīng)典教科書(shū)中開(kāi)發(fā)者甲找到開(kāi)發(fā)者乙要求共同解決問(wèn)題的結(jié)對(duì)編程方式成為可能。
結(jié)對(duì)編程當(dāng)然很好,但并非任何時(shí)侯都適用。最通常的情況是,在改錯(cuò)和維護(hù)時(shí)開(kāi)發(fā)人員并不愿結(jié)對(duì),并且這種情況下許多眼睛盯著調(diào)試代碼也確實(shí)沒(méi)什么好處。再有就是迭代期間的重復(fù)工作,這種情況下,問(wèn)題的解決方案已經(jīng)確定,不必再結(jié)對(duì)了。
還有,開(kāi)發(fā)人員有不同的個(gè)性:有些人需要間歇性的結(jié)對(duì)編程,而有些人更加出色,結(jié)對(duì)編程會(huì)妨礙他們才能的發(fā)揮而最終成為他們的負(fù)擔(dān)。
單元測(cè)試和集成構(gòu)造單元測(cè)試和集成構(gòu)造絕對(duì)是必要的,這意味著如果我們沒(méi)有測(cè)試,我們就不能提交任何代碼。當(dāng)應(yīng)用
程序變得越來(lái)越大時(shí),沒(méi)有測(cè)試我們就不能加入任何新功能或進(jìn)行重構(gòu)。我們現(xiàn)在在新代碼進(jìn)庫(kù)時(shí)會(huì)有集成構(gòu)造和測(cè)試。關(guān)于這些構(gòu)造及測(cè)試的細(xì)節(jié),負(fù)責(zé)人員會(huì)及時(shí)放在內(nèi)部網(wǎng)頁(yè)上,這樣每個(gè)開(kāi)發(fā)人員都能知道當(dāng)前的構(gòu)造狀態(tài),事務(wù)分析員和QA能拿到最近構(gòu)造的信息來(lái)測(cè)試新的功能。
組內(nèi)所有和信息共享
對(duì)于這樣一個(gè)大型的項(xiàng)目,為了防止被分為孤立的部分而使整個(gè)系統(tǒng)做出不適當(dāng)?shù)募僭O(shè),信息的發(fā)布和不同部分的代碼輪作非常重要。溝通是必需的(但我們無(wú)法強(qiáng)迫一個(gè)沉默的人開(kāi)口)于是我們?cè)噲D在每?jī)芍芤淮蔚亩虝?huì)上給每個(gè)人發(fā)言的機(jī)會(huì),以使沉默寡言的人能說(shuō)出他們的要求。而最終我們?nèi)∠诉@樣的會(huì)議,因?yàn)榇蠖鄶?shù)開(kāi)發(fā)人員認(rèn)為這種把每件事都蜻蜓點(diǎn)水地提一下的會(huì)議只是浪費(fèi)時(shí)間。這也是這個(gè)團(tuán)隊(duì)的優(yōu)點(diǎn)之一??我們總能象一個(gè)共同體般地工作,如同結(jié)對(duì)編程一樣地合作。
開(kāi)始,我們采用了輪作的方法,也就是每個(gè)人對(duì)每件事都作一點(diǎn),這使得我們?cè)诤髞?lái)快到截止期限的時(shí)候都在從事己經(jīng)了解的事情。但對(duì)于一段復(fù)雜的代碼,要做到這一點(diǎn),時(shí)間投入非常巨大。最好采用折衷的辦法:也
就是在項(xiàng)目時(shí)間緊的時(shí)候只作每個(gè)人熟悉的工作,而在其它時(shí)候,比如改錯(cuò)、研究或正在做一項(xiàng)熟悉的工作時(shí),可以同時(shí)做一件不熟悉的事。我們現(xiàn)在的原則是,開(kāi)發(fā)人員在幾個(gè)迭代周期中連續(xù)做一些相關(guān)的卡片,同時(shí)逐漸地轉(zhuǎn)向系統(tǒng)的其它部分。在同一個(gè)迭代期內(nèi)簽入幾個(gè)不同卡片的做法己經(jīng)不再用了。
代碼確實(shí)會(huì)越變?cè)皆恪J且驗(yàn)槲覀兊捻?xiàng)目有些大嗎?還是因?yàn)樵S多做代碼的人都是新手(是指這個(gè)功能領(lǐng)域中的新手,而非編程新手)?答案可能是二者皆有吧。但有時(shí)我們不接觸代碼,很難開(kāi)始系統(tǒng)的其它部分,因此定期清理代碼是必須的。這樣引出我們下一個(gè)論題:重構(gòu)。
重構(gòu)
在應(yīng)用XP的大型項(xiàng)目中,為了消除代碼的不一致性,重構(gòu)是絕對(duì)必要的。即便對(duì)于那些對(duì)項(xiàng)目的應(yīng)用領(lǐng)域很熟悉的人,也會(huì)面對(duì)重構(gòu)的巨大工作量望而卻步。對(duì)于項(xiàng)目經(jīng)理來(lái)說(shuō),必須認(rèn)識(shí)到重構(gòu)需要另行分配時(shí)間。我們做到了這一點(diǎn),我們留出了時(shí)間來(lái)重構(gòu)代碼的主要部分。
短迭代周期
迭代期及其期限是必須的,但長(zhǎng)度一直是個(gè)問(wèn)題。過(guò)去我們采用長(zhǎng)迭代周期(如一個(gè)月),而每到月末就會(huì)很緊張并伴隨著一些不良代碼的加入,并且不可避免地在估計(jì)上出現(xiàn)問(wèn)題。我們不得不接受一些未做的卡片(這對(duì)開(kāi)發(fā)人員來(lái)說(shuō)非常困難,并且難以滿足既定的期限)。
總結(jié)
下面把18個(gè)月中我們?cè)谶@個(gè)50人項(xiàng)目的經(jīng)驗(yàn)和教訓(xùn)列出如下:
1) 在每個(gè)迭代期開(kāi)始時(shí)進(jìn)行迭代計(jì)劃會(huì)議。每日客戶和開(kāi)發(fā)人員討論最近的故事卡片并評(píng)估它們,在每天的討論結(jié)束后重新分組并演示這些評(píng)價(jià)和發(fā)現(xiàn),然后讓開(kāi)發(fā)人員簽入。這可以讓整個(gè)項(xiàng)目組知道項(xiàng)目的當(dāng)前情況而不用讓每個(gè)人都卷入馬拉松式的會(huì)議。
2) 使提交版本周期盡可能短:我們是兩周一次,但在必要的時(shí)候也可以使提交跨多個(gè)迭代周期。允許在多個(gè)迭代周期簽入卡片,但要以每個(gè)周期為單位進(jìn)行進(jìn)度的監(jiān)控。
3) 進(jìn)行盡可能多的單元測(cè)試,這是不言而喻的。應(yīng)當(dāng)有一個(gè)自動(dòng)進(jìn)行功能測(cè)試的軟件包以保證測(cè)試的覆蓋范圍。但是QA組是不可替代的(無(wú)論開(kāi)發(fā)人員寫(xiě)過(guò)多少測(cè)試程序),因?yàn)槲覀儗?duì)系統(tǒng)怎樣工作總是存在偏見(jiàn)。
4) 簡(jiǎn)單的設(shè)計(jì)能幫助我們連續(xù)向客戶提供可工作的版本。頻繁的設(shè)計(jì)會(huì)議對(duì)于加入大量新功能時(shí)是很有用的,而午餐是一個(gè)召集整個(gè)項(xiàng)目組的好時(shí)候。這可以避免在系統(tǒng)的不同部分同時(shí)存在不兼容的解決方案。
5) 重構(gòu)是能夠做到簡(jiǎn)單設(shè)計(jì)的唯一方法。設(shè)計(jì)的重構(gòu)與代碼的重構(gòu)同等重要。盡管不進(jìn)行重構(gòu)而采用打補(bǔ)丁的解決方法往往是有吸引力的,但是如果一個(gè)系統(tǒng)的補(bǔ)丁太多,那就意味著今后它將進(jìn)行更大的重構(gòu)。
6) 在加入新功能時(shí),要堅(jiān)定不移地貫徹結(jié)對(duì)編程,而在改錯(cuò)和做重復(fù)性工作時(shí)停止,因?yàn)閱?wèn)題已經(jīng)在結(jié)對(duì)時(shí)解決了。
7) 集體所有和溝通密不可分。團(tuán)隊(duì)必須有一種有效的溝通方式。也許不僅僅是非正式的討論,有時(shí)定期的10到15分鐘的發(fā)布會(huì)是很好的方式。
8) 在大型項(xiàng)目中,一些人要扮演客戶的角色,為大量開(kāi)發(fā)人員產(chǎn)生足夠的工作。這和領(lǐng)域知識(shí)密切相關(guān)。
9) 編碼標(biāo)準(zhǔn)是十分非正式的,這不會(huì)損害你的進(jìn)度。更重要的是一種通過(guò)演示的溝通。代碼不是文檔的全部,開(kāi)發(fā)人員需要看到全貌,這是代碼不能提供的。
也有一些規(guī)則我們沒(méi)有實(shí)行:
1) 兩周一次的站立會(huì)議是低效的。可以選擇每月一次的迭代團(tuán)隊(duì)通氣會(huì)來(lái)替代。
2) 迭代計(jì)劃會(huì)議沒(méi)有必要讓全部人員參與。更好的方式是按更小的組進(jìn)行研討,而在每天下班前用30到45分鐘對(duì)卡片進(jìn)行討論。
3) 一個(gè)月的迭代期太長(zhǎng),不利于產(chǎn)生高質(zhì)量的代碼。2周一次的迭代周期更容易跟蹤并使估計(jì)更準(zhǔn)確。
4) 上一點(diǎn)在大代碼量時(shí)并不合適,特別是重構(gòu)大規(guī)模系統(tǒng)時(shí)。這時(shí)一張卡片會(huì)影響到多個(gè)迭代期。
5) 比喻(Metaphor)對(duì)于大型系統(tǒng)是不適合的。
6) 每周40小時(shí)我們來(lái)說(shuō)不成問(wèn)題,40小時(shí)是最少的情況,超時(shí)工作并沒(méi)有給我們帶來(lái)不利的影響。要重申的是,我們不是100%的實(shí)行結(jié)對(duì)編程。
這就是XP,或者說(shuō)我們的XP版本,在我們小組所做的工作。我們經(jīng)歷了按時(shí)提交大型復(fù)雜應(yīng)用的過(guò)程,也為每個(gè)開(kāi)發(fā)人員提供了應(yīng)對(duì)此類(lèi)項(xiàng)目的寶貴經(jīng)驗(yàn)。
參考書(shū)目
1. Beck, K. Extreme Programming Explained: Embrace Change. Addison-Wesley, 1999; ISBN201-61641-6
2. Fowler, M. and Foemmel, M.; Continuous Integration