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