2.10 敏捷开发
敏捷开发是一种从1990年开始逐渐引起广泛关注的新型软件开发方法,是一种应对快速变化的需求的一种软件开发能力。相对于“非敏捷开发”,敏捷开发更强调程序员团队与业务专家之间的紧密协作、面对面的沟通(认为比书面的文档更有效)、频繁交付新的软件版本、紧凑而自我组织型的团队,能够很好地适应需求变化的代码编写和团队组织方法,也更注重软件开发中人的作用。
2001年,Kent Beck和其他16位知名软件开发者、软件工程专家及软件咨询师(称为敏捷联盟)共同签署了“敏捷软件开发宣言”。该宣言声明:
我们正在通过亲身实践以及帮助他人实践的方式来揭示更好的软件开发之路,通过这项工作,我们认识到:
个人和这些个人之间的交流胜过了开发过程和工具
可运行的软件胜过了宽泛的文档
客户合作胜过了合同谈判
对变更的良好响应胜过了按部就班地遵循计划
也就是说,虽然上述右边的各项很有价值,但我们认为左边的各项具有更大的价值。
从本质上讲,敏捷方法是为了克服传统软件工程中认识和实践的弱点而形成的。敏捷开发可以带来多方面的好处,但它并不适用于所有的项目、所有的产品、所有的人和所有的情况。它也并不完全与传统软件工程实践对立。
在现代经济生活中,通常很难甚至无法预测一个基于计算机的系统(如基于网络的应用)如何随时间推移而演化。市场环境飞快变化,最终用户需求不断变更,新的竞争威胁毫无征兆地出现。在很多情况下,项目实施之前,无法充分定义需求。因此,必须足够敏捷地去响应不断变化、无法确定的商业环境。
不确定性意味着变更,而变更意味着付出昂贵的成本,特别是在软件工程失去控制或疏于管理的情况下。而敏捷方法最具强制性的特点之一就是它能够通过软件过程来降低由变更所引起的代价,可以通过改进软件工程本身来适应敏捷带来的挑战。
2.10.1 什么是敏捷
就软件工程工作而言,敏捷已经成为当今描述现代软件过程的时髦用词。每个人都是敏捷的。敏捷团队是能够适当响应变化的灵活团队。变化就是软件开发本身,软件构建有变化、团队成员在变化、使用新技术会带来变化,各种变化都会对开发的软件产品及项目本身造成影响。必须接受“支持变化”的思想,它应当根植于软件开发的每一件事中,因为它是软件的心脏与灵魂。敏捷团队意识到软件是由团队中所有人共同开发完成的,这些人的个人技能和合作能力是项目成功的关键所在。可见,普遍存在的变化是敏捷的基本动力,软件工程师必须加快步伐以适应这种快速变化。
但是,敏捷不仅仅是有效地响应变化,它还鼓励能够使沟通(组员之间、技术和商务人员之间、软件工程师和经理之间)更便利的团队结构和协作态度。它强调可运行软件的快速交付而不十分看重中间产品(这并不总是好事情);它将客户作为开发团队的成员以消除一直普遍存在于多数软件项目中的“区分你我”的态度;它意识到在不确定的世界里计划是有局限性的,项目计划必须是可以灵活调整的。
敏捷可以应用于任何一个软件过程。但是,为了实现这一目标,非常重要的一点是:过程的设计应使项目团队适应于任务,并且使任务流水线化,在了解敏捷开发方法的流动性的前提下进行计划的制订,并精简软件开发过程,强调这样一个增量交付策略:根据具体的产品类型和运行环境,尽可能快地将切实可行的软件交付给用户。
2.10.2 敏捷及变更的成本费用
软件开发的传统方法中(有几十年的开发经验做支持),变化的成本费用随着计划的进展成非线性增长(见图2-16,实黑曲线)。这种方法在软件开发团队收集需求时(在项目的早期)相对容易适应变化。应用场景需要修改,功能表应该扩充,或者书面说明书需要编辑。这项工作的费用是最小的,所需的时间不会严重影响项目的结果。但是,如果在经过数月的开发之后将会怎么样?团队在进行确认测试的过程中(也许是在项目后期的某个活动中),一个重要的干系人要求变更一个主要的功能。这一变更需要对软件的体系结构设计进行修改,包括设计和构建3个新组件,修改另外5个组件,以及设计新的测试等。费用会迅速升级,所需的时间和费用完全是为了保证变化不会引起非预期的副作用,而这方面的开销则是可观的。
图2-16 变更成本是开发时间的一个函数
敏捷的拥护者认为,一个设计良好的敏捷过程“拉平”了变更成本曲线(见图2-16,虚线),使软件开发团队在没有超常规的时间和费用影响的情况下,在软件项目后期能够适应各种变化。大家已经学习过,敏捷过程包括增量交付。当增量交付与其他敏捷实践耦合时,例如连续单元测试及结对编程,引起变更的费用会衰减。虽然关于拉平曲线的程度的讨论仍然在进行,但是证据表明,变更费用显著降低。
2.10.3 什么是敏捷过程
任何一个敏捷过程都可以由所强调的3个关键假设来识别,这3个假设可适用于大多数软件项目。
1)提前预测哪些需求是稳定的而哪些需求变更非常困难。同样,预测项目进行中客户优先级的变更也很困难。
2)对很多软件来说,设计和构建是交错进行的。也就是说,两种活动应当顺序开展以保证通过构建实施来验证设计模型,而在通过构建验证之前很难估计应该设计到什么程度。
3)从制订计划的角度来看,分析、设计、构建和测试并不像人们所设想的那么容易预测。
给出这3个假设,同时也就提出一个重要的问题:如何建立一种能解决不可预测性的过程?正如前文所述,答案就在于过程(对于飞快变化的项目和技术条件)的可适应性。因此,敏捷过程必须具有可适应性。
但是原地踏步式的连续适应性变化收效甚微,因而,敏捷软件过程必须增量地适应。为了达到这一目的,敏捷团队需要客户的反馈(以做出正确的适应性改变),可执行原型或部分实现的可运行系统是客户反馈的最有效媒介。因此,应当使用增量式开发策略,必须在很短的时间间隔内交付软件增量(可执行原型或部分实现的可运行系统)来适应(不可预测的)变更的步伐。这种迭代方法使客户能够:周期性地评价软件增量,向软件项目组提出必要的反馈,影响能够接受反馈的过程的适应性变更。
敏捷联盟为希望达到敏捷的人们定义了12条原则。
1)最优先要做的是通过尽早、持续地交付有价值的软件来使客户满意。
2)即使在开发的后期,也欢迎需求变更。敏捷过程利用变更为客户创造竞争优势。
3)经常交付可运行软件,交付的间隔可以从几个星期到几个月,交付的时间间隔越短越好。
4)在整个项目开发期间,业务人员和开发人员必须天天都在一起工作。
5)围绕有积极性的个人构建项目。给他们提供所需的环境和支持,并且信任他们能够完成工作。
6)在团队内部,最富有效果和效率的信息传递方法是面对面交谈。
7)可运行软件是进度的首要度量标准。
8)敏捷过程提倡可持续的开发速度。责任人、开发者和用户应该能够长期保持稳定的开发速度。
9)不断地关注优秀的技能和好的设计会增强敏捷能力。
10)简单(使不必做的工作最大化的艺术)是必要的。
11)最好的架构、需求和设计出自自组织团队。
12)每隔一定时间,团队会反省如何才能更有效地工作,并相应地调整自己的行为。
并不是每一个敏捷模型都同等使用这12项原则,一些模型可以选择忽略(或至少淡化)一个或多个原则的重要性。然而,上述原则定义了一种敏捷精神,应该在每一个过程模型中都维护这种精神。
敏捷软件开发强调“人的因素”在其中的重要性。“敏捷开发关注个人的才智和技巧,根据特定人员和团队来塑造过程。”这一描述的关键点在于“构造可以满足人员及团队需求的过程模型”,而非其他可选的过程模型。
如果敏捷开发团队成员希望努力维护所使用的过程的特性,则该团队成员及团队本身必须具备以下一些特点。
1)基本能力。同在传统软件工程中一样,在敏捷开发中,“能力”一词包含了个人内在才能、特定的软件相关技能,以及对所选过程的全局知识。关于过程的技能和知识可以而且应该教给敏捷团队的每一位成员。
2)共同目标。虽然敏捷团队成员能完成不同的任务,为项目提供不同的技能,但是所有人必须瞄准同一个目标,即在承诺的时间内向客户提交可运行的软件增量。为了实现这一目标,项目组还应当做出或大或小的连续的适应性变化,以使过程更适合于团队的需要。
3)精诚合作。抛开过程而言,软件工程就是在项目组沟通中评估、分析和使用信息;产生能够帮助所有干系人了解项目组工作的信息;构建对客户具有业务价值的软件和相关数据库等信息。为了实现这些任务,项目组成员之间、项目组与所有其他干系人之间必须精诚合作。
4)决策能力。包括敏捷团队在内,任何一个好的软件项目组必须有能够掌握自身命运的自由。这意味着应当赋予项目组在技术和项目问题上的自主决策权。
5)模糊问题解决能力。软件项目经理应当认识到:敏捷项目组被迫不断面对不确定的事情,被迫不断和变更做斗争。有时,项目组不得不接受今天正在解决的问题明天根本不需解决这样的现实,然而,今后的项目将会从任何解决问题的活动(包括解决错误问题的活动)中学习到经验。
6)相互信任和尊重。敏捷团队必须成为具有凝聚力的团队,这样的团队展现出的相互信任和尊重使其形成“一个强有力的组织,确保整体实力大于各部分实力之和”。
7)自组织。自组织在敏捷开发中具有三重含义:①敏捷团队组织自身以完成工作;②团队组织最能适应当前环境的过程;③团队组织最好的进度安排以完成软件增量交付。自组织具有一些技术上的好处,但是更为重要的是它能促进合作,鼓舞士气。本质上,这也就是项目组的自我管理。
2.10.4 极限编程
为了更详尽地说明敏捷过程,下面来看看极限编程(eXtreme Programming,XP)的论述,它是敏捷软件开发使用最广泛的一个方法。极限编程相关的思想和方法最早出现于20世纪80年代后期,近年来,XP的变种—工业XP(IXP)被提了出来。IXP细化了XP,目标是在庞大的组织内部使用敏捷过程。
Beck为实施XP的全部工作定义了5个有重要意义的要素,即沟通、简明、反馈、鼓励和尊重。这5个要素中的每一个都是完成特定的XP活动、动作和任务的驱动力。
为了在软件工程师和其他干系人之间获得有效的沟通(例如,为软件建立所需的特性和功能),XP强调在用户和开发者之间进行紧密的、非正规的(口头的)合作,建立交流重要理念的有效隐喻和连续的反馈,避免以大量的文档作为交流媒介。
为了做到简明,XP限制开发者只对即时需求做设计,而不考虑长远需求。这样做的目的是为了使代码设计简单化。如果设计需要改进,那么以后能够实现重构。重构可以让软件工程师在不改变外部功能和行为的情况下改进设计的内部结构(或是源代码)。实际上,重构可以用来提高设计的效能、可读性或性能,或改进实现设计的代码。
反馈来自以下3项:已实现的软件本身、客户和其他软件团队成员。通过设计和完成一个有效的测试策略,软件(通过测试结果)给敏捷团队提供反馈信息。XP使用单元测试作为主要的测试策略。每进行一级开发,开发团队就设计一个单元测试来测试每个操作是否按照规定功能完成。当一个增量提交给客户时,经增量完成的用户故事或用例就作为用于验收测试的一个基础。软件完成输出、功能和用例行为的程度构成了一种反馈。最后,当新需求作为迭代计划的一部分而提出时,团队就把费用和进度影响的反馈信息提供给客户。
敏捷团队还应在团队成员之间,在其他干系人和团队成员之间,间接地(包括软件本身)灌输相互尊重的思想。当团队成功交付了软件增量时,他们对XP过程的尊重也会增加。
XP使用面向对象的方法作为推荐的开发范型,它包含了策划、设计、编码和测试4个框架活动的规则和实践。图2-17描述了XP过程,并指出与各框架活动相关的关键概念和任务。下面将概括XP关键的活动。
1.策划
策划活动(也称为策划比赛)开始于倾听,这是一个需求获取活动,该活动要使XP团队技术成员理解软件的商业背景,充分感受要求的输出和主要特征及主要功能。倾听产生一系列“故事”(也称为“用户故事”),描述即将建立的软件所需要的输出、特征及功能。每个故事(类似于用例)由客户书写并置于一张索引卡上,客户根据对应特征或功能的综合业务价值标明故事的权值(即优先级)。XP团队成员评估每一个故事并给出以开发周数为度量单位的成本。如果某个故事的成本超过了3个开发周,则将请客户把该故事进一步细分,重新赋予权值并计算成本。重要的是应注意到新故事可以在任何时刻书写。
图2-17 极限编程过程
客户和XP团队共同决定如何把故事分组并置于XP团队将要开发的下一个发行版本中(下一个软件增量)。一旦认可对下一个发布版本的基本承诺(所包括的故事、交付日期和其他项目事项),XP团队将以下述3种方式之一对有待开发的故事进行排序:①所有选定故事将在几周之内尽快实现;②具有最高权值的故事将移到进度表的前面并首先实现;③高风险故事将首先实现。
项目的第一个发行版本(也称为一个软件增量)交付之后,XP团队计算项目的速度。简而言之,项目速度是第一个发行版本中实现的客户故事个数。项目速度将用于:①帮助估计后续发行版本的发布日期和进度安排;②确定是否对整个开发项目中的所有故事有过分承诺。一旦发生过分承诺,则调整软件发行版本的内容或者改变最终交付日期。
XP并不强调设计的重要性。这一点不是所有人都会同意的。事实上,有时设计还是应该强调的。
在开发过程中,客户可以增加故事,改变故事的权值,分解或者去掉故事。接下来由XP团队重新考虑所有剩余的发行版本并相应地修改计划。
2.设计
XP设计严格遵循简洁原则,即使用简单而不是复杂的表述。另外,设计为故事提供不多也不少的实现原则,不鼓励额外功能性(因开发者假定以后会用到)设计。
XP鼓励使用CRC(类—责任—协作者)卡作为在面向对象环境中考虑软件的有效机制。CRC卡确定和组织与当前软件增量相关的面向对象的类。XP团队使用过程来管理设计工作。CRC卡也是作为XP过程一部分的唯一的设计工作产品。
如果在某个故事设计中遇到困难,XP推荐立即建立这部分设计的可执行原型,实现并评估设计原型,在真正地实现开始时降低风险,对可能存在设计问题的故事确认其最初的估计。XP鼓励既是构建技术又是设计优化方法的“重构”。
重构是以不改变代码外部行为而改进其内部结构的方式来修改软件系统的过程。这是一种净化代码(并修改或简化内部设计)以尽可能减少引入错误的严格方法。实质上,重构就是在编码完成之后改进代码设计。
因为XP设计实际上不使用符号并且几乎不产生工作产品,所以设计会被当做可以并且应当在构建过程中连续修改的临时的人工产品。重构的目的是控制那些由于提出“可以根本改进设计”的小设计修改而造成的(代码)改动。然而应当注意的是,重构所需的工作量随着应用软件规模的增长而急剧增长。
XP的中心观念是设计可以在编码开始前后同时进行,重构意味着设计随着系统的构建而连续进行。实际上,构建活动本身将给XP团队提供关于如何改进设计的指导。
3.编码
XP推荐在故事开发和初步设计完成之后,团队不是直接开始编码,而是开发一系列用于检测本次(软件增量)发布的包括所有故事的单元测试,一旦建立了单元测试,开发者就更能够集中精力于必须实现的内容以通过单元测试。不需要加任何额外的东西(保持简洁)。一旦编码完成,就可以立即完成单元测试,从而向开发者提供即时反馈。
XP编码活动中的关键概念(也是讨论最多的方面)之一是结对编程。XP建议两个人面对同一台计算机共同为一个故事开发代码。这一方案提供了实时解决问题(两个人总比一个人强)和实时质量保证的机制(在代码写出后及时得到复审),同时也使得开发者能集中精力于手头的问题。实施中不同成员担任的角色略有不同,例如,一名成员考虑设计特定部分的编码细节,而另一名成员确保编码遵循特定的标准(XP所要求的那些)或者确保故事相关的代码能够通过相对于故事而开发的单元测试。
当结对的两人完成其工作后,他们所开发的代码将与其他人的工作集成起来。有些情况下,这种集成作为集成团队的日常工作实施。还有一些情况下,结对者自己负责集成,这种“连续集成”策略有助于避免兼容性和接口问题,建立能及早发现错误的“冒烟测试”环境。
4.测试
正如已经指出的,在编码开始之前建立单元测试是XP方法的关键因素。所建立的单元测试应当使用一个可以自动实施的框架(因此易于执行并可重复),这种方式支持代码修改之后即时的回归测试策略。
一旦将个人的单元测试组织到一个“通用测试集”,每天都可以进行系统的集成和确认测试。这可以为XP团队提供连续的进展指示,也可在一旦发生问题的时候及早提出预警。
XP验收测试,也称为客户测试,由客户规定技术条件,并且着眼于客户可见的、可评审的、系统级的特征和功能。验收测试根据本次软件发布中所实现的用户故事而确定。
2.10.5 工业极限编程
工业极限编程(IXP)是XP的一种有机进化,它由XP的最低限要求、以客户为中心和测试驱动精神组成。IXP与原来的XP的主要差别在于其具有更大的包容性,它扩大了用户角色,升级了技术实践。IXP合并了6个新实践,这些新实践的设计是为了有助于确保在一个庞大的组织内一些重要项目中XP工作能成功地实施。
1)准备评估。在IXP项目开始执行前,组织机构应进行准备评估。评估应确定是否:①存在支持IXP的适合的开发环境;②开发团队由合适的干系人组成;③组织机构具有清晰的质量大纲并且支持连续的改进;④组织文化会支持一个敏捷团队的新的权值;⑤组成较为广泛的项目社区。
2)项目社区。经典XP建议选择适合的人员组成敏捷团队可以确保成功。也就是说,团队成员必须经过良好的训练,具有良好的适应性和技能,以及适宜的性格为自组织团队做出贡献。当在一个大型组织内将XP应用于重要的项目,团队的概念就变成社区。一个社区可能拥有一个技术专家和处于项目成功核心地位的客户们,以及其他干系人(如法律人员、质量检验员、生产或销售人员),“他们通常位于IXP计划的周边,但在项目中他们扮演着重要的角色”。在IXP内,应明确定义社区成员和他们的角色,应建立社区成员之间的交流和合作机制。
3)项目承租。IXP团队通过对项目本身进行评估来确定对于项目的合适的商业调整是否存在,以及是否可以进一步深化组织机构的全部目标和目的。承租也要检查项目环境来决定项目如何完成,如何扩展,或者如何替代现在的系统或过程。
4)测试驱动管理。一个IXP项目需要可测量的标准来评估项目的状态和迄今为止的进展情况。测试驱动管理建立一系列可测量的“目标”,然后定义一些机制来确定目标是否可以实现。
5)回顾。IXP团队在一个软件增量交付之后要实施特定的技术评审。这种评审称为回顾,它在软件增量过程中,以及/或者全部软件的发布过程中复查“问题、事件及教训”。这样做的目的是为了改善IXP过程。
6)持续学习。由于学习是持续过程改进中至关重要的组成部分,因此,应鼓励(或激励)XP团队的成员学习新的方法和技术来提高软件产品质量。
除了以上讨论的6个新实践,IXP还修改了大量已有的XP实践。故事驱动开发(SDD)主张验收测试的故事写在所有代码生成之前。领域驱动设计(DDD)是XP中“系统隐喻”概念的改进。DDD建议渐进建立域模型,“域模型可以精确表示领域专家如何考虑课题”。结对扩展了XP结对编程的概念,包括管理者和其他干系人,目的是提高那些可能不直接参与技术开发的XP团队成员间的知识共享程度。迭代可用性并不鼓励前载接口部件设计,其用意在于支持可用性设计,从而有利于软件增量交付,以及用户与在研软件的交互。
IXP对其他XP实践进行了少量的修改,并重新确定某些角色和责任,使他们担负起大型组织重要项目的责任。