原帖:http://www.programmer.com.cn/8264/
文/何勉
敏捷已成为软件开发领域的潮流,但单纯为迎合潮流去实施敏捷是不负责任的。开发方法和实践必须服务于业务成功,作为业务导向的敏捷实施成功的前提,首先必须问的问题是:通过敏捷实施要达成的业务目标是什么?为达成这些目标需要做到什么?如何做到?本文将从业务目标出发,分别从这三个方面展开讨论。
提高组织的响应能力
每一次软件产品的开发都是一个创造的过程,预知一切是不可能完成的任务。
首先,商业环境和市场的需求处于变化之中。Jonathan Rasmusson 在 The Agile Samurai 一书中陈述了三个关于需求的简单事实:一、不可能在项目开始的时候收集到所有的需求;二、不管你收集到什么样的需求,它一定会发生变化;三、要做的功能,一定会超过时间和金钱允许的范围。加之,商业环境的不断变化,在项目的初始阶段固定所有需求,只是一个有害无益的幻觉。
其次,技术环境以及团队对技术的认识处于变化之中。软件产品的开发是一个探索和发现的过程。项目启动时,团队不可能建立对业务领域、遗留代码、实现技术、工具等方面的完整认知,处于对项目最无知的状态。随着开发的进展,团队不断积累关于业务和技术的知识,并做出相应调整,加之技术环境的不断变化,使得在项目的初始阶段就确定所有的技术方向和实现方案,既不合理也不经济。
不确定性是软件开发的固有属性。这既是一个挑战,也是一个机会。传统的管理和技术手段不再适用,不能把握不确定性,适应用户的需求,就会被市场抛弃;同时,不确定性也意味着无限的可能性,掌控不确定性就意味着把握了赢得市场竞争的机会。敏捷软件开发正是要构建组织适应不确定性的能力。《精益和敏捷开发大型应用指南》一书的作者 Bas Vodde 曾经这样定义敏捷软件开发的完美愿景—“构建组织能够随时在没有额外成本的前提下、交付产品或改变方向的能力”。在本文中,这被称作“响应能力”,并把它定义为:
响应能力—软件开发组织在尽量小的额外开销的前提下,通过随时交付,或改变方向来准确、即时地响应市场变化的能力。
“响应”是组织对外部(市场和用户)所做出的反应,具体包括:交付产品,当市场需要时向客户交付已完成的工作;改变方向,当市场需要,或组织策略发生变化时,做出针对性的调整。
“响应能力”指做出这种反应的能力,具体指标有:即时,在用户需要时随时做出反应;准确,应对市场的真实需求;低额外开销,不应该产生过多正常开发之外的成本,如返工和额外测试或牺牲产品质量等。
在网络社会,话语权越来越多地为用户所掌握,不确定性正在从事物背后的规律,变成被市场供需双方普遍接受的事实。面对不确定性,拒绝和避免它不再是一个选项,唯有打造适应它的响应能力,对于软件开发这类创造活动尤其如此。从业务角度来看,实施敏捷软件开发的首要目标正是要打造组织的响应能力。
改善协作
想象一个软件开发组织,它和市场紧密连接,可以随时交付完成的工作或调整方向,对市场做出准确的反应,这样的组织必然会在市场竞争中占据优势。响应能力是所有软件开发组织所期望具备的,但真正能做到的却很少。究竟是什么影响了组织的响应能力。从内部看,主要是堆积的“在制品”,和技术及学习“债务”。从外部看主要是和市场的连接程度,也即研发团队和业务人员、用户的协作。以下将分别分析这三个方面对组织响应能力的影响。
“在制品”是响应外部变化的负担
所谓“在制品”(Work In Progress,WIP),是指已经开始但未完成(可以向最终用户交付)的工作。主要包括:未实施的决策、未实现的设计、未集成的编码和未验证的系统。
图 1 是一个典型的瀑布开发模型,需求阶段产出系统的全部需求规格说明;分析设计阶段产出整个系统的架构和详细设计;测试验证前完成所有的代码。这些都属于不可交付的“在制品”,在整个开发过程中,都无法交付软件以响应客户的需求。另外,在项目开发过程中,任何需求的变更,都意味着对“在制品”的返工,例如在编码结束时需求发生了变化,意味着前期的需求分析、架构和详细设计,以及编码都要进行返工,这引入了需求变更的额外成本。因为这些原因,团队无法在较低额外开销的情况下,随时向客户交付产品或改变方向,其响应能力必然是受限的。
图1 瀑布模型下的在制品及其影响
如图 2 所示,理想的迭代模式下,“在制品”仅仅存在于迭代之内。团队在迭代开始时,计划一部分需求,迭代结束时产生可交付的软件,清空所有的“在制品”。这样的模式为提升组织的响应能力提供了可能,1)迭代结束时软件是可交付的,可以通过交付它们来响应市场的需要。2)每个迭代开始前,都可以对产品的功能需求和规划做出调整,响应市场、用户及相关各方的反馈。而且因为迭代结束时“在制品”的数量为零,不存在对“在制品”的返工成本。
图2 理想的迭代开发模型
即使是迭代开发,大部分团队并不能做到迭代产出物的直接可交付。如图 3 所示,在一个典型的迭代开发中,每个迭代产生可运行的软件。但可运行不等于可交付,由于技术手段等的限制,迭代结束时会有遗留未完成工作。随着迭代的进展,未完成工作不断累积,形成迭代开发模式下的“在制品”,它们损害了组织响应能力。首先,可运行的软件加上未完成的工作,才构成可交付的软件。也就是交付软件前必须完成这些工作,组织无法随时交付以响应客户的需求。其次,这些“在制品”也意味着风险,未完成工作的不确定性,使得交付更加不可控,损害响应能力。
图3 现实迭代模型的 WIP
技术和学习“债务”(debts)加大响应的难度
“债务”是指将来某个时刻要偿还的负担。如图 4 所示,如果没有适当技术实践的支持,随着迭代的进行,既有代码的单元测试工作增加;功能回归测试工作量变大;代码质量因频繁变更而变差;系统越来越复杂,团队成员却缺乏对系统的理解。这些构成了软件开发中的“债务”,它们加大了将来系统修改、测试的难度,因而降低了系统的响应能力。
图4 技术债务和学习债务的积累
“债务”又可以分为技术“债务”(tech. Debts)和学习“债务”(learning Debts)。技术“债务”是指因系统的不良设计、实现和测试,导致系统在需要变更时,难以被理解,难以改变;改变后,难以验证代码的正确性;难以对既有功能进行回归测试。学习“债务”是指,在开发过程中,团队成员未能及时分享和掌握业务及实现相关的知识,加大系统变难度,而且更容易引入质量问题,甚至新的技术“债务”。“债务”对系统的影响,并不会立刻显现。但长期来看,它会使系统响应变化时,耗费时间更长,成本更高,引入质量问题的可能性更大,严重损害组织的响应能力。
有效的协作决定响应及时性和准确
“在制品”数量、“债务”水平决定了软件开发组织的内部响应性,与之同样重要的是响应的准确性。它取决于组织与外部的连接程度,也就是团队与用户及业务人员有效协作,以及团队内部有效协作,即时获取、整合市场信息,并准确传递至开发团队,有效地落实到开发活动当中。
综上所述,只有降低“在制品”,维持低“负债”,并合理协作,才能保证准确、即时地响应,达成目标。敏捷开发的实践正是在构建这样一个系统。
实施敏捷
从一定程度上说,敏捷软件开发就是通过系统的实践,减少“在制品”数量,降低“债务”水平和改善协作来提高组织的响应能力。表 1 列举常见的敏捷实践,并分析了它们对于以上三个方面的贡献。我们将这些实践分成了三类—管理实践、团队技术实践和个人技术实践,下面将分别对这些实践加以总结。
表1 敏捷实践分
管理实践
用户故事:用户故事是端到端的,足够小需求单元。用户故事应该是具备用户价值和可交付的。它作为敏捷开发中计划的单位,为减少“在制品”提供了可能。同时,它是从用户的角度出发,以用户的语言描述需求,是用户、业务人员和开发团队沟通的起点和工具,改善了他们之间的协作。
短迭代开发,小版本发布:短迭代开发模式下,每个迭代产生可交付的软件,“在制品”始终控制在迭代内,处于较低的水平。小版本发布,即时获取用户的反馈,为调整产品的方向提供了最可靠的输入,使开发团队与用户的协作变得具体和有效。
自组织的多功能团队:多功能的团队,避免了工作在各个功能团队间的传递和等待,极大降低了“在制品”产生的可能,和维持的时间。同时,多功能团队成员间的知识共享和学习也降低了学习的“债务”。团队的自组织,使决策可以在团队层面迅速做出,减少了等待和批准的时间,避免 “在制品”的累积。
团队技术实践
持续集成:持续集成作为一个开发实践,强调小步地增加功能,并随时集成、验证,始终维持可运行的软件系统,使“在制品”数量控制在一个低水平。同时,持续集成作为一个团队纪律,保证被集成的代码能达到定义的质量标准,如通过代码规则检查,自动测试等,屏蔽特定类型的技术“债务”。
统一语言:使用领域专家的语汇(领域语言)作为开发团队、业务人员以及用户之间的沟通语言,减少沟通中产生误解的可能,提高沟通效率。代码中出现的概念也应该与领域语言保持一致,这进一步确保了实现和领域之间的一致,既改善了合作,也降低了因系统理解难度而带来的技术“债务”。
接收测试驱动开发:在开发活动前,由业务、开发人员和测试人员共同参与,用实例对需求进行细化和澄清,确保大家的一致理解。这首先是一个良好的沟通工具,使三方的合作变得有序和有效。另一方面这些实例将成为功能测试自动化的起点,降低了测试相关的技术“债务”;这些实例同时还会成为理解系统的依据,降低了学习“债务”。
个人技术实践
整洁代码:代码仅实现功能是不够的,整洁的代码清晰的表达意图,易于理解和改变。整洁的代码降低了技术“债务”。
设计原则和实践:不管是 Robert.C.Martin 的S.O.L.I.D.原则,还是其它一些耳熟能详的设计原则,如消除重复、分离关注点和向稳定依赖等,其目的都是要产生一个高内聚、低耦合的系统,使系统易于变更和维护,减少技术“债务”。
持续重构:随着变更的引入,即使是好代码也有变坏的倾向,产生技术“债务”。持续重构正是要消除这些技术“债务”,维持代码的整洁,并使之符合基本的设计原则。持续重构应该是团队和个人的工程习惯,而非一次性的行为。
简单设计:过度设计带来的复杂性,本身就会成为未来变更的负担(债务)。另一方面,超越当下功能要求的设计无法得到充分的功能验证,会成为“在制品”。简单设计在实现功能、消除重复,以及清晰表达的前提下,力求用最少的元素实现系统。
自动单元测试, 测试驱动开发:单元测试是系统健壮性的基石,设计良好的自动化单元测试是系统最有效的防护网,它确保代码发生变更时,原有意图不被破坏。也只有有了单元测试的保护,重构才可能持续安全地进行。测试驱动开发则是产生良好设计的手段,同时它往往能生成最优和最及时的单元测试用例。
结对编程:结对编程有机综合了两个程序员的思维能力,更容易产生设计和实现良好的代码。同时,通过持续的代码检查,减少错误引入,并在开发人员之间有效传递了知识,降低了技术和学习“债务”。
以上,我们从响应能力出发,梳理了敏捷软件开发中的主要实践,这些实践并非为敏捷软件开发所专享。其实施,也非一蹴而就,需要以团队整体技术和管理水平的提高作为支撑。否则反而可能会带来新的“在制品”和“债务”,特别是“债务”。比如,引入单元测试自动化是为了减少技术“债务”,但如果单元测试自动化本身设计不良,测试用例过分依赖于实现细节。这样,对实现细节的改变,会影响到测试用例的正确运行,反而使单元测试成为负担,提高变更的成本,降低响应能力。
敏捷的实践之间是相互关联的,成为一个有机系统,才能发挥效益。管理实践之间是相互支持的。例如,离开多功能的团队,开发任务就必须在多个功能团队之间传递,无法做到短迭代开发。技术实践之间是相互支持的。例如,没有单元测试自动化的保障,持续重构的风险将激增,在操作上变得不现实;有了持续重构保障,维持一个最简单的系统设计,需要时再通过重构,使设计适应新的变化要求,简单设计才更可能得以实施。技术实践和管理实践也是相互支持的。例如,短迭代开发中没有技术实践支持,就会不断积累技术“债务”,产品质量和开发效率不断下降;而,短迭代内全生命周期的反馈,也为各类技术实践的部署提供了便利。
综上,敏捷实践的应用和团队技术水平的提高,相辅相成,在应用中不断总结提高,构建和完善实践体系,加强客户、业务人员和团队的合作,以及团队内部的合作,持续、有效地减少“在制品”,以及技术和学习“债务”,提升组织的整体技术和管理水平,只有这样才能构建可持续的响应能力。
敏捷开发的最新实践
敏捷开发的思想并非在一夜之间产生,有些实践,如迭代开发几乎和软件开发的历史一样久远。上世纪 90 年代各类系统的敏捷开发框架和方法被相继提出,2001 年敏捷宣言的发布,敏捷的概念正式形成,敏捷开发方法迅速普及。早期的敏捷实践,虽然也会涉及到业务和用户,但往往还是以开发团队为核心,重点关注团队内部实践。
随着敏捷实践的普及和完善,团队内部技术和管理实践作为组织响应能力的瓶颈被突破。近年来敏捷社区的关注重点更多地向团队前端和后端转移,一些新敏捷实践应运而生。
前端是指团队的输入,也即需求(包含用户体验)的获取、挖掘、澄清、整理和设计。得到比较多应用的新实践有 SBE(Spec by Example)和 Agile UX。严格意义 SBE 和验收测试驱动开发(ATDD)属于同一实践的不同表述,它突出强调了,业务、开发和测试在前期通过表格化的实例对需求进行充分地沟通和协作,澄清和概念及一致性验证,并确保各方对业务和需求理解的一致。Agile UX 把用户体验设计,更早和更紧密地融入敏捷软件开发过程,确保团队在迭代模式下,能持续交付具备良好用户体验的产品,同时加强与用户体验相关的响应能力。
后端是指团队的输出,也即产品的交付、运营和维护。得到比较多应用的新实践有持续交付和 DevOps。持续交付是持续集成的延伸,它的目标不仅是使软件始终处于可运行和通过适当验证的状态,它致力于确保整个开发周期中,软件都处于可交付的状态,从而降低交付风险,并缩减从概念形成到软件交付的时间周期。DevOps 则进一步拓展,加强软件开发和 IT 运营之间的沟通、合作及整合,弥补两者之间的信息鸿沟,更快地交付软件产品,提供软件服务。
总结
在激烈竞争和充满无限可能的今天,技术、应用都在飞速发展,用户对体验的要求也前所未有的提高,响应变化的能力成为组织的核心生存能力。就这一点而言,敏捷对于软件开发组织是一个必然的选择,而非一个可有可无选项。
通过敏捷开发实践的系统运用,减少“在制品”数量、降低技术和学习“债务”,改善团队与用户及市场的协作,构建了组织的灵活响应能力。然而,敏捷对于软件开发,也绝非一个银弹,软件开发没有因之变得更加容易,甚至因响应性要求,团队和个人都面临更大的挑战。敏捷实践的正确实施,响应能力的建设,最终还是依赖与在实践中不断总结、提高,依赖于个人和团队总体能力的提升。
原帖:http://www.programmer.com.cn/8264/