專案回顧:一個開發人員的觀察與思考(轉)

ger8發表於2007-08-15
專案背景
這個專案的客戶是歐洲一家人壽保險公司。該保險公司目前進行的一個計劃是對現有的業務流程(business process)和IT系統進行較大規模的改造,以適應新的市場要求。我們的專案是這個計劃的一個組成部分,它的目標是建立一個瀏覽系統與保險公司的後端保險管理系統相連。這個瀏覽系統用Web瀏覽器作為使用者介面,以取代目前使用的字元終端介面。 2002年初,公司的有關部門(專門作金融服務)曾作了一些可行性研究工作。專案於2002年四月底正式啟動,主要的開發工作於10月底結束。然後由另一組人馬進行使用者接收測試(user acceptance test)。

技術與系統構架
系統採用J2EE技術,系統基本上是三層(3-tier)結構,即Web介面(web presentation),應用邏輯(application logic)和後端伺服器(backend)。

系統需求與規範
該專案主要有兩種文件提供給開發人員:一種是系統功能描述,由一系列的模組組成。每個模組類似於一個use case,主要給出了該模組需實現的螢幕/視窗(screen shot),並一般地描述了這些螢幕上的操作以及它們之間的切換。這樣的模組描述檔案在專案中被稱之為“故事板”(story board)。“故事板”由業務分析人員(business analyst)會同使用者產生。另一種文件是與後端伺服器介面的規範說明,如命令格式,資料格式等,由後端系統設計開發組提供。按照專案規定,兩種檔案都需要經過充分討論並基本穩定下來之後,由相關負責人員簽發(sign off),然後交給開發組。

專案團隊與工作地點
專案組有14個開發人員,2個業務分析人員(business analyst),一個專案經理。主要開發組在英國(10個開發人員),專案經理和其餘人員則在客戶所在地(與英國有一小時的飛行的距離)。

工作筆記
由於工作習慣,我一般每天都作工作筆記。而在這個專案中前兩個多月中,我也每週都寫一篇小結,主要是對專案執行的觀察(所計述的都是對我所在的公司本部開發組而言)以下便是稍作整理後的前十週的小結(略去了公司和人員的名字)。這些筆記可能顯得有些雜亂和瑣碎,列在這裡的主要目的是想忠實地把我眼中的專案進展情況展現出來。

第一週
開發組成員逐步聚集到專案組所在地。我們十來個人都坐在一起,邊上一個大白板。這種坐位設定非常利於交流,特別是Cockburn所稱的“滲透”式交流。各成員的技能方面也各有所專,如Java,EJB,JSP等。
專案有個時間錄入跟蹤系統(time tracker/timesheet)。每人需錄入每天所做的任務和時間。這主要用於專案管理,而這些歷史資料也是對新任務進行規劃評估的基礎。
這周的主要工作是設定工作環境。每人一個Windows NT或XP 2000作為工作站。我們選用的 IDE(Integrated Development Environment)是NetBeans。選用NetBeans的主要原因是經費問題(NetBeans是開放式原始碼並免費)。原始碼控制是用的Microsoft SourceSafe。原始碼庫放在一個專案所用的專門伺服器上。而專案的文件材料,如需求分析,系統架構以及開發管理檔案,如時間進度表等。
雖然專案已決定使用J2EE技術,但具體的系統架構仍然需要建立。初步的選擇是一個應用框架(application framework)。這個框架是由我以前參加的一個專案發展而來,我對它的起源和演化都相當瞭解,因此我也開始對這個框架進行評審(review)。
我注意到沒有單元測試環境,便開始作一些這方面的工作,因為組裡只有我有過建立及使用單元測試的經驗。

第二週
我建立了一個基於JUnit的單元測試框架,並寫了一份2頁紙的簡短介紹,包括單元測試的概念與實踐,以及一種稱之為“Mock Object”的技術,因為我認為我們的系統需用到這種技術。
本週有一次角色分配。一位精於JSP的同事將主要負責前端(front-end presentation),兩位對專案所涉的後端有較多知識並參加過可行性研究的同事將更多地與在客戶地點的business people (業務人員)打交道,一位對編碼標準(coding standards)有濃厚興趣的同事負責收集大家在編碼方面的問題並執筆編碼標準文件,還有一位精於Ant的同事主要做工具開發與配置管理(configuration management),等等。
我們的編碼標準主要是以Sun Microsystem的“Java Code Conventions”為藍本。另外,客戶要求原始碼中所有的public and protected methods必須要有JavaDoc說明。一些與我們的特定系統有關的問題,如命名慣例(naming convention),將隨著開發程式不斷地編入文件中。
大家對現有的“故事板”進行的討論分析,並分解出一些具體任務,然後大家根據自己的角色和興趣sign up(“認購”)任務並開始設計與編碼。
關於原始碼控制與原始碼庫,大家同意每天早上更新自己的工作版本(working copy)。如果要修改或加入檔案,在check in之前,一定要保證整個原始碼能透過編譯。

