一次有益的敏捷XP失败

发表于:2007-04-28来源:作者:点击数: 标签:敏捷一次有益失败
本案所反映的问题和现象在我国的软件开发项目中是非常典型的。 程序员 高手和笃信编程技巧大于一切的观察家们会指着 PRM 项目说:这明显是开发人员的水平不够,页面处理太笨, 数据库 设计太次 …… 要是我早就搞定了!可是,问题的根源果真是技术原因吗?


本案所反映的问题和现象在我国的软件开发项目中是非常典型的。程序员高手和笃信编程技巧大于一切的观察家们会指着 PRM 项目说:这明显是开发人员的水平不够,页面处理太笨,数据库设计太次 …… 要是我早就搞定了!可是,问题的根源果真是技术原因吗?


前言

当我三年前第一次在 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 人一组进行审核。

开发时间一共只有 2 个月(从 7 月 1 日到 9 月 1 日), 9 月 2 日当天就要交付,所以,可用于开发、测试的实际时间不足 2 个月。显然,进度紧是这个项目的一个显著特点。这让很多人联想到,应该采用敏捷快速开发,快速迭代应该会适用于这个项目。

约束条件:

1)由于客户预算原因,要采用原有硬件。

了解了 PRM 项目的大致情况后,我们有一个基本问题:这个项目到底是“成功”还是“失败”,应该如何来评判?


目前,项目由于性能问题,仍然没有验收,维护时间日益增长,目前仍然有 30 万左右的尾款没有收到;更为严重的是,目前项目开发商正在投标的另一快速消费品行业著名客户的合作伙伴与该客户有很大的重叠,因此,对于潜在项目的招标造成一定的影响。

从以上这段话,我作出的判断是:这个项目是比较失败的,没有能达到敏捷迭代开发的初衷。系统在初次交付(9 月 2 日)的 8 个月之后(次年 5 月)竟然还没有通过验收,这不能不说是客户、开发商都不愿看到的糟糕局面。

当然,作者也写到:


另外一方面,项目交付是在合同规定日期之前完成,而且通过了所有的功能测试。从一定意义上的讲,项目的开发是取得了一定的成功的。

我觉得,这可以理解为“局部的成功”,那么,“局部的成功”能否抵消“全局的失败或被动”?所以,“局部”的经验和长处当然也要总结,不过看来我们更应当很好地吸取这个项目的“全局”教训。

做事后诸葛亮是比较容易的。如果这个项目从头让你再做一遍,你会怎么做?


经验

Johnson 总结了这个案例的经验:该项目成功采用了部分 XP 实践:每日晨会、持续集成和小步发布,此外还采用了交叉审核、集成测试回归测试缺陷管理质量保证措施。

关于 TDD,PRM 团队是这样做的:


对于系统框架的核心类设计过程中,项目小组采取了 TDD 的方式进行开发。在后续的系统开发中,每个开发人员在进行开发前,首先要完成一个功能测试(Function Test)列表,将要完成的 Use Case 中的主要业务逻辑以及关联逻辑都要罗列出来,在提交测试人员进行集成测试之前,开发人员需要保证完成 Function List 中的所有选项。

 

教训

大家印象最深刻的应当是导致这个项目失败的直接原因:系统在完成数据迁移、上线运行后出现的性能问题。到底出了什么问题,严重到什么程度,起因是什么,作者是这样介绍的:


项目在交付以后,最初的两个订货季节没有出现功能与性能上的问题。但是,由于合同中有数据迁移的条款,在项目交付 2 月后, 项目开发商将旧应用系统中的数据导入新系统以后,在下一个大的订货季节中,持续的出现性能上的问题。在代码修改和硬件环境提高以后,系统性能目前获得了一定的改善。


在项目交付以后,由于旧系统数据迁移后,数据量有了很大的增长,同时,在秋季的定购高峰中,有大量的并发用户访问,出现了下列问题: 数据库死锁;大量数据计算与显示页面速度很慢,页面要经过 5~10 分钟才能够完全显示; 上述两种情况在少量负载的单元测试和集成测试中是不可能出现的。

一幅页面(上的数据记录)完全显示要 5 分钟以上,这显然是用户完全不能接受的。为什么没有采用分页显示措施?用户能够接受的响应时间范围是多少?还有,在实际的应用中,到底最多有多少并发用户,需要支持的并发任务、事务以及数据吞吐量的峰值是多少;为什么数据库会死锁,是不是逻辑设计上的错误等等,这些问题在开发中好像一直没有明确。

PRM 系统遇到的麻烦可以大致地概括为“系统容量问题”,即系统无法在真实环境下满足大用户量、大数据量并发访问的需要。有意思的是,系统在交付之后的前两个月内,并未出现性能瓶颈,取得了“初步的成功”。为了下面讨论方便,我们假定真实环境下客户对 PRM 系统的最大容量要求为同时支持 5000 位用户访问,而在 PRM 系统的设计变更完善之前,也就是系统 9 月交付到秋季订货高峰期这段时间,系统最多只能满足 2000 位用户的同时访问。

