不一样的敏捷开发实践
项目背景
2006年年初,一位客户联系我的公司,希望能够为其企业创建一个企业网站项目。根据客户的简单描述,这个项目本质上就是一个内容管理系统,并集成了论坛、FTP和电子邮件等功能,因此不算复杂。按照以往的经验估计,最多一个月就可以完成这个简单的项目。
需求分析
大体而言,该项目的主要功能包括新闻和文章发布、产品发布以及后台的用户管理和权限设置,还有外围的论坛、FTP和电子邮件系统。应该是一个很简单的Web应用程序,通常情况下写一个简单的概要性文档,就可以安排开发人员进行实际编码了。
但这个客户是国有企业,所以简单的概要性文档是显然不可能通过领导审核的。为此,我和客户一起,将网站所有的功能整理成了列表,并标记出各个功能之间的关联关系。功能和内在逻辑关系整理完毕后,客户还和设计师一起将所有网页的布局也画成了图表。最终,需求文档多达50页。
在整理需求文档的过程中,我发现项目并不像客户最开始描述的那样简单。因为客户所在的企业有一百多个部门、车间,所以客户要求按照部门和车间对网站的用户进行管理。同时,权限管理是层层授权的。简单来说就是上级部门可以,也只能给直接下级部门授权,而不能越级授权。获得授权的用户可以创建一个产品子类别。然后给子类别指定一个下级部门的管理员,然后再由该下级部门的管理员来创建更深层次的子类别或管理产品信息。
从表面上看,这种设计没什么问题。但在实际操作中,这种设计要求对每一个部门的相关员工都进行培训,让其掌握系统的使用,增大了项目的应用成本。同时,由于繁琐的授权模式,最终负责产品管理的人员反倒没有充分的权力使用系统。
所以我对这些不合理的地方提出了自己的看法,希望采取更灵活更实用的设计方案。不幸的是,我没能说服客户接受我的意见。毕竟这是个金额较大的项目,客户方负责人坚持己见,我也无可奈何。
虽然按照需求文档,我把项目开发时间定为两个月,但事实证明两个月的时限过于乐观。
原型系统开发和初步评审
文档准备完毕后,我安排了开发人员和设计师进行此项目。而开发人员不到20天,就拿出了一个原型系统,虽然细节上还有不少需要完善的地方,但主要功能都已经具备了。原型系统开发完毕后,我们和客户一起进行了初步评审。评审结果双方都比较满意,所以准备在余下来的时间中完善细节。
但我发现这个原型系统中权限部分实用性非常差,因此再次提出了修改意见。不过客户显然对于我这种“怀疑”他的做法很不愉快,最后用一句“这是我们行业特点决定的”来结束了讨论。虽然我早已知道决定项目成败的,“人”才是关键因素,但迫于客户的压力,我再次选择了妥协。
许多开发者认为只要原型系统通过评审,整个项目就不会遇到大问题了。但实际情况有时候非常复杂。因为原型系统通常只是几个人坐在一起简单展示或者试用一下,和实际使用该系统的环境有着巨大区别。所以许多问题是根本不可能在原型展示阶段暴露出来的。
做好后的系统却彻底失败
从需求文档准备好到实际开发工作进行还不到一个半月,整个系统就非常完善了。期间由于客户方负责人出差,客户企业的其他联系人要么没有决策权,要么说不知道此事(国企通病),所以我们只有在没有获得进一步反馈意见的情况下继续按照需求文档进行开发。不过完善后的系统倒是“很顺利”的通过了客户的检查,开始部署到服务器上进行试运行。
但就像火山一样,系统中存在的问题超过临界点就会爆发。短短一周以后,上门为客户提供培训的技术支持人员就带回来了一份详细的修改意见文档和反馈意见。而我仅仅看了这些文档几分钟,就明白这个项目将要进行重大修改,否则不可能投入实际应用。
修改意见文档的内容主要集中在权限系统上,具体而言就是权限系统的设计太复杂、太死板。首先,层层授权太过繁琐,有时候改变产品类别的名字也要找到上级管理员才行。其次,由于系统限定不能给一个管理人员分配多级产品分类的权限,所以必须每个产品分类层次都要设置不同的管理帐号。
客户企业有10多个大类,100多个小类,上千种型号的产品。但实际上根本没有那么多人愿意负责管理工作,最后就成了一个人用几个帐号,当初设想的严格权限管理形同虚设。而且由于使用太麻烦,实际的管理工作逐渐向少部分人集中,导致这些人怨声载道,开始对系统提出各种各样的负面看法。
在这种情况下,我公司和客户企业领导进行了多次会议,初步决定两条腿走路。一方面用最短的时间修改现有系统,保证客户企业新产品发布时,网站能够正式推出。另一方面重新做一套新系统来替换现有系统。
重新开始,该如何抉择?
对于软件公司来说,一个项目如果重做,损失和影响是非常大的。因为不但其他的开发计划要被打乱,而且公司投入的成本也要成倍增加。这个时候,如何降低损失就是最重要的事情了。好在和和客户经过进一步协商后,客户承担了一半的损失。而完全重做也改为只重做权限系统部分。
根据这个目标,我首先安排开发人员对系统进行修改。砍掉了权限系统(实际上就是这一块导致了整个系统的重做),并按照其他项目的成功经验,对多处功能进行了修改。修改完成后的系统虽然缺乏权限管理,但其他功能经过客户企业员工使用都反映良好。而且这样简化后的系统大部分功能都可以直接搬到重新开发的新系统中,最大程度的降低了成本。
同时,在我的强烈要求下,客户企业决定安排专人负责此项目。这样我才能保证新系统的开发不至于重蹈覆辙。
引入敏捷开发
其实我公司不是第一次尝试敏捷开发,只是这个项目由于前期做了“细致”的文档,所以没有按照惯用的快速迭代模式进行开发。但新系统在排除了“人”的障碍后,采用敏捷开发的条件已经很充分了。
User Story
我首先和客户方派来的代表一起模拟了权限系统的运作方式,最终得到了一个和最初设计功能近似,但具备充分实用性的权限系统设计方案。
模拟过程类似角色扮演游戏。我先在许多张卡片上写好各个部门及员工的名字、职位信息。然后我和客户代表一起,手持不同的卡片扮演不同的角色。然后将不同角色之间的交互过程记录下来。这个过程就是敏捷开发中倡导的“User Story”,虽然简单,但是非常有效。不但能够真正理清各个角色之间的关系,还能找出实际运用时的不足之处。
在我和客户代表的模拟过程中,开发人员则迅速创建一个符合我们演示过程的权限系统来验证权限系统的可行性。当然,如此高要求的快速开发还需要借助公司从以往项目中积累的大量可复用代码以及高水平的开发人员。
快速决策和充分沟通
由于在客户企业,一个很简单的决策可能也要层层批复。所以经过我公司的艰苦努力,客户企业最终决定由一位领导来专门负责该项目的决策。所以大部分决策可以在较短的时间内获得反馈意见。
而客户代表在整个新系统开发期间,几乎一半时间都在我公司上班。这也保证了我公司和客户之间的充分沟通,并且当面沟通也比通过电话更容易说服客户接受我的意见。
实际上,不管采用何种开发模式,充分的沟通都是保障项目成功的关键因素之一。沟通越充分、双方协作程度越高,项目就会进行得越顺利,成功的几率也更大。而在敏捷开发中,由于是通过小步前进的快速迭代来逐步逼近项目最终目标,所以沟通就更为重要。否则一次迭代完成后,却得不到及时和正确的反馈,那么项目也无法进行下去。
有限的单元测试
持续集成虽然非常有用,但是对于这个项目却不太适合。不过为了保证子系统的修改不至于影响到全局系统,我仍然编写了一些重要的单元测试。
准确来说,这几十个单元测试都不太符合“单元测试”的标准。因为每个测试实际上都要用到子系统的许多接口。但在项目时间压力下,这些测试既能很大程度上保证子系统的修改不至于对全局系统产生太大的破坏作用,又不用花太多时间去维护。所以是一个折衷的选择。
不过这里我认为这里做得很好的地方就是单元测试是由我来编写的,并不是开发人员自己编写的,所以更能够反映我和客户的意图。同时测试重点也更偏重业务领域,而不是程序行为。
版本控制系统
虽然敏捷开发没有对版本控制系统做要求,但使用版本控制系统可以很明显的提高开发效率。例如我和客户代表验证一个想法后,发现这个设想并不好,那么通过版本控制系统,开发人员几分钟就可以退回到先前的代码或者切换到其他阶段的代码。
而且版本控制系统在需求变动激烈的项目中,更是充当了一种保险机制。无意中用错误版本的代码覆盖工作拷贝的事情可以得到彻底解决。
灵活运用敏捷开发
实际上,我从来没有采用过完整的敏捷开发。因为我认为既然敏捷开发本身强调的就是应对变化,那么敏捷开发过程本身也应该是可剪裁的。像结对编程、持续集成这些,对公司本身和开发人员的要求相对来说都更高。如果要一一达标,确实不太可能。所以有选择性的实行敏捷开发,是我最常用的方式。
例如对于User Story,除非是客户充分配合,否则是难以实施的。这个时候应该在前期将工作做细致,尽可能明确大部分需求。然后以最快的速度做出原型系统后,和客户进行沟通获取反馈意见。
同时,即便采用User Story,对于较复杂的系统,也应该深入客户企业,了解客户企业员工的工作流程。因为有时候客户方负责项目的人士很可能会按照自己的想法渲染实际的工作流程或者环境,这对项目的中后期是非常不利的。
在国内,一个项目要做得顺利,项目负责人还要懂得“做人”。和客户方负责人、联系人保持良好的关系是非常非常重要的,否则他们一句话可能就会让项目的开发成本增加不少。这些东西的内容远远超过了技术领域,但却是我们不得不面对的问题。
迟来的成功
新系统花了一个月就开发完成了,因为除了权限系统是完全重做的外,其他部分都大量利用了已有系统的结构和代码。虽然在模拟权限系统时花了不少时间,但这样模拟的结果保证整个系统具有充分的可用性。