第三週
大家繼續上週的設計與編碼工作。同事們似乎都是Cockburn所說的“good citizen”,工作中人人爭先,個個奮勇。
隨著編碼的進展,一個問題開始出現了。我們這裡沒有後端系統,所以不能對開發的程式進行完整的測試與整合。
星期四,專案安排了一次電視會議(video conference),參加者是我們這邊的成員與在客戶那邊的成員。兩邊的同事透過電視見見面,每人作一簡短的自我介紹。然後主要是討論了當前開發中的問題。最嚴重的問題包括需求與規範文件沒能按時簽發(signoff),以及我們這邊沒有測試環境。

第四周
由於客戶對我們選用的應用框架(application framework)仍有疑慮,應客戶要求,我們公司找了一位有著非常豐富的開發經驗的、並且在我們目前所用的應用框架(application framework)上做出過系統的技術權威向客戶解答他們的問題。
新來的技術權威是位敏捷方法(agile methodology)的熱心者,我們在他的建議下準備採用類似XP的過程,首要目標是向客戶,也是向我們自己顯示出我們能出活(we can deliver)。週一下午,全組開了一個兩小時的會議:
確定了要實現的一個很基本的功能,該功能其實大部分的編碼已完成。
圍繞這個功能,分解出若干需完成的任務,如:原始碼審查(code review),原始碼修改, 單元測試,建立“啞”(dummy)後端以作為初步功能測試與整合,找一臺空機器以作 系統建造與演示,等等。
大家根據自己的特長與興趣來“瓜分”把這些任務。
根據每人對完成自己任務所需時間的估計制訂出工作流程與進度,以0.5天為一單元。
根據進度表,“交貨”時間為下週四中午12:00點。屆時,我們需提供一份release notes, 上面將列出系統安裝的步驟。按照這些步驟,我們需要:
在一臺只有作業系統的空機器上建立執行系統所需的環境;
編譯原始碼並安裝系統;
執行系統並演示所完成的功能。
因為下週一週二放假(慶祝女王登基五十週年),時間非常緊迫。大家開足馬力,開始工作。

第五週
儘管大家盡了最大努力,星期四的期限沒能完全達到,其主要原因是系統安裝上有些沒預料到的 困難。但最終在星期五中午完成了所有的要求並進行了系統演示。
星期五下午全組會議,主要是對這一週期中的開發過程進行討論,特別是那些感到最困難的事情。 大家發現以下這些任務花的時間比預想的多:
熟悉應用框架和系統架構;
建立“啞”後端服務系統以作測試環境
建立單元測試
系統安裝。

第六週
根據上週的討論,編碼標準(coding standards)進行了更新。
對第二三週所作的工作重新進行了討論並制訂了新的進度表。這個週期的工作其實包含了 三個use case(或“故事板”),每塊“故事板”基本上由兩個人負責。其他人則主要完善 單元測試框架及建造“啞”後端系統。
本週工作中發現,有一處設計/模型需修改以適應新的功能。多數人被吸引到討論中並提出 了若干方案。我提出用一種“角色模式(role pattern)”,因為我認為它非常適用我們 現在的情形。

第七週
關於修改模型的討論繼續。

討論與思考
下面的討論將主要圍繞一些XP的實踐原則展開。

使用者在場(On site custome r)
儘管開發組的一部分在客戶地點,但主要的開發工作卻是在公司本部進行的。這裡沒有使用者,沒有業務分析人員。這點顯然與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與“傳統”上的意義有些不同。首先,因為我們坐得都很靠近,如果某人有什麼問題,只要一叫,一般就會有人跳起來跑過去幫忙。這可算是一種 “基於解決問題”的pair programming。

另外一種更主要的是方式是編碼與單元測試的結對。由於測試者對於如何使用一個(待測試)的類一般不會有著與編碼者完全一致的思路,這樣對於發現問題是很有幫助的。另外,由於測試者一般都會在測試時詳細閱讀原始碼並與編碼者討論,這對於改進一個class的細部設計與實現也是很有用的。但這種審閱與原始碼有所不同,這裡主要是著重“邏輯”的正確與有效,而原始碼審閱則偏重於原始碼的風格與標準的統一。