针对这些问题,作者总结了这样几处教训:

(教训一)没有考虑新平台的影响,照搬旧系统的功能以及页面设计。


旧系统中,对于订单信息以及产品信息的展示,不管是多是少(系统页面最多显示上千条记录),都是在一个页面中显示。这对于没有明显的层次结构,直接在 Script 中调用数据库记录的 PHP 来说,性能还是可以接受的。但是,新系统的设计中客户提出考虑系统用户习惯一致性的问题,就照搬了旧系统的页面设计;同时,在架构设计上,对于这种页面显示大量数据的情况,也没有给予充分的考虑,为后来的性能问题,埋下了伏笔。

我猜,PRM 团队这么做很有可能是受到了 XP 重构思想的影响。在一个页面上竟能显示上千条记录,这不能不说是打在架构设计上的一个“大问号”,为什么架构评审的时候没有评审出来?大概 PRM 团队全队的注意力都放在全力保证进度上了,所以可以容忍个别的、暂时的缺陷,也难怪。是不是有这句话:世界上聪明的程序员都是爱偷懒的!只是聪明的我们需要时刻提防“聪明反被聪明误”,什么地方可以偷懒,什么地方绝对不可以偷懒,这可是一门编程的艺术!

(教训二)对于企业应用系统,尤其是业务系统,没有切实注意负载、性能等非功能性需求


项目合同中主要描述的是系统功能性的需求,而没有非功能性需求的规定;同时,在需求获取解决,也没有明确的了解系统的性能指标等非功能性需求。主要原因在于项目开发商之前没有大规模业务系统开发的经验,对于非功能性需求没有足够的重视 ... 在测试阶段,也没有对于系统负载和性能做过测试。

公平地讲,合同双方其实都表现得不太成熟:都不知道要在合同中对非功能需求、一些性能指标作出明确规定。另外我们看到,PRM 团队所作的测试也是片面和不完备的。

(教训三)系统框架设计只考虑面向对象和可维护性,没有在完美的设计与高效率的代码之间做出权衡。

(教训四)在面向韵蟮娜砑?低彻菇ㄖ校?鍪邮?菘馍杓埔约?DBA 的重要作用。


该项目在系统维护期间,对代码进行走查,修改了很多对于性能有影响的语句;同时,在框架设计中,尤其是数据库操作方法,利用 Cache 原理,从一定程度上解决了性能的问题 ... 对于这个 PRM 系统, 在数据库的设计上并没有经过 DBA 的审查就开始进行开发;而在性能问题出现以后,客户增加了 512M 的内存,也没有请求 DBA 对 Oracle 的参数做相应的调整,造成了很大的资源浪费。 在项目维护过程中,依靠 DBA 的帮助,开发商对于数据库系统参数、索引、存储过程和 SQL 语句 都做了一定的调整,这对于系统性能的提高起了很大的作用。

(教训五)客户或者客户经理对于项目的参与力度不够。

这几个原因 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 个月基本上都用在了系统的性能优化和改进上。总体上有点手忙脚乱、进度失控的感觉,初步估计 PRM 项目的进度至少延误了 100% 以上。

软件工程不相信眼泪。如果 PRM 团队和客户从一开始就意识到了系统潜在的性能问题,明确了对系统容量的要求;如果 PRM 系统的架构师拥有足够的设计经验,系统表示层、控制层和数据资源层在上线之前就已经得到优化,提供了足够的性能;如果架构设计评审产生了真正的效用;如果 PRM 团队做到了完备的系统测试;如果时间能够倒流 ... 所有这些“如果”当中,只要有一条灵验,那么那颗可恶的“地雷”不就不复存在了?所以,到底 PRM 项目原本可不可以做得更成功?我想,这是有可能的,我的理由来自逆向思维:如果 PRM 团队能够把这个项目再重头做一遍,把这里吸取到的教训和学到的软件工程“新”知识都用上,在 5 个月内提供满足客户实际要求的系统应该不再是件很困难的事了吧,至少 PRM 团队下次再遇到类似的项目他们成功的几率肯定会大许多。

可见规避风险,成熟的软件工程可以设置几道防线,采取许多措施。如果 PRM 项目按照 RUP 风险驱动的迭代方式来做,其实从项目一开始我们就应该对需求、架构进行更为细致、全面的分析,既包括功能,也包括非功能,还可以通过多次迭代反馈来确认分析的结果。如果不知道有哪些风险,又如何来防范?所以,关键是要建立一张随着迭代演进不断被动态更新维护的十大(或五大)风险清单( RUP 工件叫 Risk List),制定出防范其中所有主要风险的预案。

