我希望大家都度过了一个快乐的春节。我也享受了一段轻松的假期——大部分时间宅在家里接待朋友和他们的家人,同时去杭州做了短暂的旅行。
十分感谢你们通过博客或者私下里给我的反馈。我希望在这篇博文中回答一些你们提出的问题。同时,为了延续整个系列的行文思路,我也会涉及一些我们团队计划sprint的方法以及sprint过程中发生的事情,并穿插着回答你们提出的那些问题。
首先,我想说的是,不存在敏捷无需计划的神话。可是,敏捷开发中的计划的确和传统软件开发中的计划有着很大区别。正如我在上一篇博文中所说,我们针对利益攸关方(stakeholders)给出的上层需求创建了带有优先级的产品待开发事项(product backlog)。这一带有优先级的任务列表形成了最基本的sprint计划。在这一过程中,我们一般遵循三阶段的步骤:在主管间进行的预计划阶段,所有团队成员都参加的计划阶段以及包含利益相关者的计划提交阶段。这里的关键是:计划是在所有成员的通力合作中进行,最重要的是由组员自发来制定标准、而不是依赖于某个项目经理。
预计划阶段在上一个sprint的最后一周进行,在这一阶段中,团队中分别带领项目经理,开发和测试的几位主管会聚集到一起讨论出在即将进行的下一个sprint中,需要开发的故事(story)列表。这个过程取决于很多因素,其中最重要的是:上一个sprint的进展情况,从利益相关者那里得到的反馈,需求或故事优先级发生的变化以及预计的团队速度。项目经理(有时甚至是开发人员或者测试人员)在阐述故事的时候会尽量简短到只描述出目标、故事的简单介绍以及故事的具体流程。我们发现OneNote很好的满足了我们这一需求(稍后会给出一个故事的截图)。
产品待开发事项总是列出对客户有价值的条目,同时它也可以增加这个团队要求的条目。但是,只有那些最终会给客户带去价值的条目才可以出现在待开发事项中。举例来说,创建并维护一个持续集成服务器以持续保证最终产品的质量,这样的条目被允许出现在待开发事项中的。
计划阶段通常在sprint的第一天进行。在开会前,项目经理会把OneNote页面的链接发送给组员,以便大家评估,并且为计划会议做好准备。通常,组员会在OneNote页面中交换意见,从而在会议之前澄清那些不明了的地方。在计划会议当天,团队组员会聚集到一起,过一下所有的故事,解决之前发现的任何问题,把故事进一步细分成一些任务,并描述每个故事的验收测试。组员同时也会对完成这些故事所需要的时间做一个大致的估计,然后根据这些估计决定在这个sprint中,团队可以完成哪些故事。
计划提交阶段在之后的一天进行,主管会再度聚集在一起并且向利益相关者介绍团队承诺完成的任务。此时,利益相关者可以提出建议对优先级进行调整。比如,如果团队成员可以完成故事A,B以及C,但是不能完成D和E,利益相关者可以建议团队在这一个sprint中完成A,B,D以及E(假设D和E消耗的总时间和C相同)。然后,项目经理会把这些故事输入用来管理我们项目的Visual Studio Team Foundation Server。
注:我们花了好几个sprint来学习并总结出以上这个计划流程。这就是sprint回顾(我会在以后的博文中提及)发挥的重要作用。
现在,让我来回顾一些针对我上一篇博文提出的问题:
在sprint中的变化以及干扰
有一位朋友提了这样一个问题,变化是敏捷方法的核心,那么团队应该如何应对sprint过程中发生的变化呢?诚然,快速有效的应对变化是所有敏捷方法的核心部分,然而,在sprint过程中的干扰始终对生产力有着不良影响。在我们的团队中,我们总是尽量避免sprint过程中的干扰,把变化延缓到下一个sprint中。因为我们把sprint的长度控制在4个星期,所以对于那些变化,意味着他们平均需要等待2个星期。:) 最起码,我们希望团队在应对变化之前,先完成那些计划了的故事。这一策略当然需要利益相关者的支持,并且在之前就达成一致。干扰对团队的影响很容易观察到,方法之一就是留意团队速度的下降。(比如在燃尽图上看到曲线的变化)
代码重构:
另一个问题是该怎样应对因需求改变导致的重构现有代码。当研究一个新的设计时,重构是有效的方法;当代码量不大时,重构也不是一个大问题。然而,一旦你的代码量开始变大,重构的代价就会变得很昂贵。由于利益相关者的反馈和需求的变化,我们也曾有相当一部分代码需要重构。 在考量代码重构问题时,最重要的依据是重构对于产品和团队的影响。
举一个例子,我们曾不得不改变当一个图形被拖动到另一个图形内部时的产品行为。因为在最初设计这一行为的时候,我们的信息不够充分。在初期的实现之后,我们注意到有一些人已经对这一行为记录了bug,因为他们认为产品的表现和他们的预期不同。我们针对这一情况采取了下列的方法:1)收集更多的反馈以明确预期的行为;2)提供了一个穿刺(spike)方案(译者注:Spike指在产品线的外部开发的试探性的原型系统),调整了产品的行为;3)对穿刺方案进行代码复查和“伙伴测试”确保解决问题。(译者注:伙伴测试指找产品组成员帮忙适用产品的新功能,以查找问题)4)对已发生的变化撰写单元测试。
另外,我需要指出的是,在任何的重构过程中,自动化测试的好处都不会被过分夸大。它能够确保正在进行的代码改变不会给产品的其他部分带来计划外的破坏。
架构与设计
尽管我们应该预期到设计和实施中会有变化发生,然而,就如我之前提及的,当代码量增大时,对代码的改变和重构的代价呈非线性的增长。面对这个问题,预先进行一定程度的架构与设计就带来了好处。这里的架构与设计并不需要非常具体化,其目的是能够刚好鉴定出在之后的实施中可能面对的主要问题。当然,说起来容易做起来难J。在项目的初期,当上层的需求齐备了,也有一个初步的产品待开发事项列表时,就可以开始进行上层架构了。尽管这可以通过纸笔或者任何建模工具来完成(我们希望在Dev10发布之后,你们会用Visual Studio Team Architect完成这项任务),你将会需要开发一个原型来支持你的设计与架构。我们发现这一步骤对项目的成功非常有帮助。对有一定复杂度的项目,你可以通过这个方法来确定应使用的技术,明确依赖关系等等。对团队来说,这也是对其各自的自动化框架加强建设的好时机。
OK,我已经讲了很多形而上学的东西。下面让我展示一些截图,把我们团队在sprint计划阶段进行的工作映对到我在前文中所讲述的方法原则。
下面的这些截图展示了我们团队在sprint计划以后讨论出的故事列表。 同时,你也会注意到一些不同的团队成员留下的评论。其中的交付编号是在TFS中对应的标识号码。
一个用户故事从用户角度描述了一个需求功能点。一个好的用户故事包括需求功能的描述,谁需要它,怎么使用它,为什么需要它。
Spint计划中的重要一环是让团队对“完成”的定义达成共识。在我们对“完成”的定义中,编写并且运行通过验收测试是重要内容之一。验收测试是在软件交付之前进行的黑盒测试。在我们的语境中,它意味着用户故事的核心内容实现得如同预期的那样。
一个验收测试应该满足两个条件:1)产品的拥有者应该能够根据它鉴定用户故事已经被实现。2)开发人员应该能够根据它检验他们是否已经开发出了预期的功能。我们不开发那些不能被检验的功能。
下面是我们所创建一个典型用户故事的具体组成部分:
接下来,你会看到一个示例故事以及基于这个故事展开的讨论。<作者注:为了节省空间,以及展示团队成员间的合作,在复核阶段对问题展开的讨论,我做了手工编辑并把他们合并在了一起。〉最重要的是对故事的讨论是团队在动手实施之前的协作,团队在那一时刻已经达成了一致。在这里,我们把对质量的要求往上流推进到很早期的阶段,甚至在团队动手开始写任何一行代码之前,我们已经开始为产品质量作了努力。事实证明,这一办法在之后节省了我们很多的时间精力。如下图所示,团队讨论并解决了关于可用性,可实施性以及可测试性的问题。
这篇博文中,我已闲庭信步于sprint计划阶段,不多言了。下一篇中,我会就sprint的实施阶段展开进一步的讨论。
Ramesh Rajagopal
Visual Studio Team Architect 中国团队经理
翻译:朱永泰
校译: 林裕科