這個項目的客戶是歐洲一家人壽保險公司。該保險公司目前進行的一個計劃是對現有的業務流程(business process)和IT系統進行較大規模的改造,以適應新的市場要求。我們的項目是這個計劃的一個組成部分,它的目標是建立一個瀏覽系統與保險公司的后端保險管理系統相連。這個瀏覽系統用Web瀏覽器作為用戶界面,以取代目前使用的字符終端界面。 2002年初,公司的有關部門(專門作金融服務)曾作了一些可行性研究工作。項目于2002年四月底正式啟動,主要的開發工作于10月底結束。然后由另一組人馬進行用戶接收測試(user aclearcase/" target="_blank" >cceptance test)。
系統采用J2EE技術,系統基本上是三層(3-tier)結構,即Web接口(web presentation),應用邏輯(application logic)和后端服務器(backend)。
該項目主要有兩種文檔提供給開發人員:一種是系統功能描述,由一系列的模塊組成。每個模塊類似于一個use case,主要給出了該模塊需實現的屏幕/窗口(screen shot),并一般地描述了這些屏幕上的操作以及它們之間的切換。這樣的模塊描述文件在項目中被稱之為“故事板”(story board)?!肮适掳濉庇蓸I務分析人員(business analyst)會同用戶產生。另一種文檔是與后端服務器接口的規范說明,如命令格式,數據格式等,由后端系統設計開發組提供。按照項目規定,兩種文件都需要經過充分討論并基本穩定下來之后,由相關負責人員簽發(sign off),然后交給開發組。
項目組有14個開發人員,2個業務分析人員(business analyst),一個項目經理。主要開發組在英國(10個開發人員),項目經理和其余人員則在客戶所在地(與英國有一小時的飛行的距離)。
由于工作習慣,我一般每天都作工作筆記。而在這個項目中前兩個多月中,我也每周都寫一篇小結,主要是對項目運行的觀察(所計述的都是對我所在的公司本部開發組而言)以下便是稍作整理后的前十周的小結(略去了公司和人員的名字)。這些筆記可能顯得有些雜亂和瑣碎,列在這里的主要目的是想忠實地把我眼中的項目進展情況展現出來。
第3-6月基本上是順著第6-10周的路子走,但也有一些明顯的變化:
下面的討論將主要圍繞一些XP的實踐原則展開。
盡管開發組的一部分在客戶地點,但主要的開發工作卻是在公司本部進行的。這里沒有用戶,沒有業務分析人員。這點顯然與XP的原則相違背,而其造成的不利影響貫穿于項目始終。首先是對制訂計劃與進度表的影響。一般來說,在制訂計劃之前,開發人員需要花一兩天讀一個“故事板”,然后作出初步估計。故事板簡單易讀,能使人很快地大致了解一個功能模塊的需求。但是,很多地方比較模糊,有些地方在閱讀文檔中仔細一點就能看出來。這時最好有用戶/業務人員在場,馬上就能澄清。象我們靠電話和email,時間花的多,還不見得能說得很清楚。
第二個影響要更嚴重一些?!肮适掳濉敝械牟淮_定性有些在讀故事板時能發現,而有些就只能到了編碼的時候才能暴露出來。由于業務分析人員在客戶那邊,只能靠電話和email,其質量當然不如面對面的討論好。特別是當不能及時得到答案而時間又緊迫時,程序員往往得靠經驗和“推理”作一些假定。如果是錯的話,就只能在功能測試和用戶接收測試時再改了。
另外一個類似問題是與后端系統通訊的規范說明,這是由另一個項目組提供。那么我們與他們之間也是有個溝通的問題。而糟糕的是該項目組也是在客戶那邊,因此只能靠電話和email??傊?,我覺得這個項目的實踐是從反面證明了用戶在場的重要性。
如上所言,制訂計劃和進度表是從閱讀“故事板”和規范說明開始。經過若干電話與email的往來后,制訂出一個計劃,主要是把需完成的模塊進一步分解成一些任務,以及估計完成每個任務要花的時間,以0.5天為一個單元。該計劃是一個spreadsheet文件,放在項目的文檔服務器上,大家隨時可查看。
我們的項目有一點和正規的use case driven的過程不太一樣。一般是一個周期主要是做一個 use case。在我們項目中,一個“故事板”大概算一個use case。如在“工作筆記”中所言,在項目走上“正軌”后(從第六周開始),一般是2-3人做一個“故事板”。所以,一般同時可能有兩三個“故事板”在進行。每個“故事板”的周期約3-4周。這樣做的好處是提高了生產率,但我覺得這樣做的前提條件是每個開發人員必須是有足夠的經驗與技能,還有就是熟悉開發過程(第4-5周的實踐是非常重要的)。
單元測試是客戶的一個要求。在前兩個月,我們做還有些“過分”。我們是用NetBeans提供的工具對每個類(class)都生成相應的JUnit測試類(test class)。對一個類而言,其中的每一個 public和protected method都在test class中有對應的一個test method。而這個test method 里只有一個fail語句。這樣強迫你用測試來替換這個fail(當然你也可以把它刪掉)。結果我們發現花了很多時間去寫getter和setter的測試,實在不值得,因為這些methods非常簡單。我們因此說服客戶,允許我們不用對getter和setter些測試。不過,我倒是發現,對 getter的測試,實際上是在測試相應attribute的初始值。設置初始值常常被忽略,而會在運行中引起一些問題,如最常見的NullPointerException。
單元測試通常要求把被測的對象孤立起來,即測試不要用到其他的類。而實際中,一個類往往要用到其他的class提供的服務來完成自己的功能,最常見的如使用數據庫或一個遠程服務。單元測試需要切斷這種依賴性,即一次只測試某個我們需要測的類。這可以利用Mock Object (“模擬對象”)技術來達到,例如,用一個mock service來代替真正的service,而我們可以很方便地對這個mock service進行狀態設置。在我們的單元測試中就大量地運用了這種技術。網上有一些工具可用來幫助產生mock objects,如MockMaker,可以節省一些時間。
總的來說,單元測試的確提高了源碼質量。那些逐步建立起來的test cases,我感覺是起到了一張 “警戒網”的作用。源碼修改需要作改動時,這種作用特別明顯。不過,我們也注意到,對一個類進行完整的單元測試所花的時間往往不少于編碼的時間。因此,一個很重要的問題是決定測試到什么程度。XP的建議是對“可能會出問題”的地方要重點測試。但什么是“可能會出問題”的地方則需視具體情況而定。
由于我們這里沒有一個真正的后端系統,因此沒辦法作真正的集成與功能測試。在項目的前半期,我們曾建立了一個“啞”后端,這的確對我們的測試/調試起到了很大作用。但是,當越來越多的功能加入后,“啞”后端變得越來越負責和不可維護。最后,我們只得放棄這種做法。
項目的管理層曾作了很大努力,想使我們這里能直接連上客戶那里的后端系統,這在技術上是完全沒有問題的(畢竟這是網絡時代)。但不幸的是,這個合理要求沒能得到滿足。所以項目后半期,當一個“故事板”的的編碼與單元測試完成后,一般由編碼者或測試者飛到客戶那里去做集成與功能測試。
在我們這個項目的實際中,pair programming與“傳統”上的意義有些不同。首先,因為我們坐得都很靠近,如果某人有什么問題,只要一叫,一般就會有人跳起來跑過去幫忙。這可算是一種 “基于解決問題”的pair programming。
另外一種更主要的是方式是編碼與單元測試的結對。由于測試者對于如何使用一個(待測試)的類一般不會有著與編碼者完全一致的思路,這樣對于發現問題是很有幫助的。另外,由于測試者一般都會在測試時詳細閱讀源碼并與編碼者討論,這對于改進一個class的細部設計與實現也是很有用的。但這種審閱與源碼有所不同,這里主要是著重“邏輯”的正確與有效,而源碼審閱則偏重于源碼的風格與標準的統一。
還有一種方式可稱之為“結對排故”(pair debugging),我發現這種情況多在系統功能測試中出現。如果在測試中出現一個問題(bug),找來找去找不到(因為這時涉及的東西的較多),搞得昏頭脹腦。那么最好是抓一個同事到屏幕邊上(最好不是和你搞同一個部分的),然后給他講講是怎么回事。他可能會一眼看出問題所在(如果他曾遇到過類似的問題),或者會從另一個角度來提供一個思路。另外,常常也有這種情況,來幫忙的可能只是聽著,而你在講的時候可能就自己發現問題了。我想這是因為你在給其他人解釋一件事的時候,你實際上是在強迫你自己清理自己的思路,而這肯定是有助于找到問題的(特別是在昏頭脹腦的時候)。
項目開始的時候,我們就決定采用Sun的“Java Code Conventions”作為我們編碼標準的藍本。隨著項目的進行,大家不斷地討論并同意加入一些與項目有關的標準,例如:
源碼審閱一直是項目的要求之一。但在項目的前半期,這點做得不是很正式。當然,一個主要的原因是大家想盡快地做出一些功能。這樣造成的一個后果是源碼開始有些雜亂并且不一致。項目后半期開始比較嚴格地進行源碼審閱,并且規定一個“故事板”的源碼在進入系統測試之前一定要有正規的源碼審閱。
進行源碼審閱時,審閱者一般是根據編碼標準上所列的條款對源碼進行檢查,看是否符合標準。同時,也可對一些具體實現上提出自己的看法。這些意見用一張專門的表格一項一項地記錄下來,交給編碼者修改或給出進一步的說明。最后,審閱者對源碼復查,對每一項進行核對,滿意之后簽字認可。我們的經驗表明,這樣的源碼審閱大大地提高了源碼的質量以及可讀性和可維護性。另外一個作用是使refactoring得以經常及時的進行。
我們項目里,refactoring基本上是與編碼標準和源碼審閱同步進行的。項目的前半期,基本沒有refactoring,盡管有些不好的碼段或實現被不斷的發現并記錄在案。當然,主要原因還是由于大家想集中精力先做出一些功能。在項目的后半期,開始和源碼審閱一起較嚴格地執行。和源碼審閱一樣,這樣做的結果是大大地提高了源碼的質量。
以上這些就是對這個項目的一些觀察與思考??傊?,對開發人員來說,這個項目有許多的不確定性,這主要反映在需求與規范文件上,也反映在相關項目組之間的協調(或扯皮)上。項目組分散兩地,測試環境的缺乏都是開發中的很大問題。在這種情況應用XP的實踐原則,如密切溝通,單元測試,源碼審閱與重整,能有效的(也許是艱苦地)推進項目的進展。
記得幾年前曾看過一篇文章是講中國的MBA的教育的。大意是說工商管理是個實踐性很強的專業,做MBA的一項主要工作是做大量的個案分析。而目前中國似乎還沒有足夠的個案,有待于現在的MBA們畢業后在工作中去積累。這些積累起來的個案將是今后MBA們一筆寶貴資源。由此想到軟件開發,何嘗不是如此。軟件開發是個實踐性很強的群體/團隊工作,這點從三十幾年前“軟件工程”的提出時,人們就已經認識到了。提出的方法也是層出不窮,但真正在實踐中運用的并不多。這幾年出現的以XP為代表的agile方法興起,主要原因就是在于它們是從工程實踐中提煉出來,而其可行性至少在一定條件下或范圍內又為他人的實踐所證明。那么我覺得現在最重要的不是高談闊論,而是扎扎實實的實踐,即在了解這些原則后如何在工程實踐中加以運用。更進一步,如果能把這些實踐記錄下來,加以總結,這對自己和同道都是一件好事?;谶@樣的想法,在下不避瑣碎,把自己對一個項目的觀察與思考寫下來,期望能拋磚引玉,看到更多的同道能把自己的經驗寫下來,與大家共享。
胡健
Email: jian4_hu2@hotmail.com
Web: http://members.tripod.com/jian_hu