就 PRM 项目而言,功能开发估计不是一个重大风险,因为有旧的 PHP 系统甚至还有源代码、现成的算法可以参考,而客户为什么要启动二期开发,功能目标也是相当明确的。另一方面,J2EE 平台上的企业应用开发有哪些风险?行业经验提示我们 J2EE 应用架构设计得不好可能会存在性能问题。故看来,我们应该把注意力更多放到系统的非功能风险上(性能、可靠性、可维护性等等):客户应用访问的最大并发用户数到底是多少,而我们交付到客户手里的系统最大容量又是多少?怎样才能保证系统的性能?如果上线后性能达不到,不能满足客户要求怎么办?

明确了项目所面临的重大风险,比方系统的性能问题,我们就可以根据需求和设计方案制定出完善的、有针对性的测试计划,包括明确在客户可接受的响应时间要求下,系统最大能够支持多少个用户的并发访问(具体可细分为增、删、改、查等多个操作类型)。明确了项目的风险、需求还不行,作为风险预案的落实,我们还应该进行系统性能、可靠性等方面的设计,并真正(通过编码)做出一个符合要求的架构(框架)基础,通过迭代开发、测试和评审对此进行验证。

在开发阶段,系统还未部署,我们无法获得真实的用户和使用环境怎么办?模拟测试。

我想,如果严格按照 RUP 风险驱动的迭代演进式开发进行管理,在半年多的时间里应该还是有机会尽早发现这个问题的。原文提到“该项目在系统维护期间,对代码进行走查,修改了很多对于性能有影响的语句”,需要提醒的是,这种方式可以消除局部的缺陷,但却很难发现全局性的架构问题。对于软件架构,“头痛医头,脚痛医脚”的做法往往是行不通的。

作者写道:


由于会议(指每日晨会)是每天进行的,PM 很容易从中获得真实的项目情况-"掀开地毯下面的东西",从而对风险有了较好的控制。

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 项目有没有可能做到真正的“现场客户”?我看可能性不大。现阶段,国内项目完全实施 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 ClearCaseClearQuestRobot)进行代码、缺陷和测试等管理,应用水平在国内当属领先。自动化软件工程工具对于提高效率、保证进度必不可少,然而工具的作用是有限的,工具被动地为人服务,它们不能替代正确、完善的软件项目管理和开发工艺流程。本案恰好说明软件开发团队即使使用再好的工具(“银弹”?),也无法弥补、挽救其在做事方式、开发方法和技能上存在的缺陷,归根结蒂还是人的因素。


总结

PRM 案例非常典型,给我们的教训也是非常深刻的。我想它遇到麻烦的一些主要原因在于风险管理缺位,前期需求分析、架构设计不足,加上系统测试不完备,迭代式开发也没有起到应有的积极反馈效果,结果造成虽然 PRM 系统被按时交付使用,但是一些关键性能需求(开发中的系统到底能支持多少并发用户)被进度“顺利完成”的假象掩盖了,导致系统上线运行后才暴露出性能和架构设计上的严重缺陷,既导致客户不满,也给开发商造成了很大被动。这正是我们通常不愿看到的软件开发最糟糕的一个结果,可谓一个双输的经典案例。通过讨论,我们发现采用成熟的软件工程/过程实践,设置几道风险防线,原本可以显著地降低本案失败的几率。

为什么一个采用 XP、RUP、敏捷实践的项目也会失败呢?软件过程的沙盘推演是一回事,软件过程在实际项目中的执行则是另外一回事,后者充满了各种荆棘坎坷和难以预料的风险。本案还反映了在 XP、UP 和敏捷过程应用中存在的一些典型陷阱。我们不能因为 XP 强调敏捷、极限,就天真地省略成熟软件过程的一些关键环节和必要步骤,敏捷并不等于极限,这方面适应面更广的 UP 框架能给我们较大的帮助。我们不能只学习 XP、UP、敏捷过程的表面知识,应该注重掌握软件工程的基本原则和软件过程的核心设计思想。在未理解 XP、RUP 和敏捷过程精神实质的情况下,仅凭一腔热情而不顾具体适用条件,盲目地裁减、定制、修改自己团队的软件过程有的时候是相当危险的,本案恰好没能避免此种风险的发生。所以,把本案的失败归罪于 XP、RUP 或敏捷过程等方法本身的缺陷显然是缺乏依据的,本案可以归结为敏捷、统一过程实施的失败。

本案还提醒我们不能把项目的成功完全寄希望于事后重构(除非你“艺高人胆大”),针对架构打补丁实在是很不划算、很不聪明的做法,架构级的重构可能会让组织和个人冒很大的风险(一旦架构瓦解)。 PRM 项目说明等到架构级风险发生了,再去重构、弥补,将要付出怎样的代价。相信 PRM 团队与越来越多的读者会接受我的建议:宁可与客户一道多花些精力做好前期工作,尤其要掌握相对全面的需求分析和风险管理方法,做到善于发现风险、捕捉潜在的需求问题,把握好软件设计的两面 —— 前构与重构的平衡。

原文转自:http://www.ltesting.net