還有一種方式可稱之為“結對排故”(pair debugging),我發現這種情況多在系統功能測試中出現。如果在測試中出現一個問題(bug),找來找去找不到(因為這時涉及的東西的較多),搞得昏頭脹腦。那麼最好是抓一個同事到螢幕邊上(最好不是和你搞同一個部分的),然後給他講講是怎麼回事。他可能會一眼看出問題所在(如果他曾遇到過類似的問題),或者會從另一個角度來提供一個思路。另外,常常也有這種情況,來幫忙的可能只是聽著,而你在講的時候可能就自己發現問題了。我想這是因為你在給其他人解釋一件事的時候,你實際上是在強迫你自己清理自己的思路,而這肯定是有助於找到問題的(特別是在昏頭脹腦的時候)。

編碼標準(coding standards)
專案開始的時候,我們就決定採用Sun的“Java Code Conventions”作為我們編碼標準的藍本。隨著專案的進行,大家不斷地討論並同意加入一些與專案有關的標準,例如:
所有的classes和所有的public and protected methods都必須要有JavaDoc註釋;
對於packages,classes,variables的命名標準;
如何使用集合(collection)型別,如變數的型別需是interface,如Map而非HashMap;
如何使用實數型別,如規定用double而非float;
如何使用logging(我們使用log4j);
如何處理exceptions,等等

原始碼審閱(code review)
原始碼審閱一直是專案的要求之一。但在專案的前半期,這點做得不是很正式。當然,一個主要的原因是大家想盡快地做出一些功能。這樣造成的一個後果是原始碼開始有些雜亂並且不一致。專案後半期開始比較嚴格地進行原始碼審閱,並且規定一個“故事板”的原始碼在進入系統測試之前一定要有正規的原始碼審閱。

進行原始碼審閱時,審閱者一般是根據編碼標準上所列的條款對原始碼進行檢查,看是否符合標準。同時,也可對一些具體實現上提出自己的看法。這些意見用一張專門的表格一項一項地記錄下來,交給編碼者修改或給出進一步的說明。最後,審閱者對原始碼複查,對每一項進行核對,滿意之後簽字認可。我們的經驗表明,這樣的原始碼審閱大大地提高了原始碼的質量以及可讀性和可維護性。另外一個作用是使refactoring得以經常及時的進行。

原始碼重整(refactoring)
我們專案裡,refactoring基本上是與編碼標準和原始碼審閱同步進行的。專案的前半期,基本沒有refactoring,儘管有些不好的碼段或實現被不斷的發現並記錄在案。當然,主要原因還是由於大家想集中精力先做出一些功能。在專案的後半期,開始和原始碼審閱一起較嚴格地執行。和原始碼審閱一樣,這樣做的結果是大大地提高了原始碼的質量。

以上這些就是對這個專案的一些觀察與思考。總之,對開發人員來說,這個專案有許多的不確定性,這主要反映在需求與規範檔案上,也反映在相關專案組之間的協調(或扯皮)上。專案組分散兩地,測試環境的缺乏都是開發中
的很大問題。在這種情況應用XP的實踐原則,如密切溝通,單元測試,原始碼審閱與重整,能有效的(也許是艱苦地)推進專案的進展。

後記
記得幾年前曾看過一篇文章是講中國的MBA的教育的。大意是說工商管理是個實踐性很強的專業,做MBA的一項主要工作是做大量的個案分析。而目前中國似乎還沒有足夠的個案,有待於現在的MBA們畢業後在工作中去積累。這些積累起來的個案將是今後MBA們一筆寶貴資源。由此想到軟體開發,何嘗不是如此。軟體開發是個實踐性很強的群體/團隊工作,這點從三十幾年前“軟體工程”的提出時,人們就已經認識到了。提出的方法也是層出不窮,但真正在實踐中運用的並不多。這幾年出現的以XP為代表的agile方法興起,主要原因就是在於它們是從工程實踐中提煉出來,而其可行性至少在一定條件下或範圍內又為他人的實踐所證明。那麼我覺得現在最重要的不是高談闊論,而是紮紮實實的實踐,即在瞭解這些原則後如何在工程實踐中加以運用。更進一步,如果能把這些實踐記錄下來,加以總結,這對自己和同道都是一件好事。基於這樣的想法,在下不避瑣碎,把自己對一個專案的觀察與思考寫下來,期望能拋磚引玉,看到更多的同道能把自己的經驗寫下來,與大家共享。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7839396/viewspace-960190/,如需轉載,請註明出處,否則將追究法律責任。

相關文章