在21世纪的前十年中,软件工程方法发生了并继续发生深刻而又意义深远的变迁。软件的特征越来越被大多数人所看清,人们曾经认为,只要按照在建筑业或者制造业已经被证明是成功的管理方法,就可以使软件开发走向成功,但事实却让人气绥:进度滞后、项目失败、即使项目完成了用户还是不满意,结果很快导致商业机会的丧失,这些都严重困扰着软件工程界。为了改善这种状况,几乎所有的软件企业都在进行过程改进,但这种改进的目标是什么呢?有人说是正规的文档和流程,也有人说是有计划、秩序便于协调。但这些都不是根本目的,软件过程改进根本上是需要从经济学的观点来定义过程,追求的是高的投入回报比,这是一种崭新的价值观。
1,软件过程:现代对于传统的挑战
传统的软件过程所关注的是制订软件开发的规程,强调的是通过使用标准的、定义良好的、组织持续改进的过程。其质量控制的策略,是通过在各个关键位置上形成质量保证检查点,在该点上首先要对进度进行检查,并对提交物进行质量控制。其基本观念是:如果没有计划和规范,那项目一定是混乱和不一致的,尽管某些局部可能成功,但整体上可能永远也不会完成。传统是必须的,它是一种传承。
老式的软件过程所特有的低效率,使其在时间、预算和市场上失去机会等方面都代价高昂。所谓现代软件过程是最近十年来人们对软件开发困境进一步反思的结果,现代思维方式并不是完全反传统的,而是希望在传统之上进一步改进,使软件开发具有更好的经济性。良好的经济性就意味着能够更有效的管理有限的资源并获得最优结果。现代软件过程针对传统方法提出了一套新的价值观,这种价值观是反思了几十年来软件的特征所归纳的结果,由此演化出一套有价值的方法论。
2000年之后,人们重新审视软件工程方法,着眼点上已经不是重在建立内省的按部就班的规则和行为上,而是重在提升企业软件开发能力,并把这种能力升为一种确保项目健康和业务长期生存的手段,其过程定义更关注如何与客户交互,以及更好的交付产品或者服务。但是现实情况并不让人乐观,对大多数软件人来说,从经济学角度思考软件过程只是一种模模糊糊感觉,人们关注着编码和实现,关注着计划、进度的执行情况。关注着某个具体的项目而不是业务系统的价值。
很多行业仍然抱着那些过时的IT部门模型不放。比如,某些行业把IT部门仅仅看成一个提供内部服务的部门,只是被动的向业务部门提供他们认为能给他们带来方便的“产品”,而不是把软件看成公司整体业务价值链中不可或缺的一部分。目前把软件能力集成到业务价值链中的优势日益明显,我们需要改变公司管理与IT部门之间的那种关系。这种观念的创新改变,将会把软件开发能力提升为一种核心竞争力,而不仅仅是一项工程上的成绩。
从另一个方面来说,由于我们的教育使然,很多具有悠久历史的软件企业,仍然把上个世纪70年代建立起来的线性瀑布过程看成软件开发的正宗,他们按照这种方法,建立了一系列相互隔绝责任单一的部门(需求、设计、编码、测试),而没有意识到这种条块隔绝的模型具有天生的高风险,在面对风险的时候往往显得束手无策。
为此,各种组织和专业联盟已经提出了很多不同的方法来解决软件项目中天生的难点,比如敏捷联盟、Scrum联盟。一些专业机构,包括项目管理协会(PMI)、软件工程研究所(SEI)、IBM的Rational公司等都针对软件的特征提出了解决方案。这些新的价值观和方法论,形成了新一轮现代对于传统的挑战。尽管这些团体的观点和目标不同,但他们的价值观都有一些共同的特点,包括更加重视人在过程中的作用,更加重视可运行的结果,更加重视与客户合作获取经济上的优势,更加重视软件天生的易变性性其解决办法。从方法论来说,他们都强调了迭代式增量开发的实践,这相对于旧的瀑布式实践而言,迭代式增量开发在经济上具有无可比拟的优点。
2,瀑布方式的问题
传统的瀑布式过程是一种顺序的、基于活动的思维定势,这些活动包括:需求收集、设计、编码、测试、集成一直到验收。通过这种一级一级进行的活动,来确保开发过程的有序。经典的瀑布开发过程,如下图所示。
这个过程要求在任何设计和实现工作之前,尽可能的推敲,把需求完全定义清楚,并把它稳定下来(冻结需求),然后建立系统高层模型,包括系统和子系统的框架以及基于服务的层等。在底层设计阶段,可以精细的把业务需求转换为系统模型。然后在实现诸如编码、测试、系统集成以及部署等下游模型。瀑布式过程有8条经典法则:
1)在设计之前先要冻结需求。
2)在详细设计之前不要编码。
3)在集成之前要完成单元测试。
4)必须有详细的文档。
5)所有的交付文档都需要详细并且维护可追朔性。
6)质量评估必须是单独的团队(QA)。
7)高度精确的对所有的事情做计划。
8)审查所有的事情。
确实,这是一种非常好的工作方式,除非你是要做软件。至少,在现实中很少有用户承认需求已经被冻结了,也同意在这个被冻结的需求上签字,承诺今后如果需求变更了客户将负全部责任。如果这一条做不到,那后面所有的条条都将成为无木之本。
一个典型的瀑布式场景如下:
一群利益相关人与分析师从一组高层需求开始讨论,5个月后,分析师根据根据条客户要求把需求细化到了一定程度,他们书写出了需求规格说明书作为这个阶段的结束。需求规格说明书经过客户评审,他们认为这基本可以达到要求。管理层认为他们已经充分理解了需求,可以进入设计阶段了。
需求规格说明书被发送到了设计团队,设计师依据需求规格说明书设计产品,2个月后,完成的设计又经过了设计评审,被认为符合要求,于是设计说明书被发送到了编码团队。几个月后,代码开始陆陆续续交给测试团队,测试团队开始寻找代码中的缺陷。项目经理对这种有序的按计划进行的项目甚是满意,但是历经一年后用户看到了初步的版本,他们的反应让项目经理大吃一惊,他们认为这并不像他们当初在需求规格说明中规定的东西,于是客户至上主义使项目经理答应进行修改,问题是不仅仅是编码,一直到需求、设计、测试条件都发生了修改,找谁?于是开始发生混乱,到后来,客户又提出加入新的需求(因为一年中很多想法和环境变了),混乱开始加剧,最终项目经理失去了耐心,开始与客户争吵,这种混乱降低了效率、增加了成本、破坏了质量,开发方的损失谁来承担?客户没有按期得到有用的产品,这个损失又是谁来承担?这种混乱到底是谁造成的?到底发生了什么错?
1)对于计划本质的理解
良好的计划本质是要对问题很好的理解。在收集需求的时候,如果利益相关人对于问题、方案或者计划的理解只能到整数,那么精细刻画到小数点后5位的需求是没有意义的,因为这些精确度很大程度上是来自于猜测。前期大量猜测,后期大幅度修改,最终的结果必定是一片混乱和互相指责。前期真正重大的问题是关于架构级别的重大问题,而不是面面俱到的细节。前期花费大量时间处理细节,只会白白占用后期我们逐步植入真正问题的宝贵时间。
2)关于规格说明的理解
当花费大量精力生成详细的规格说明以后,大部分客户都会保留对于已经确定的需求进行修改的权利。因为对于大多数利益相关人来说,在看到真正能使用的东西之前,他们并不知道自己需要的东西是什么。所以“设法尽早完成需求是没有用的”。早期完成一些候选解决方案,发展成一个可以工作的演示版,频繁的演示给利益相关人来看,从而逐步丰满自己的需求,也可能是更好的方法。这也是迭代开发的核心思想。
3)对于结果的理解
如果项目经理只关注专业化的团队做了什么(文档写得怎么样?),而不关心这一切所要达到的结果,那么项目通常只会失败。关注结果的开发方式,就是让一些规模较小的开发团队,在比较短的时间内编写完一些小块的代码,并让这些代码在架构中可以运行,并且针对这些结果进行重构。而动用庞大的团队,耗用一年的时间通过若干互相隔离的阶段,来完成一个庞大的项目,最后的结果很可能就是失败。
瀑布的含义是像水一样的从上层流到下层,而不允许处于下层的水回流到上层。显然,这种模型对于今天充满了竞争、创新、新思想不断涌现的环境是不相适应的。
3,传统项目管理的问题
传统的项目管理方式,是把项目拆分成几个截然不同的阶段,比如需求、概要设计、详细设计、编码、测试等,然后为此制定一个详细的计划。在实施阶段,需要跟踪这个计划的执行,来纠正实际执行情况与原始计划的偏差。按照传统的方式,我们需要彻底检查计划的详细程度,并以此为依据来评估这个计划的质量。在这个计划的基础上,确保项目以有序可控制的方式进行。这种方法在建筑规范上体现得淋漓尽致而且效果良好,因此受过正规工程惯例训练的人们,往往也致力于把软件开发变成这种工程规范的另一个蓝本。
可惜,几十年的软件项目实践告诉我们,这种循序的、基于活动的构建方法成功率相当低。我们不能把类似建筑业的项目管理方式套用到软件来,传统的建筑业与软件行业在向客户交付产品的方式上有很大的不同。当一栋房子交付给客户以后,客户很少会提出修改结构的要求。而软件不同,即使到了项目后期,甚至项目已经交付之后,客户仍然会提出各种修改结构的要求,甚至提出新的变更的需求,让软件开发者感觉无法适应。究其根本,存在就一定有其原因,为什么会是这个情况呢?因为所有的人都知道,一栋房子建好就不能修改,但软件不同,一个勤奋的程序员一个晚上就可以把软件改的面目全非。这种建筑的不可更改性,迫使人们抑制自己的修改欲望,但软件的修改欲望会随着产品的结果越来越显现而变得愈加强烈。所有的软件开发都是一种创新,而创新的思想在前期可能只是一个模糊的愿景。这就是现实,现实迫使我们对这一切都要重新思考。
既然软件开发本身就是一种创新,在软件项目开始到时候,很多项目要素存在着很大的不确定性,通常只有在项目的过程之中,这些要素才慢慢的显现出来,这些要素包括:
问题:用户真正想要的到底是什么?
解决方案:用什么样的架构与技术更合时呢?
计划:成本(产品需要多少钱?)、时间(多少时间能完成?)、团队(用什么样的人?),阶段(项目划分成几个阶段更合理?)等,这些都带有某些猜测性质。
沟通:利益相关人员的沟通机制,用户真的想参与到项目中去吗?
而传统的项目管理很少认为这些要素是不确定的,但我们的实践告诉我们,这些在软件中这些确实是不确定的,这就需要在软件项目管理与过程改进中具有某种创新的思想。
不确定性是软件项目本质所固有的。软件的“软”字本质上表达了:需求通常会在软件完成过程中发生改变这样一个事实。换句话说,在开发团队对问题理解并不透彻的情况下,如果要求计划具有很高的精确度,其结果往往适得其反。从质量管理的角度,我们需要用一种更加现代的方法来考虑软件质量管理。这者考虑需要能够包容我们在这个行业几十年来看到的、学到的、感觉困惑的一些教训和成功模式。
2000年以后,大多数组织推荐的管理实践,都是研究如何引领(“steering”)软件项目通过这些“不确定性”所带来的壕沟,而不是坚持必须按照一个精确的长期计划进行。这也是为什么人们越来越重视通过迭代生命周期,来逐步交付创新的成果。在这种迭代周期中,领导艺术集中在始终关注风险管理、客观的监督修正以及把项目监督由要求转换成一种“引领”。不论团队是大还是小,这种“引领”的风格重在唤起团队成员的主动性和创造力,而不是仅仅满足于团队成员按部就班的完成任务。在这个过程中,也可能会有废品和返工,但我们会更早的发现那些东西能工作,那些东西不能工作,最终通过利益相关方对于需求认识的不断深化,最终提交对客户真正有用的产品,而不是仅仅完成了一份合同。
这种思想,对于那些习惯于传统工程实践的人来说(建筑、制造等),无异于异端邪说。人们思维常常出现的缺失是:当一件事出现困难的时候,总是认为是自己还做得不够好,而不去考虑这件事做法的本身是不是有问题?当我们承受需求变更之苦的时候,总认为自己做的还不够规范,于是进一步强化规范,明确条块分割,结果造成项目更加困难。正是这样的现实,才迫使人们回过头来想,需求发生变更的原因到底是什么?
软件本身与生俱来的特征,迫使人们换个角度想问题。更准确地说,研究软件过程更需要从经济学的角度来考虑,而不是仅仅从工程学的角度来考虑问题。软件项目经理每天需要做的决策,不仅仅是是否完成了规定的任务,而是取决于对价值的判断、成本的权衡、人力资源、宏观经济趋势、技术趋势等等,而不是像普通工程管理一样,仅仅让高级管理人员来考虑这些问题,在实施阶段主要考虑是不是由进度偏差,这就形成软件管理思维方式和工作方法极大的不同。
4,计划的滚动式循序渐进模式
当我们尝试用动态的观点看问题时,项目的渐进特点说明,项目的计划需要经过一个由粗而精、不断完善的过程。也就是项目的计划在频繁反馈中不断变更、滚动完善的模式。
我们应该深刻理解项目计划的两难境地,当前景不确定因素太多的时候,一种倾向是:通过猜测构造一个似乎详尽面面俱到的计划,由于这个计划相当繁复,在发生变化的时候根本无法更新这个复杂的计划,结果计划被束之高阁。另一种倾向是:初期计划编的太粗,以便留下变更的弹性,结果是计划的粗糙又为实施过程制造了大量新的不确定性因素,迫使计划反复修改,这种按下葫芦浮起瓢的局面,最后导致了一个灾难性的后果,也就是计划变成了一个毫无威信的文字游戏,谁也不认真对待了。
计划的滚动完善模式,是把计划失控的被动变更,变成了可控的主动变更,可以有效地解决上述矛盾。初步的计划可能就像一个提纲,随着项目的进展计划会根据初步的提纲逐次展开,在每个具体的迭代阶段,这个计划会更进一步细化。这种把不确定性因素逐步明朗、有粗变细、循序展开的方法,可以同时兼顾计划的刚性和弹性、前瞻性、反馈性、准确性等诸多要求。而在这一推进过程中出现的缝隙,可以用成本估算中预留的不可预见费进行填补。
问题在于这个变更点应该在什么地方?大部分的计划变更都与需求变更有关,我们在说项目管理的时候,会说计划与控制如影相随,那么计划与需求变更能不能也是如影相随呢?我认为是不可以的。需求的变更要求是随时都可能发生的,这种计划与需求变更如影相随,就是把计划变成了昆虫,把需求变成了刺激,刺激到来昆虫就一跳,这样的计划除了造成混乱以外没有其它作用。
为了减少需求变更对于计划的不利影响,防止随着需求不断变更而计划也不断随需而变,我们可以制定如下的规则:
首先:以每个里程碑为一个单元,建议一个里程碑的时间长度为项目整个生命周期的1/12左右,每个里程碑是一个完整的计划、实施与控制过程,其任务就是交付一定数量的产品。
其次:在一个里程碑时间段内需求是不能变更的(如果客户有变更要求可以说:现在离里程碑点已经不远了,先记下来,到那个时候一起讨论),所有团队成员全力以赴达到里程碑目标。
最后:在每个里程碑点上,除了传统的检查评审以外,还需要加上一项,就是与客户一起主动寻求新的需求,进而制定新的计划,化被动为主动。这样一来,需求管理与项目管理之间的集成关系就变得清晰了,如下图所示。
从顺序上来说,在需求分析与计划制定上要分三个层次来考虑问题:
首先考虑远期目标,先有目标分析,确定最终成功交付的一个稍稍模糊但是具有宏观指导意义的目标。远期计划不能没有,不然会形成一个目光短浅随需而变的开发;也不能太详细,否则会形成靠猜测形成的Glass
Case Plan(玻璃柜计划),即无法更新,也无法应对需求变更。
然后是中期目标,也就是里程碑目标,根据需求的优先级,设定每个里程碑需要交付的功能,也就是说,里程碑计划关注的是该点的结果,而不是活动。
最后才是近期目标,以目前的里程碑(很多情况下还包括下一个里程碑)为界,进行详细计划,包括里程碑期间的任务和活动,其中还需要对需求进行细化分析,这就可以根据现有的状况来应变,使计划可以落实。
从宏观上说,里程碑实施是动态的,而里程碑评审是静态的。对于变化而言,动态过程中实施变化要比静态过程实施变化难得多,所以坚持了这个规则,就有可能在动态实施与静态变化之间实现了良好平衡。
5,迭代式软件开发过程
正是由于不确定性是今天软件开发所固有的,所以迭代式开发的特点,是并不监督一个长期的计划执行,而是研究如何引领软件项目通过“不确定性”所带来的壕沟。引领一词隐含着这样一层意思:管理层主动参与,不断纠正航向,目标是生产出更好的软件。而这个对于目标的“瞄准”是由利益相关人相互合作达成的。
这种迭代过程由于其本身的不确定性,在管理上是有难度的。特别是在极其复杂的环境下,如果没有经过整体设计和构思,直接进入螺旋迭代风险将会很大。在这个过程中到底会发生什么事情无法预料。为了平衡这种演进减少管理上的难度,需要有一种对于不确定性和风险进行管理的策略。它的生命周期包括四个大的阶段,每个阶段都具有可验证的结果以及若干子阶段。这几个阶段分别为:
起始阶段:愿景、业务用例的定义以及原型化。
细化阶段:对于架构基线的综合、论证和评估。
构造阶段:增量式的对有用的功能进行开发、论证和评估。
交付阶段:可用性评估、最终部署以及发布。
这些阶段是以一个大的项目状态为标志的,是结果驱动的,而不是以顺序的“需求、设计、编码、测试、交付”的一个活动序列为基础,这种方法整体上主要是一种顺序周期。对于设计、开发和大部分测试使用了迭代方法,如下图所示。
我们来解释一下上面的图形。如前所述,在整体上这个方法共分成4个阶段:
1)起始阶段
这个阶段主要工作是调查,并通过调查研究生成一个项目的愿景。其工作方法是通过业务上下文和收集业务事件来确定解决方案的边界。对环境进行彻底的搜索,以寻找位于边界之内或在边界之上进行交互的潜在输入。这些输入构成现场调查的一部分,它们被输入到资料库中。这个阶段的结束时得到了一个工程规划,这个规划确定了接下来的迭代工程周期的结构。这个阶段的主要输出包括:业务上下文、业务事件、用例清单、资料库、工程规划。更重要的是有一些原型化的初始方案存在。
2)细化阶段
根据初始阶段的结论,进行架构概念设计,寻找架构最需要解决的问题和重点,明确解决方案。在这个基础上,以实现的方式完成初始架构设计与实现。再进行架构基线的综合、论证和评估。在细化阶段结束以后,项目规划需要估计构造阶段实际的迭代次数、每次迭代的周期,
3)构造阶段
构造阶段将采用多次循环迭代模式,理想情况下要每天执行一次,或每周执行一次(在大型项目中,执行的次数可能要超过100次)。它遵行“发现、重构、生成和测试”这样一个过程,其中伴随着反馈。在这个阶段,对问题描述、解决方案和用于支持解决方案的模式进行增量的精化。在第一次迭代结束以后,需要把细化阶段结束时的迭代次数估计重新进行修正。这个阶段的主要输出包括:迭代计划、系统上下文、用例、问题定义。还包括组件模型、操作模型、性能模型、源代码、测试规格说明、测试报告、部署包等。
4)交付阶段
此阶段首先通过完成任何剩余的测试来执行正式的系统验收。重点应该放在验收和操作测试上,因为其它部分已经完成了。这个阶段的主要输出包括:测试报告、教育和培训资料。然后把已验收的解决方案被移交给服务交付和应用程序维护人员。开始新解决方案的培训和教育。此后解决方案开始运行。这个阶段的主要输出包括:应用程序维护移交包、服务交付移交包。
在构造阶段值得我们特别注意,因为它又被分成很多子阶段(每个子阶段相当于一个迭代周期),每个阶段有两种迭代方式:首先,构造期间至少运行3次。此外,每个子过程都具有内置的迭代反馈机制。工程阶段每个迭代周期的简要描述如下:
1)发现
发现阶段是把额外的信息输入资料库中的过程。这些信息是正式的视图形式,或者来自周围环境的现有资产。这个阶段可能会创建或找到额外的模式,以增加资料库中知识深度或广度。这个阶段的主要输出包括:资料库更新、问题定义更新。
2)重构
在重构阶段中,资料库中的知识构成了一个基础,用于形成非常详细的问题描述,并逐渐形成详细的解决方案描述。“目标”状态与“目前”状态可能不同,因此需要一些模型的重构。模型、资产和软件包也可能需要重构,以便更好是适应解决方案。在解决方案定义和模式定义中,需要把来自比较早的工程周期的反馈考虑在内。这个阶段的主要输出包括:定义更新、修改后的模式、转换。
3)生成
资料库和模式共同用于生成解决方案和测试案例,测试案例可能包括用于走查的设计文档、测试规格说明、用于单元测试的测试用例。需要识别可以在本地纠正的缺陷或者遗漏的资料库信息,这些信息将会被反馈到下一个发现阶段。这个阶段的主要输出包括:本地缺陷(本地解决,以便进行下一次迭代)、视图缺陷(被输入到下一个发现迭代中)、设计文档、测试规格说明、单元测试用例。
4)测试
在每次迭代中,必须执行一些静态的或可执行的测试。通过这样的测试,可以得到一些反馈,以便纠正遗漏了信息的视图(发现),或者需要更新的视图(重构)。这个阶段的主要输出包括:测试报告、视图缺陷(被输入到下一次发现迭代中)。
这个模型在大型项目开发上,是以一个稳定的顺序为基础,把不同的迭代阶段以简单的方式组合成一个过程,从而在开发大型项目的时候,保持有效的对项目进行控制,并把项目引向成功。
这种迭代式的方法本质上是一种基于结果的方式,而不是基于活动的方式。使关注干成了什么,而不是关注干了什么。即使是在细化阶段,也不是仅仅交付一些细化的设计文档,而是要真正完成一个可以运行的架构基线,并且经过了确认和验证。这样一来,后期迭代交付的成果,就有了逐步集成的框架,而不是等到最后进行统一集成。我们的观念是:在软件领域,真正的结果是可执行的程序,其它所有的东西包括:需求文档、设计模型、测试用例、计划、过程文档、监督检查文档等等,都是次要的,这些都是达成最终结果的手段,而软件程序才是最后真正重要的。
无论我们看到的计划、模型、文档是多么完美无缺,你仅仅只能推测将来能达到的质量和进度,软件只有放到环境中运行,才可能展示的足够具体,也才可能据此判断它们全部集成在一起的时候的效果。
迭代式的软件开发所交付的结果每次都有所递增,你不仅仅可以向项目相关人员展示项目的进展,还可以优先解决那些真正的难题,这样就可以尽早的展示项目的价值和可行性,也可以更早的发现变更的需求。
这种通过可演示的结果来评估自己目前身在何处,也是我们对于项目的实际进展更加有把握。迭代开发的评估不传统方法会更频繁,客户的参与程度会更高,而不是把问题全部堆积到项目后期来解决,这样就降低了项目的风险,引领项目达到一个更加成功的目标,而不是仅仅完成了一个初期的合同。
这些新方法的纯熟应用,并以此为基础所定义的软件过程及其改进,将有可能极大的提升软件开发的经济性。
|