當我三年前第一次在 developerWorks 上讀到這篇文章(漫談企業應用項目的軟件開發過程:一個 PRM 系統實施的經驗與教訓)時,就發現它是一篇非常難得的好文章 —— 國內類似這樣的軟件工程案例分析太少了,很多人沒有時間寫或舍不得與旁人分享屋中瑰寶,何況這篇文章還是專門針對 XP、RUP 涉及到敏捷統一過程實踐的。 除了這篇 PRM (Partner Relationship Management)案例外, Johnson 其實早在 2002 年 7 月還發表過一篇《從一個項目談 XP 在國內的應用》,該文在網絡上也流傳甚廣。在我的印象中,這兩篇文章好像是國內(互聯網上)最早公開的 XP 實踐案例,而且還是嘗試 XP、RUP 整合的案例。姑且不論它們是否真正做到了敏捷,整合是否成功,這兩個應用案例的結局恰好一個成功,一個失敗,其價值就在于真實性和典型性,具有很好的說服力和教育意義。通過案例介紹用事實和數據說話,我為 Johnson 的勇氣和科學態度叫好!不管結果如何,PRM 原文的篇幅不長,卻有很多值得我們借鑒、學習的地方。除了作者總結的經驗和教訓之外,我還看出了一些其他問題,有一些新的聯想和補充,于是寫下來與大家交流探討。 我個人認為 PRM 這個項目無論從商務角度,還是從工程技術的角度來看,都是比較失敗的。印象最深刻的一處是 PRM 系統雖然通過 2 個月緊張的敏捷、迭代開發準時交付使用,卻由于后來出現性能問題,過了大半年仍然沒有通過客戶驗收,不但有近一半的尾款沒有收到,而且還影響了開發商其它項目的投標。為什么一個曾一度成功按時交付的系統,在新舊系統數據集成、上線運行的幾個月后會出現嚴重的性能問題,并暴露出系統架構設計上的缺陷,導致遲遲無法獲得客戶的信任,讓項目各方都陷于被動和尷尬?這些麻煩究竟是什么原因造成的,是 XP 不行,RUP 不行,還是敏捷過程、方法不行?有沒有可能事先避免這種嚴重的風險呢?以上所有這些有趣的問題都值得我們深入探究。 從 Johnson 的介紹來看,這是一個遺留系統改造項目,涉及的應用領域是快速消費品領域的 PRM(合作伙伴關系管理),系統主要提供訂單管理等功能。需要用 J2EE 框架開發一個新系統來取代已經運行了 2 年的 PHP 老系統,以完善、增加功能。從系統規模來看,有三十多個用例,六萬多行代碼,近二百個 JSP 頁面,當屬于中等復雜度。 項目團隊為 10 人,分別有項目經理、技術懟⒖突Ь恚˙usiness Development)、開發人員、測試人員、HTML 人員(頁面設計)等 6 種角色,其中技術經理和頁面設計師均為兼職。我看到,團隊成員清單里沒有包含客戶方面的技術(或業務)人員。 我有個疑問,不知道這個團隊里誰承擔“架構師”的任務,是項目經理、技術經理,還是開發人員(程序員)當中的某個人?技術經理是兼職的,好像不適合擔任架構師。不知道項目經理是否參與編程。 開發時間一共只有 2 個月(從 7 月 1 日到 9 月 1 日), 9 月 2 日當天就要交付,所以,可用于開發、測試的實際時間不足 2 個月。顯然,進度緊是這個項目的一個顯著特點。這讓很多人聯想到,應該采用敏捷快速開發,快速迭代應該會適用于這個項目。 約束條件: 1)由于客戶預算原因,要采用原有硬件。 了解了 PRM 項目的大致情況后,我們有一個基本問題:這個項目到底是“成功”還是“失敗”,應該如何來評判? 從以上這段話,我作出的判斷是:這個項目是比較失敗的,沒有能達到敏捷迭代開發的初衷。系統在初次交付(9 月 2 日)的 8 個月之后(次年 5 月)竟然還沒有通過驗收,這不能不說是客戶、開發商都不愿看到的糟糕局面。 當然,作者也寫到: 我覺得,這可以理解為“局部的成功”,那么,“局部的成功”能否抵消“全局的失敗或被動”?所以,“局部”的經驗和長處當然也要總結,不過看來我們更應當很好地吸取這個項目的“全局”教訓。 做事后諸葛亮是比較容易的。如果這個項目從頭讓你再做一遍,你會怎么做? Johnson 總結了這個案例的經驗:該項目成功采用了部分 XP 實踐:每日晨會、持續集成和小步發布,此外還采用了交叉審核、集成測試、回歸測試、缺陷管理等質量保證措施。 關于 TDD,PRM 團隊是這樣做的: 教訓 大家印象最深刻的應當是導致這個項目失敗的直接原因:系統在完成數據遷移、上線運行后出現的性能問題。到底出了什么問題,嚴重到什么程度,起因是什么,作者是這樣介紹的: 一幅頁面(上的數據記錄)完全顯示要 5 分鐘以上,這顯然是用戶完全不能接受的。為什么沒有采用分頁顯示措施?用戶能夠接受的響應時間范圍是多少?還有,在實際的應用中,到底最多有多少并發用戶,需要支持的并發任務、事務以及數據吞吐量的峰值是多少;為什么數據庫會死鎖,是不是邏輯設計上的錯誤等等,這些問題在開發中好像一直沒有明確。 PRM 系統遇到的麻煩可以大致地概括為“系統容量問題”,即系統無法在真實環境下滿足大用戶量、大數據量并發訪問的需要。有意思的是,系統在交付之后的前兩個月內,并未出現性能瓶頸,取得了“初步的成功”。為了下面討論方便,我們假定真實環境下客戶對 PRM 系統的最大容量要求為同時支持 5000 位用戶訪問,而在 PRM 系統的設計變更完善之前,也就是系統 9 月交付到秋季訂貨高峰期這段時間,系統最多只能滿足 2000 位用戶的同時訪問。 針對這些問題,作者總結了這樣幾處教訓: (教訓一)沒有考慮新平臺的影響,照搬舊系統的功能以及頁面設計。 我猜,PRM 團隊這么做很有可能是受到了 XP 重構思想的影響。在一個頁面上竟能顯示上千條記錄,這不能不說是打在架構設計上的一個“大問號”,為什么架構評審的時候沒有評審出來?大概 PRM 團隊全隊的注意力都放在全力保證進度上了,所以可以容忍個別的、暫時的缺陷,也難怪。是不是有這句話:世界上聰明的程序員都是愛偷懶的!只是聰明的我們需要時刻提防“聰明反被聰明誤”,什么地方可以偷懶,什么地方絕對不可以偷懶,這可是一門編程的藝術! (教訓二)對于企業應用系統,尤其是業務系統,沒有切實注意負載、性能等非功能性需求。 公平地講,合同雙方其實都表現得不太成熟:都不知道要在合同中對非功能需求、一些性能指標作出明確規定。另外我們看到,PRM 團隊所作的測試也是片面和不完備的。 (教訓三)系統框架設計只考慮面向對象和可維護性,沒有在完美的設計與高效率的代碼之間做出權衡。 (教訓四)在面向韻蟮娜砑低徹菇ㄖ,忽枢]菘饃杓埔約?DBA 的重要作用。 (教訓五)客戶或者客戶經理對于項目的參與力度不夠。 這幾個原因 Johnson 總結得非常好!教訓二是需求分析上的原因,沒有從一開始就把重要的非功能需求考慮在內;教訓一、教訓三和四都可以概括為架構設計上的失誤,教訓一主要談表示層的問題,教訓三為控制層 J2EE 框架應用的問題,教訓四談數據庫設計的問題,這三層都存在設計上的性能缺陷;教訓五涉及到客戶關系、干系人溝通、職責定位等管理方面的原因。 的確,造成 PRM 項目失敗的主要直接原因是技術原因:系統性能達不到要求源于架構設計上的缺陷, PRM 系統的頁面顯示、J2EE 框架應用、數據庫設計等方面存在著性能問題。有的讀者可能會說,這不是一目了然嗎?下次找一個經驗更豐富的架構師,或者總結經驗、加強訓練,提高開發人員的設計能力,不就得了?還有必要浪費筆墨,再討論下去嗎? 提高架構設計能力當然很重要,也很有必要,但我覺得恐怕答案并非那么簡單?陀^地講,PRM 團隊的技術能力其實并不差,至少事發后他們還是努力找到了解決方案,比如通過引入中間層的 Cache 機制、進行數據庫優化取得了較好的成效。然而問題是,一個關鍵的問題:為什么 PRM 團隊以及客戶都沒有能提早(比如在項目開始后的前 5 個月內)發現系統可能存在的性能缺陷,問題反而是在系統運行幾個月之后才暴露?前 5 個月發現與后 5 個月發現對 PRM 項目的命運而言顯然大有不同,如果問題早發現、早解決,PRM 項目不就成功了嗎? 實際上,軟件開發中的許多技術問題最終都可以歸結到管理問題上。如果我們簡單地把 PRM 失敗歸咎于開發者的設計經驗不足,就有可能忽略了問題的本質和根源,無法消除 PRM 團隊除了架構設計以外的缺陷,也就很難保證下次不再重犯同樣或同類的錯誤。所以,在 Johnson 概括總結的基礎上,我想 PRM 項目失敗還可能有其他幾方面的甚至更深層次的管理原因,這些原因主要涉及到軟件工藝流程和管理方面,包括風險管理、需求評審、過程成熟度等等。 1)沒有實施有效的風險管理,做到真正的風險驅動的迭代式開發,盡早排除架構(性能上)的風險 總體上我認為,沒有做到真正的風險管理與控制是導致此項目失敗的最重要原因。我一直持有這樣一個觀點:風險管理是軟件項目管理的第一管理(要點)。 敏捷開發的基礎是首先做到迭代式(iterative)開發。迭代不僅僅意味把整個系統開發任務逐一分解、按階段分步驟來實現, 如果迭代的含義僅僅停留在這個層面上,那么提出用迭代演進式過程取代瀑布型開發模型也就毫無意義, 因為我們做任何工作本來就是要一周一周、一天一天、一塊一塊地來完成——不管瀑布型還是演進式,皆如此。迭代真正的主要目的其實是為了通過加速客戶反饋顯著地消除開發風險,這就要求每次迭代結束必須有一個可運行、可演示的系統。這時的系統可能功能上還不完整,僅僅是一個骨架,但它總是系統開發中最難、最重要也就是風險最大的部分。 風險驅動的迭代是 RUP 的核心特征之一,XP 對此強調的不夠,在早期的 XP 項目中主要是客戶驅動的。所以,真正的迭代式開發能夠在項目早期就允許客戶對可運行的系統進行驗證,從而使項目的風險減到最小。開發工作也應該根據風險的大小來安排,通過迭代及時調整優先級,風險越大的任務越應該及早設計、實現、測試和反饋。 我們知道,RUP 從風險驅動出發把一個軟件項目分為四個階段:起始階段、細化階段、構造階段和移交階段,這四個階段分別對應著項目的四個里程碑。起始階段主要消除項目的業務風險,細化階段應該盡力消除項目的主要技術風險 —— 架構風險(同時包括功能和非功能兩方面)。很遺憾,PRM 項目是在到了項目最后一個階段:移交階段 —— 在系統運行了幾個月、數據遷移完成之后才發現架構設計上存在著嚴重的性能缺陷需要修補,而重要的是,在項目之初的合同上其實就已經對數據遷移、上線運行的要求作出了規定?梢娺@個事后暴露的最大架構級風險 —— 系統性能否滿足用戶的真實需要 —— 直到臨近項目結束也一致未能被消除,也可以這樣認為,實際上 PRM 項目的“細化階段”并未真正完成,建立穩定、可靠的系統架構的里程碑目標也從未達到。 在項目幾近成功、圓滿結束的時候,突然爆炸一顆碩大的“地雷”(嚴重的系統缺陷或問題),導致項目進度拖延甚至失控,干系人失和,資金拖欠,這差不多就是軟件開發中最糟糕的一種情況,在各種經典的軟件工程教材中都能看到大量此種案例。不幸的是,歷史再一次地在已經(部分)采用了敏捷 XP、RUP 實踐的 PRM 項目上重演了。那么,我們有沒有可能事先防范 PRM 項目這顆延遲爆炸的“地雷”呢? 從當年 7 月至次年 5 月 PRM 項目已經花了 10 個月的時間,卻仍未能通過客戶驗收。前期用了 2 個月完成功能開發,2 個月部署和試運行,從第 5 個月完成實際數據導入、開始正式運行起,出現嚴重的性能問題,隨后的 6 個月基本上都用在了系統的性能優化和改進上?傮w上有點手忙腳亂、進度失控的感覺,初步估計 PRM 項目的進度至少延誤了 100% 以上。 軟件工程不相信眼淚。如果 PRM 團隊和客戶從一開始就意識到了系統潛在的性能問題,明確了對系統容量的要求;如果 PRM 系統的架構師擁有足夠的設計經驗,系統表示層、控制層和數據資源層在上線之前就已經得到優化,提供了足夠的性能;如果架構設計評審產生了真正的效用;如果 PRM 團隊做到了完備的系統測試;如果時間能夠倒流 ... 所有這些“如果”當中,只要有一條靈驗,那么那顆可惡的“地雷”不就不復存在了?所以,到底 PRM 項目原本可不可以做得更成功?我想,這是有可能的,我的理由來自逆向思維:如果 PRM 團隊能夠把這個項目再重頭做一遍,把這里吸取到的教訓和學到的軟件工程“新”知識都用上,在 5 個月內提供滿足客戶實際要求的系統應該不再是件很困難的事了吧,至少 PRM 團隊下次再遇到類似的項目他們成功的幾率肯定會大許多。 可見規避風險,成熟的軟件工程可以設置幾道防線,采取許多措施。如果 PRM 項目按照 RUP 風險驅動的迭代方式來做,其實從項目一開始我們就應該對需求、架構進行更為細致、全面的分析,既包括功能,也包括非功能,還可以通過多次迭代反饋來確認分析的結果。如果不知道有哪些風險,又如何來防范?所以,關鍵是要建立一張隨著迭代演進不斷被動態更新維護的十大(或五大)風險清單( RUP 工件叫 Risk List),制定出防范其中所有主要風險的預案。 就 PRM 項目而言,功能開發估計不是一個重大風險,因為有舊的 PHP 系統甚至還有源代碼、現成的算法可以參考,而客戶為什么要啟動二期開發,功能目標也是相當明確的。另一方面,J2EE 平臺上的企業應用開發有哪些風險?行業經驗提示我們 J2EE 應用架構設計得不好可能會存在性能問題。故看來,我們應該把注意力更多放到系統的非功能風險上(性能、可靠性、可維護性等等):客戶應用訪問的最大并發用戶數到底是多少,而我們交付到客戶手里的系統最大容量又是多少?怎樣才能保證系統的性能?如果上線后性能達不到,不能滿足客戶要求怎么辦? 明確了項目所面臨的重大風險,比方系統的性能問題,我們就可以根據需求和設計方案制定出完善的、有針對性的測試計劃,包括明確在客戶可接受的響應時間要求下,系統最大能夠支持多少個用戶的并發訪問(具體可細分為增、刪、改、查等多個操作類型)。明確了項目的風險、需求還不行,作為風險預案的落實,我們還應該進行系統性能、可靠性等方面的設計,并真正(通過編碼)做出一個符合要求的架構(框架)基礎,通過迭代開發、測試和評審對此進行驗證。 在開發階段,系統還未部署,我們無法獲得真實的用戶和使用環境怎么辦?模擬測試。 我想,如果嚴格按照 RUP 風險驅動的迭代演進式開發進行管理,在半年多的時間里應該還是有機會盡早發現這個問題的。原文提到“該項目在系統維護期間,對代碼進行走查,修改了很多對于性能有影響的語句”,需要提醒的是,這種方式可以消除局部的缺陷,但卻很難發現全局性的架構問題。對于軟件架構,“頭痛醫頭,腳痛醫腳”的做法往往是行不通的。 作者寫道: PRM 項目雖然模仿了 XP 迭代周期,甚至每天都開例會(這有點像 Scrum),保證了初始版本的準時交付(在保證 PRM 前期進度方面,迭代還是有功勞的),卻仍然沒有能夠防止較大風險的發生(交付系統幾個月后才逐漸暴露出性能和架構上的質量問題),可以說這并沒有達到 XP 或 RUP 迭代開發的最終目的。在項目初期沒有把合同中已經提到的數據遷移視為一個關鍵風險是前期分析工作或者說整個項目的一大失誤。 2)需求分析失誤 如果從一開始就明確了需求 —— 系統最大要能支持 5000 名用戶的并發訪問, PRM 項目的麻煩是否就不復存在了! 由于進度很緊(看來 2 個月交付的進度計劃一開始就定得不太合理), PRM 團隊以及客戶都把注意力放在了按時完成功能上,卻忽視了關鍵的非功能需求(原文教訓 2 “在需求獲取階段沒有明確地了解系統的性能指標等非功能需求”),從而導致系統的架構設計不能勝任大量并發用戶和大量業務數據的處理,這也是導致 PRM 項目失敗的重要原因之一。正是由于需求不完備,沒有及時發現遺漏了關鍵的、隱蔽的性能需求,才直接導致了項目后期的被動。 借鑒了 RUP 用例方法的 PRM 項目為什么還會出現遺漏關鍵需求的狀況?雖然 PRM 團隊采用了用例來管理需求,在嚴格性、需求的精度上比 XP 進了一步,但缺點在于只重視了描述功能需求(用例),忽視了提取非功能需求。完善的需求工程,比如 RUP 的需求科目,非常強調捕獲非功能需求, RUP 中的完整需求工件集是由《用例文檔》和《補充規約》兩部分組成的。系統級非功能需求主要在《補充規約》中描述,包括 FURPS+(特性、易用性、可靠性、性能、可維護性)等許多方面。除了《補充規約》,我們還可以在用例的“特殊需求”、“非功能需求”、“約束”、“業務規則”等字段中明確地說明每個具體用例的非功能需求。這些要求都是顯示說明的,也就是說如果程序員認真看過、學習過這些經驗指南,應該是不大容易忘記的。項目一開始,需求定義不完整、不明確、不一致沒有關系,在敏捷迭代開發中, RUP 的需求分析不是一次性完成的,而是通過多次迭代和演進逐步清晰、完善和穩定下來的?上 PRM 項目的客戶參與度不夠,整個開發過程也似乎缺少正式、規范的需求驗證和評審過程,這些因素綜合作用才導致在系統部署上線之前的很長一段開發時間內 PRM 團隊始終沒有能及時發現遺漏的關鍵非功能需求,給項目成功造成很大的隱患。本案再次向我們強調了全面、細致需求分析的重要性。 讀者可能會問,如果 PRM 團隊當初采用 XP 的用戶故事來描述管理需求,結果會怎樣?我的估計是:可能更糟。 XP 采用非正式的、寫在索引卡片上的用戶故事來定義需求,短短只有幾句話,而且要求寫得簡單不能再簡單,大部分內容應該放在腦子里,通過程序員與客戶的口頭交流和實際的程序來細化,故而我猜測像并發用戶數、頁面一次顯示的記錄個數之類的非功能約束往往很難通過這些簡單的用戶故事句子被發現。我們知道只有通過細致認真、全面成熟的需求分析、需求工程方法,才有可能捕獲許多潛在的、隱蔽的需求。為什么 XP 對待需求可以如此隨意和輕松?這是因為,在明確的干系人責權利法則庇護下,XP 項目擁有認真負責、全身心投入、專業素質很高的現場客戶。加上,系統級認可測試也是在客戶的積極參與和主導(授權)下編寫,測試驅動下的 XP 要求測試方案、測例相當完備和詳盡,基本上可以起到取代詳盡需求、保證項目成功的作用, XP 用詳盡的測試代替詳盡的需求,這才有可能在很大程度上減少因遺漏需求導致項目失敗的風險。而且,即使項目出現了風險、遇到麻煩,對于客戶驅動下的 XP 項目,客戶至少也應該承擔起 50% 以上的責任。 事實表明 PRM 團隊既沒有做到 XP 的測試驅動、現場客戶,也沒有做到 RUP 的結構化需求分析(用例加非功能)和完備測試,所以 PRM 系統風險兌現、遭到失敗看來是偶然中的必然。 3)不成熟的開發過程 PRM 項目靈活采用了若干敏捷做法,因而在項目開發進度的控制上的確取得了局部成功 —— 項目前期進度得到了保證,按時交付了系統的功能。然而,質量、內容和速度永遠是個矛盾。由于風險管理沒有到位,PRM 項目看似滿足了合同的進度要求,卻在項目后期遇到麻煩,客戶不滿意系統的質量,由于交付了一個不符合客戶性能要求的系統架構,項目完成的總體進度實質上是遠遠拖后了。 從以上的討論我們發現,把本案的失敗歸結為敏捷、XP 或 RUP 的失敗其實是不公平的。 PRM 團隊采用的是 XP 過程嗎?不是。理由: 現場客戶、測試驅動、結對編程、頻繁重構是 XP 的幾個核心特點,在這幾個方面 PRM 團隊沒有做到或者雖然嘗試了卻沒有達到 XP 的要求。PRM 團隊做到了每日晨會、持續集成、小步發布和部分的自動測試,這些實踐其實是大部分敏捷迭代方法所共有的特點,它們很重要也很基礎,但僅僅做到這些,離真正的 XP 其實還很遠。 PRM 團隊采用的是 RUP 過程嗎?顯然也不是,盡管他們做了并不充分的用例分析和架構設計。幾條理由: PRM 團隊沒有采用風險驅動的迭代式開發,自始自終進行有效的項目風險管理; PRM 團隊沒有實現通過細化階段獲得穩定、可靠的系統架構的里程碑目標;沒有充分的證據說明 PRM 項目開展了有效的需求驗證、迭代與階段評審。 PRM 團隊做到敏捷了嗎?從實際效果來看,該項目差不多延期了 100%,這顯然不能算是一個“敏捷”的過程。 PRM 項目失敗難道是因為采用了敏捷方法嗎?不是,PRM 項目恰恰是因為沒有做到真正的敏捷才會失! PRM 團隊對 RUP、XP 的定制、裁剪和整合成功嗎?No,他們沒有領會敏捷的原則,定制出來的 PRM 過程遺漏了 RUP、XP 當中最核心的東西。我們在實施軟件過程改進的過程中,往往容易機械地理解一些軟件過程的規范和教科書,單純地學習某些具體、個別的做法,卻忘卻了軟件工程的基本原則,沒有從根源上明白 XP、RUP 為什么要這樣做。 我們發現 PRM 項目在軟件開發過程的需求、架構與測試三個核心關鍵環節都存在著明顯的缺陷。 Johnson 寫到“主要(失。┰蛟谟陧椖块_發商之前沒有大規模業務系統開發的經驗,對于非功能性需求沒有足夠的重視;同時,在測試階段,也沒有對于系統負載和性能做過測試! PRM 項目做到了單元測試、集成測試,卻缺少部署上線之前的系統級性能測試和壓力測試。與其說 PRM 團隊沒有大規模業務系統開發經驗(事實說明大家的技術技能還是很不錯的,出現的一些問題后來也逐步得到了糾正),還不如說 PRM 項目的開發過程不盡完善,缺乏對實施規范軟件工程(需求、架構、測試、評審)必要性和重要性的強烈意識。難道是被個別敏捷極限過程的不當宣傳迷惑了? 如果 PRM 團隊事后總結自己的項目過程教訓,相信他們會贊成完善項目的開發過程和制度規范將在很大程度上可以避免類似情況的再度發生。如果通過迭代交付,使系統盡早進行數據遷移、容量負載方面的試驗(既然合同上已經明確提到了這個需求),并通過嚴格的需求和架構設計評審,解決頁面顯示和并發用戶訪問的性能問題就將更加從容。 4)架構與重構 待續 ... 曾經有朋友告訴我這樣一個真實的故事。在進度高壓下有一位架構師成功開發了 1 個 J2EE 應用系統,正當他為此興高采烈時,客戶卻告訴他原來的性能要求需要修改 —— 平均響應時間應該從 15ms 減少為 5ms(注:虛構數據)。這時他才發現原來自己辛辛苦苦花了 3 個月時間設計出來的架構對于這樣一個性能指標的調整完全不能適用,需要推倒從來,此時他的心情是何等沮喪可想而知。 PRM 項目的性能出現問題是在系統交付幾個月之后,如果事先沒有根據合同(客戶契約),經過充分的架構設計和評審,誰知道以后還會發生什么情況,誰又能保證今后不會出現類似的或新的問題? 5)溝通的問題 —— 對客戶的啟發、引導、反饋和協作不夠 原文寫到: 客戶代表,以及作為“現場客戶”替代者的開發商客戶經理沒有依據迭代計劃和迭代評審及時提供反饋,這也是導致 PRM 項目失敗的重要原因?蓡栴}是:PRM 項目有沒有可能做到真正的“現場客戶”?我看可能性不大,F階段,國內項目完全實施 XP 現場客戶的實踐難度較大,因而我們不能指望通過現場客戶來提高項目的成功率,而實際上十多年來國內外很多成功的項目也沒有或者沒有必要做到現場客戶。當然 XP 的反饋環也不一定要全部通過現場客戶來實現,它只是達到明確需求、強化反饋、落實客戶驅動責任的一種形式,在不同條件下可以有不同的變通處理方式。對于 PRM 項目而言,我以為在迭代周期中引入由客戶參加的正式架構評審(RUP 推薦的實踐),使關鍵性能問題能夠被盡早發現或預見到,可能比現場客戶起到的作用更大。 人們常常愛說“客戶就是上帝”,從事軟件工程千萬不能抱有這樣的觀念,這其實是一種欺騙 —— 因為“上帝”顯然是無所不能、無所不知的,而客戶絕不是萬能的,他/她們經常會犯錯誤,很多技術內幕細節客戶不了解、不知情,也不可能比開發商更懂軟件。所以,我們一方面不能等著客戶來主動告訴我們應該如何去做,另一方面也不能對客戶百依百順,客戶意見照單全收,不然結果只能是自吞苦果。成熟的軟件工程更應提倡“客戶是朋友、合作伙伴”的正確觀念。只有通過規范正確的軟件工程過程和項目管理措施,在客戶和開發商之間建立起良好、透明的溝通橋梁和協作機制,雙方將心比心,互相理解、引導和啟發,才能增加共贏取勝的機會。 這里,我還想起了 D. Dikel 在《軟件架構:組織原則與模式》中總結的架構組織反模式。不幸的是,本案的情況好象正好符合書中的“電話鈴未響”和“遺漏的部分”兩個反模式 —— 由于“電話鈴一直未響”,在開發過程中客戶溝通不暢(很可能是因為客戶放心地認為開發商及其客戶經理非常了解業務,所以不愿親自操心),項目組也就沒有預見到在數據遷移后的大用戶量情況下可能出現的問題并提前采取措施;項目組知道了明確的用戶需求(合同規定的大致內容),卻遺漏了為了向客戶提供有價值的東西(合同之外隱蔽的需求)所應該做的事情(調整架構,數據庫優化,確保運行等等);結果造成系統雖然及時交付了功能,卻由于缺少一些客戶必需的關鍵非功能特性(保證大數據量處理和客戶端顯示的良好性能)無法被客戶所接受,用戶得到的是不完整的解決方案,它帶來的負面影響足以超過任何已完成的新功能的價值(不滿意的失去信任感的客戶不愿支付尾款);叵肫饋,PRM 系統遺漏的這些細節卻似乎又都是顯而意見的。如何避免重蹈覆轍?書中建議,需求調研的“覆蓋面很重要”, “規范的東西可以確保沒有東西被遺漏”,采取措施讓客戶參與協作,了解你的受益人、架構評審和客戶互動等模式也有助于預防此類問題的發生 …… 6)根源之一:客戶合同契約的壓力、無奈與缺陷 我們知道,作為使用者代表的客戶方高層領導及 IT 部門負責人,負責銷售和協商簽訂合同的開發商客戶經理/產品經理,以及開發商內部負責系統實現的項目經理/架構師,這幾方重要的干系人之間能否對合同的范圍和技術可行性進行充分的溝通,達成清醒的共識,對項目的最終成敗起到至關重要的影響。 從原文分析看,這份客戶合同明顯存在著缺陷。它對需求的理解是片面的(“主要描述的是系統功能性的需求,而沒有非功能性需求的規定”),對進度計劃也過于樂觀。事后看,即使用去了 10 個月,開發商也付出了不懈的努力,客戶仍不愿意驗收和支付尾款,顯然項目的實際進度已經遠遠超出了當初的估計(延誤了將近 100%)。既然如此,何必當初? 我看一開始,PRM 團隊其實并沒有必要心急火燎地趕在 2 個月內交付系統,當然,如果開發商狠下決心一定要拿下這單,即便沒有條件也要創造條件,抱著“人定勝天”的豪情壯志甩開干,也是可以理解的,關鍵就看老板如何權衡風險與收益了?墒聦嵱秩绾文?我覺得如果事先能多做些客戶的工作(早期客戶一般都對開發商充滿著期盼和信任),爭取把合同進度和條款定得寬松一些,把數據遷移和系統驗收/β 測試以及適當的返工時間和必要的補償措施也考慮在內,給雙方留有一定的余地,把基礎打好、做得更扎實一些,結果就可能要樂觀得多,雙贏的把握也更大。 當然,對此我們可能有些一廂情愿 —— 明白、明白,國內的軟件開發項目進度往往是超奇的緊,所以整個行業不是一直都在呼喚著天降“奇才”和“天才”嗎?為了應對激烈競爭,贏得商機,開發商往往對項目的前景作出過于樂觀的估計,而客戶也往往對軟件開發的復雜性估計不足,甚至憑借買方市場優勢對開發商要求過于苛刻,這些因素迫使開發商哪怕項目不行也要縮短工期、壓低報價硬著頭皮上,違背軟件工程客觀科學規律的結果必然是兩敗俱傷?陀^地講,在制造“神奇”的軟件進度這方面,客戶的責任,或者說客戶的領導以及領導的領導的責任,可能更大些。 然而,這不意味著在此種“極限”情況下,規范的軟件工程就無能為力了,我們也沒有必要做出正確的項目估算和計劃。如果開發商有了充足的理由和數據支持,至少可以據理力爭,在客戶知情的情況下有意而為之、防范于未然,總比最后因項目超時超支的地雷意外爆炸而驚慌失措、亡羊補牢要好得多。 7)OOAD、設計模式與數據庫設計的關系(對 Johnson 教訓三、四的補充) 面向對象的軟件分析設計技術(OOAD)與數據庫設計、優化技術并行不悖,兩種技術解決的是不同領域的問題,在軟件開發項目的管理中應該并重。從系統工程的角度看,無論應用軟件部分的開發采用的是傳統結構化還是新型結構化(OO)技術,數據庫和 DBA 的效能作為一個相對獨立的子系統,對于企業應用這個大系統而言始終都是關鍵因素,對系統的整體性能和質量均有著重要而直接的影響。 有些人可能認為 RUP、XP 等過程方法對數據庫介紹的篇幅不多,數據庫技術對于軟件開發方法論好像就不重要了,這完全是一種誤解。同時,面向對象方法同樣可以設計出高性能、高可靠的應用軟件,兩者并不矛盾。當然,我們也不能過度使用設計模式,應該避免在錯誤的場合運用模式(這恰好是一種反模式)。 8)工具的局限性 我們看到 PRM 項目采用了不少先進的 CASE 工具(例如 Rational ClearCase、ClearQuest、Robot)進行代碼、缺陷和測試等管理,應用水平在國內當屬領先。自動化軟件工程工具對于提高效率、保證進度必不可少,然而工具的作用是有限的,工具被動地為人服務,它們不能替代正確、完善的軟件項目管理和開發工藝流程。本案恰好說明軟件開發團隊即使使用再好的工具(“銀彈”?),也無法彌補、挽救其在做事方式、開發方法和技能上存在的缺陷,歸根結蒂還是人的因素。 PRM 案例非常典型,給我們的教訓也是非常深刻的。我想它遇到麻煩的一些主要原因在于風險管理缺位,前期需求分析、架構設計不足,加上系統測試不完備,迭代式開發也沒有起到應有的積極反饋效果,結果造成雖然 PRM 系統被按時交付使用,但是一些關鍵性能需求(開發中的系統到底能支持多少并發用戶)被進度“順利完成”的假象掩蓋了,導致系統上線運行后才暴露出性能和架構設計上的嚴重缺陷,既導致客戶不滿,也給開發商造成了很大被動。這正是我們通常不愿看到的軟件開發最糟糕的一個結果,可謂一個雙輸的經典案例。通過討論,我們發現采用成熟的軟件工程/過程實踐,設置幾道風險防線,原本可以顯著地降低本案失敗的幾率。 為什么一個采用 XP、RUP、敏捷實踐的項目也會失敗呢?軟件過程的沙盤推演是一回事,軟件過程在實際項目中的執行則是另外一回事,后者充滿了各種荊棘坎坷和難以預料的風險。本案還反映了在 XP、UP 和敏捷過程應用中存在的一些典型陷阱。我們不能因為 XP 強調敏捷、極限,就天真地省略成熟軟件過程的一些關鍵環節和必要步驟,敏捷并不等于極限,這方面適應面更廣的 UP 框架能給我們較大的幫助。我們不能只學習 XP、UP、敏捷過程的表面知識,應該注重掌握軟件工程的基本原則和軟件過程的核心設計思想。在未理解 XP、RUP 和敏捷過程精神實質的情況下,僅憑一腔熱情而不顧具體適用條件,盲目地裁減、定制、修改自己團隊的軟件過程有的時候是相當危險的,本案恰好沒能避免此種風險的發生。所以,把本案的失敗歸罪于 XP、RUP 或敏捷過程等方法本身的缺陷顯然是缺乏依據的,本案可以歸結為敏捷、統一過程實施的失敗。 本案還提醒我們不能把項目的成功完全寄希望于事后重構(除非你“藝高人膽大”),針對架構打補丁實在是很不劃算、很不聰明的做法,架構級的重構可能會讓組織和個人冒很大的風險(一旦架構瓦解)。 PRM 項目說明等到架構級風險發生了,再去重構、彌補,將要付出怎樣的代價。相信 PRM 團隊與越來越多的讀者會接受我的建議:寧可與客戶一道多花些精力做好前期工作,尤其要掌握相對全面的需求分析和風險管理方法,做到善于發現風險、捕捉潛在的需求問題,把握好軟件設計的兩面 —— 前構與重構的平衡。
本案所反映的問題和現象在我國的軟件開發項目中是非常典型的。程序員高手和篤信編程技巧大于一切的觀察家們會指著 PRM 項目說:這明顯是開發人員的水平不夠,頁面處理太笨,數據庫設計太次 …… 要是我早就搞定了!可是,問題的根源果真是技術原因嗎?
前言
案例概況
分析與設計:由一名開發人員進行系統框架的設計,其它人員進行審核;在系統框架設計進行過程中,由于系統去除訂單處理以外的其它部分比較獨立,因此,將其它模塊分配給開發人員,而將核心部分交與技術經理進行分析與設計。開發人員在每個迭代周期內,都會在分析與設計做完后,每 2 人一組進行審核。
目前,項目由于性能問題,仍然沒有驗收,維護時間日益增長,目前仍然有 30 萬左右的尾款沒有收到;更為嚴重的是,目前項目開發商正在投標的另一快速消費品行業著名客戶的合作伙伴與該客戶有很大的重疊,因此,對于潛在項目的招標造成一定的影響。
另外一方面,項目交付是在合同規定日期之前完成,而且通過了所有的功能測試。從一定意義上的講,項目的開發是取得了一定的成功的。
經驗
對于系統框架的核心類設計過程中,項目小組采取了 TDD 的方式進行開發。在后續的系統開發中,每個開發人員在進行開發前,首先要完成一個功能測試(Function Test)列表,將要完成的 Use Case 中的主要業務邏輯以及關聯邏輯都要羅列出來,在提交測試人員進行集成測試之前,開發人員需要保證完成 Function List 中的所有選項。
項目在交付以后,最初的兩個訂貨季節沒有出現功能與性能上的問題。但是,由于合同中有數據遷移的條款,在項目交付 2 月后, 項目開發商將舊應用系統中的數據導入新系統以后,在下一個大的訂貨季節中,持續的出現性能上的問題。在代碼修改和硬件環境提高以后,系統性能目前獲得了一定的改善。
在項目交付以后,由于舊系統數據遷移后,數據量有了很大的增長,同時,在秋季的定購高峰中,有大量的并發用戶訪問,出現了下列問題: 數據庫死鎖;大量數據計算與顯示頁面速度很慢,頁面要經過 5~10 分鐘才能夠完全顯示; 上述兩種情況在少量負載的單元測試和集成測試中是不可能出現的。
舊系統中,對于訂單信息以及產品信息的展示,不管是多是少(系統頁面最多顯示上千條記錄),都是在一個頁面中顯示。這對于沒有明顯的層次結構,直接在 Script 中調用數據庫記錄的 PHP 來說,性能還是可以接受的。但是,新系統的設計中客戶提出考慮系統用戶習慣一致性的問題,就照搬了舊系統的頁面設計;同時,在架構設計上,對于這種頁面顯示大量數據的情況,也沒有給予充分的考慮,為后來的性能問題,埋下了伏筆。
項目合同中主要描述的是系統功能性的需求,而沒有非功能性需求的規定;同時,在需求獲取解決,也沒有明確的了解系統的性能指標等非功能性需求。主要原因在于項目開發商之前沒有大規模業務系統開發的經驗,對于非功能性需求沒有足夠的重視 ... 在測試階段,也沒有對于系統負載和性能做過測試。
該項目在系統維護期間,對代碼進行走查,修改了很多對于性能有影響的語句;同時,在框架設計中,尤其是數據庫操作方法,利用 Cache 原理,從一定程度上解決了性能的問題 ... 對于這個 PRM 系統, 在數據庫的設計上并沒有經過 DBA 的審查就開始進行開發;而在性能問題出現以后,客戶增加了 512M 的內存,也沒有請求 DBA 對 Oracle 的參數做相應的調整,造成了很大的資源浪費。 在項目維護過程中,依靠 DBA 的幫助,開發商對于數據庫系統參數、索引、存儲過程和 SQL 語句 都做了一定的調整,這對于系統性能的提高起了很大的作用。
由于會議(指每日晨會)是每天進行的,PM 很容易從中獲得真實的項目情況-"掀開地毯下面的東西",從而對風險有了較好的控制。
開發過程中,客戶經理由于業務拓展的原因,并沒有在項目上分配多少時間進行審查;而客戶在交付前也沒有花費很多的時間研究系統,也沒有提交很多的反饋報告。在系統交付出現性能等問題后,客戶經理與開發人員一起對于系統需求進行審查,提出了很多有參考性的意見。如果從一開始,就強化"現場客戶"的最佳實踐,就可以很早發現問題。
總結
文章來源于領測軟件測試網 http://www.kjueaiud.com/