极限编程(英语:Extreme programming,缩写为XP),是一种软件工程方法学,是敏捷软件开发中应用最为广泛和最富有成效的几种方法学之一。如同其他敏捷方法学,极限编程和传统方法学的本质不同在于它更强调可适应性而不是可预测性。极限编程的支持者认为软件需求的不断变化是很自然的现象,是软件项目开发中不可避免的、也是应该欣然接受的现象;他们相信,和传统的在项目起始阶段定义好所有需求再费尽心思的控制变化的方法相比,有能力在项目周期的任何阶段去适应变化,将是更加现实更加有效的方法。
极限编程为管理人员和开发人员开出了一剂指导日常实践的良方;这个实践意味着接受并鼓励某些特别的有价值的方法。支持者相信,这些在传统的软件工程中看来是“极端的”实践,将会使开发过程比传统方法更加好的响应用户需求,因此更加敏捷,更好的构建出高质量软件。
极限编程的创始者是肯特·贝克、沃德·坎宁安和罗恩·杰弗里斯,他们在为克莱斯勒综合报酬系统(C3)的薪水册项目工作时提出了极限编程方法。肯特·贝克在1996年3月成为C3的项目负责人,开始对项目的开发方法学进行改善。他写了一本关于这个改善后的方法学的书,并且于1999年10月将之发行,这就是《极限编程解析》(2005第二版出版)。克莱斯勒在2000年2月取消了实质上并未成功的C3项目,但是这个方法学却一直流行在软件工程领域中。至今,很多软件开发项目都一直以极限编程做为他们的指导方法学。
极限编程的目标
极限编程的主要目标在于降低因需求变更而带来的成本。在传统系统开发方法中,系统需求是在项目开发的开始阶段就确定下来,并在之后的开发过程中保持不变的。这意味着项目开发进入到之后的阶段时出现的需求变更将导致开发成本急速增加,而这样的需求变更在一些发展极快的领域中是不可避免的。
极限编程通过引入基本价值、原则、方法等概念来达到降低变更成本的目的。一个应用了极限编程方法的系统开发项目在应对需求变更时将显得更为灵活。
极限编程的12个核心实践
短交付周期
极限编程和Scrum一样采用迭代的交付方式,每个迭代1-3周时间。在每个迭代结束的时候,团队交付可运行的,经过测试的功能,这些功能可以马上投入使用。
计划游戏
XP的计划过程主要针对软件开发中的两个问题:预测在交付日期前可以完成多少工作;现在和下一步该做些什么。不断的回答这两个问题,就是直接服务于如何实施及调整开发过程;与此相比,希望一开始就精确定义整个开发过程要做什么事情以及每件事情要花多少时间,则事倍功半。针对这两个问题,XP中又两个主要的相应过程:
软件发布计划(ReleasePlanning)。客户阐述需求,开发人员估算开发成本和风险。客户根据开发成本、风险和每个需求的重要性,制订一个大致的项目计划。最初的项目计划没有必要(也没有可能)非常准确,因为每个需求的开发成本、风险及其重要性都不是一成不变的。而且,这个计划会在实施过程中被不断地调整以趋精确。
周期开发计划(IterationPlanning)。开发过程中,应该有很多阶段计划(比如每三个星期一个计划)。开发人员可能在某个周期对系统进行内部的重整和优化(代码和设计),而在某个周期增加了新功能,或者会在一个周期内同时做两方面的工作。但是,经过每个开发周期,用户都应该能得到一个已经实现了一些功能的系统。而且,每经过一个周期,客户就会再提出确定下一个周期要完成的需求。在每个开发周期中,开发人员会把需求分解成一个个很小的任务,然后估计每个任务的开发成本和风险。这些估算是基于实际开发经验的,项目做得多了,估算自然更加准确和精确;在同一个项目中,每经过一个开发周期,下一次的估算都会有更过的经验、参照和依据,从而更加准确。这些简单的步骤对客户提供了丰富的、足够的信息,使之能灵活有效地调控开发进程。每过两三个星期,客户总能够实实在在地看到开发人员已经完成的需求。在XP里,没有什么“快要完成了”、“完成了90%”的模糊说法,要不是完成了,要不就是没完成。这种做法看起来好像有利有弊:好处是客户可以马上知道完成了哪些、做出来的东西是否合用、下面还要做些什么或改进什么等等;坏处是客户看到做出来的东西,可能会很不满意甚至中止合同。实际上,XP的这种做法是为了及早发现问题、解决问题,而不是等到过了几个月,用户终于看到开发完的系统了,然后才告诉你这个不行、那个变了、还要增加哪个内容等等。
结对编程
结对编程是指代码由两个人坐在一台电脑前一起完成。一个程序员控制电脑并且主要考虑编码细节。另外一个人主要关注整体结构,不断的对第一个程序员写的代码进行评审。
结对不是固定的:我们甚至建议程序员尽量交叉结对。这样,每个人都可以知道其它人的工作,每个人都对整个系统熟悉,结对程序设计加强了团队内的沟通。这与代码集体所有制是息息相关的。
可持续的节奏
团队只有持久才有获胜的希望。他们以能够长期维持的速度努力工作,他们保存精力,他们把项目看作是马拉松长跑,而不是全速短跑。
代码集体所有
代码集体所有意味着每个人都对所有的代码负责;这一点,反过来又意味着每个人都可以更改代码的任意部分。结队程序设计对这一实践贡献良多:借由在不同的结队中工作,所有的程序员都能看到完全的代码。集体所有制的一个主要优势是提升了开发程序的速度,因为一旦代码中出现错误,任何程序员都能修正它。
在给予每个开发人员修改代码的权限的情况下,可能存在程序员引入错误的风险,他/她们知道自己在做什么,却无法预见某些依赖关系。完善的单元测试可以解决这个问题:如果未被预见的依赖产生了错误,那么当单元测试运行时,它必定会失败。
编码规范
XP开发小组中的所有人都遵循一个统一的编程标准,因此,所有的代码看起来好像是一个人写的。因为有了统一的编程规范,每个程序员更加容易读懂其他人写的代码,这是是实现代码集体所有的重要前提之一。
简单设计
XP中让初学者感到最困惑的就是这点。XP要求用最简单的办法实现每个小需求,前提是按照这些简单设计开发出来的软件必须通过测试。这些设计只要能满足系统和客户在当下的需求就可以了,不需要任何画蛇添足的设计,而且所有这些设计都将在后续的开发过程中就被不断地重整和优化。
在XP中,没有那种传统开发模式中一次性的、针对所有需求的总体设计。在XP中,设计过程几乎一直贯穿着整个项目开发:从制订项目的计划,到制订每个开发周期(Iteration)的计划,到针对每个需求模块的简捷设计,到设计的复核,以及一直不间断的设计重整和优化。整个设计过程是个螺旋式的、不断前进和发展的过程。从这个角度看,XP是把设计做到了极致。
测试驱动开发
测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码,然后只编写使测试通过的功能代码,从而以测试来驱动整个开发过程的进行。这有助于编写简洁可用和高质量的代码,有很高的灵活性和健壮性,能快速响应变化,并加速开发过程。
测试驱动开发的基本过程如下:
① 快速新增一个测试
② 运行所有的测试(有时候只需要运行一个或一部分),发现新增的测试不能通过
③ 做一些小小的改动,尽快地让测试程序可运行,为此可以在程序中使用一些不合情理的方法
④ 运行所有的测试,并且全部通过
⑤ 重构代码,以消除重复设计,优化设计结构
简单来说,就是不可运行/可运行/重构——这正是测试驱动开发的口号
重构
XP强调简单的设计,但简单的设计并不是没有设计的流水账式的程序,也不是没有结构、缺乏重用性的程序设计。开发人员虽然对每个USERSTORY都进行简单设计,但同时也在不断地对设计进行改进,这个过程叫设计的重构(Refactoring)。这个名字最早出现在MartinFowler写的《Refactoring:ImprovingtheDesignofExistingCode》这本书中。
Refactoring主要是努力减少程序和设计中重复出现的部分,增强程序和设计的可重用性。Refactoring的概念并不是XP首创的,它已经被提出了近30年了,而且一直被认为是高质量的代码的特点之一。但XP强调,把Refactoring做到极致,应该随时随地、尽可能地进行Refactoring,只要有可能,程序员都不应该心疼以前写的程序,而要毫不留情地改进程序。当然,每次改动后,程序员都应该运行测试程序,保证新系统仍然符合预定的要求。
系统隐喻
为了帮助每个人一致清楚地理解要完成的客户需求、要开发的系统功能,XP开发小组用很多形象的比喻来描述系统或功能模块是怎样工作的。比如,对于一个搜索引擎,它的Metaphor可能就是“一大群蜘蛛,在网上四处寻找要捕捉的东西,然后把东西带回巢穴。”
持续集成
集成软件的过程不是新问题,如果项目开发的规模比较小,比如一个人的项目,如果它对外部系统的依赖很小,那么软件集成不是问题,但是随着软件项目复杂度的增加(即使增加一个人),就会对集成和确保软件组件能够在一起工作提出了更多的要求-要早集成,常集成。早集成,频繁的集成帮助项目在早期发现项目风险和质量问题,如果到后期才发现这些问题,解决问题代价很大,很有可能导致项目延期或者项目失败。
持续集成是一种软件开发实践,即团队开发成员经常集成它们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。
现场客户
在极限编程中,“客户”并不是为系统付帐的人,而是真正使用该系统的人。极限编程认为客户应该时刻在现场解决问题。例如:在团队开发一个财务管理系统时,开发小组内应包含一位财务管理人员。客户负责编写故事和验收测试,现场客户可以使团队和客户有更频繁的交流和讨论。
极限编程的4个价值
除了XP实践,极限编程还提倡四大价值:沟通、简单、回馈、勇气。
沟通
构建一个软件系统的基本任务之一就是与系统的开发者交流以明确系统的具体需求。在一些正式的软件开发方法中,这一任务是通过文档来完成的。
极限编程技术可以被看成是在开发小组的成员之间迅速构建与传播制度上的认识的一种方法。它的目标是向所有开发人员提供一个对于系统的共享的视角,而这一视角又是与系统的最终用户的视角相吻合的。为了达到这一目标,极限编程支持设计、抽象、还有用户-程序员间交流的简单化,鼓励经常性的口头交流与回馈。
简单
极限编程鼓励从最简单的解决方式入手再通过不断重构达到更好的结果。这种方法与传统系统开发方式的不同之处在于,它只关注于对当前的需求来进行设计、编码,而不去理会明天、下周或者下个月会出现的需求。极限编程的拥护者承认这样的考虑是有缺陷的,即有时候在修改现有的系统以满足未来的需求时不得不付出更多的努力。然而他们主张“不对将来可能的需求上投入精力”所得到的好处可以弥补这一点,因为将来的需求在他们还没提出之前是很可能发生变化的。为了将来不确定的需求进行设计以及编码意味着在一些可能并不需要的方面浪费资源。而与之前提到的“交流”这一价值相关联来看,设计与代码上的简化可以提高交流的质量。一个由简单的编码实现的简单的设计可以更加容易得被小组中的每个程序员所理解。
反馈
XP团队重视反馈,反馈越快越好。在极限编程中,“反馈”是和系统开发的很多不同方面相关联的:
来自系统的反馈:通过编写单元测试,程序员能够很直观的得到经过修改后系统的状态。
来自客户的反馈:功能性测试是由客户还有测试人员来编写的。他们能由此得知当前系统的状态。这样的评审一般计划2、3个礼拜进行一次,这样客户可以非常容易的了解、掌控开发的进度。
来自小组的反馈:当客户带着新需求来参加项目计划会议时,小组可以直接对于实现新需求所需要的时间进行评估然后反馈给客户。
反馈是与“交流”、“简单”这两条价值紧密联系的。为了沟通系统中的缺陷,可以通过编写单元测试,简单的证明某一段代码存在问题。来自系统的直接反馈信息将提醒程序员注意这一部分。用户可以以定义好的功能需求为依据,对系统进行周期性的测试。用Kent Beck的话来说:“编程中的乐观主义是危险的,而及时反馈则是解决它的方法。”
勇气
极限编程理论中的“系统开发中的勇气”最好用一组实践来诠释。其中之一就是“只为今天的需求设计以及编码,不要考虑明天”这条戒律。这是努力避免陷入设计的泥潭、而在其他问题上花费了太多不必要的精力。勇气使得开发人员在需要重构他们的代码时能感到舒适。这意味着重新审查现有系统并完善它会使得以后出现的变化需求更容易被实现。另一个勇气的例子是了解什么时候应该完全丢弃现有的代码。每个程序员都有这样的经历:他们花了一整天的时间纠缠于自己设计和代码中的一个复杂的难题却无所得,而第二天回来以一个全新而清醒的角度来考虑,在半小时内就轻松解决了问题。
极限编程的5个原则
组成极限编程基础的原则,正是基于上面描述的那几条价值。在系统开发项目中,这些原则被用来为决策做出指导。与价值相比,原则被描述的更加具体化,以便在实际应用中更为简单的转变为具体的指导意见。
1. 快速反馈
当反馈能做到及时、迅速,将发挥极大的作用。一个事件和对这一事件做出反馈之间的时间,一般被用来掌握新情况以及做出修改。与传统开发方法不同,与客户的发生接触是不断反复出现的。客户能够清楚地洞察开发中系统的状况。他/她能够在整个开发过程中及时给出反馈意见,并且在需要的时候能够掌控系统的开发方向。
单元测试同样对贯彻反馈原则起到作用。在编写代码的过程中,应需求变更而做出修改的系统将出现怎样的反应,正是通过单元测试来给出直接反馈的。比如,某个程序员对系统中的一部分代码进行了修改,而假如这样的修改影响到了系统中的另一部分(超出了这个程序员的可控范围),则这个程序员不会去关注这个缺陷。往往这样的问题会在系统进入生产环节时暴露出来。
2. 假设简单
假设简单认为任何问题都可以”极度简单”地解决。传统的系统开发方法要考虑未来的变化,要考虑代码的可重用性。极限编程拒绝这样做。
3. 增量变化
极限编程的提倡者总是说:罗马不是一天建成的。一次就想进行一个大的改造是不可能的。极限编程采用增量变化的原则。比如说,可能每三个星期发布一个包含小变化的新版本。这样一小步一小步前进的方式,使得整个开发进度以及正在开发的系统对于用户来说变得更为可控。
4. 拥抱变化
可以肯定地是,不确定因素总是存在的。“拥抱变化”这一原则就是强调不要对变化采取反抗的态度,而应该拥抱它们。比如,在一次阶段性会议中客户提出了一些看来戏剧性的需求变更。作为程序员,必须拥抱这些变化,并且拟定计划使得下一个阶段的产品能够满足新的需求。
5. 高质量的工作
没人喜欢拖泥带水,每个人都期望出色的完成工作。极限编程的提倡者认为范围、时间、成本和质量这个四个软件开发的变量,只有质量不可妥协的。