专业训练者和导师们一再发现:许多敏捷团队囿于同一种模式的困扰——在平庸的“照本宣科(Norming)”阶段一直徘徊,团队的成长始终没法进步到令人兴奋的“大放光彩(Performing)”的阶段[1]。我们请读者来一起思考一下,在所有的软件开发项目中,是不是存在一种共通的东西,当我们能够将之发挥到最大程度时,可以令生产力暴增。实际上,我们相信大多数成功团队(无论敏捷还是传统的),都曾经利用过这种看上去简单但常常被忽略的软件开发的“秘方”:经常地花时间反省和学习。学什么?一切:成员彼此、技术、问题域、客户,等等。学得快的团队才会成为赢家。本文将详细分析这种困扰团队表现的看不见的“学习瓶颈”。
一项假想的实验
我们要做些什么才能让软件开发变得更好?什么是软件开发中通常的瓶颈所在?这里面有什么共通性吗?
今天的环境已经不允许我们实际去进行实验。要进行一次真正的实验你需要把同一个项目完成两次。不幸的是,这对今天的商业环境来说,成本太高以致不可能进行。因此,要想进行真正的实验来确切地找到共通的弱点是不现实的。
不过,我们都是实践过软件开发的人。那就让我们来展示一个假想的情形吧:
假设我是你的客户,我想让你和你的团队给我开发一个软件系统。于是你的团队动手实现这个软件系统。一共花了一整年时间——12个月——来完成这个运作正常的、通过测试的系统。
于是我向团队表示感谢,接受了交付的系统,然后把它丢掉。然后我让你和你的团队给我重新开发这个系统。你的人员保持不变。需求也保持不变。工具软件也保持不变。基本上什么都没变——整个环境完全一样。
你和你的团队要花多少时间来重新完成这个系统呢?
当我们提出这个假想的情形——提问的对象中很多都有着超过二十年的软件开发经验——他们的答案多数在第一次完成时间的20%到70%之间变动。也就是说,原来要花一年时间开发的系统,重新完成一次只需要2.5到8.5个月。这是一个很大的差别!很难再找到另一个因素能够对软件开发有这么大的影响了!
那么,问题到底在哪里?第二轮开发中到底有什么不一样了?是团队。他们一整年都粘在一个团队中,因而互相了解。他们又理解了真正的需求——不仅仅是写下来的那些需求。他们还学会使用手中的工具,理解了在整个软件开发中浮现出来的问题领域的种种特质;基本上在创造并交付一个成功的软件解决方案之前,他们已经解决了所有未知的东西。学习过程正是软件工程的瓶颈。[2]
学习过程的存在占据了工作量的很大比例。实际上,我们估计它消耗了30%到80%的开发时间。正是出于同样的理由,敏捷实践才如此成功——它们的主题就是识别并应对变化。
敏捷实践,从测试驱动开发和持续集成,到迭代和回溯,共同组成了一个帮助团队学习得更快的链条。通过反复运用种种实践,敏捷团队加速了学习的过程,直面软件工程的瓶颈。我们可称之为“科学方法”、“持续改进”或者“检验一切”。
以“学习过程是瓶颈”的眼光
检验敏捷
敏捷软件开发是可行的——这已经是一个确立的事实。在过去的八年中已经记录下了数以百计的成功故事。敏捷开发中特别有趣的一点是,大多数实践都不是什么新东西——实际上它们都颇有些年纪了。敏捷仅仅是把许多最成功的软件开发实践提炼出来,并把它们放到一起。实际上,敏捷宣言并没有在创造新事物,而是在自90年代以来已经获得成功的许多轻量级方法当中寻找共通点(Jim Highsmith和Uncle Bob [4]分别对这些方法作了总结)。对大量被认为最有效的实践的反思揭示了一些非常有意思的共通点……
“识别并应对变化”的循环
那么我们如何学习呢?我们从自身的错误中学习(只要我们留心,许多人都做不到这点)。我们怎样学得更快?更快地犯(小)错——“Fail Fast”。马上运用我们学到的东西,是巩固学习过程使之牢固的关键。通过在下一个循环中立即运用我们学到的东西,我们重复利用了学习的效果(就像复合利息一样!)。
我们一再遇到的一个非常典型的循环过程如下:
 |
循环过程 |
这个简单的循环正是敏捷开发中许多实践的基本:
◆测试驱动开发(TDD):1)编写一个会失败的测试,2)编写代码以让测试通过,3)运行测试——它通过了吗?4)如果测试仍然失败则学习其原因所在然后转到2)。
◆每日循环:1)全体一起设立本日要完成的任务,在每日的站立会议上作报告,2)开始工作,3)第二天回来报告完成的进度和遇到的阻碍,4)在第二天的计划中应用学到的东西。
◆测试驱动的需求(Test Driven Requirements,TDR):1)把需求定义成测试,2)开发软件,3)运行测试;如果通过了就算完成,如果没有就进行4)找出原因并回到2)。
◆迭代:1)定义一个“完成状态”,2)确定一组需求作为本次迭代的要交付的工作(Backlog),3)进行迭代完成要交付的工作,4)测试每个项目是否达到完成状态,将之标志为完成,或者将之放回未交付工作待后考虑,并据此结束迭代。
◆产品演示(Demo):通常在迭代结束的时候进行,它给了客户一个机会去检验预想的需求在实现出来之后是否真的能够解决手头的问题。1)(隐含地)确定需求所针对的业务需求/价值,2)定义需求,3)在迭代中满足需求,4)按照业务上的需要来评估需求的实现。
◆回顾(Retrospective):1)决定一种团队工作的方式,2)在一次迭代中运用这种方式,3)反省该实践的效果,4)采取行动,规定负责人和期限,以图创造一种更有效的过程,并实现它们。
◆发布:1)为本次发布设立愿景和要达成的商业目标。2)设立本次发布要交付的工作(Backlog),3)为本次发布进行若干迭代,4)部署给客户,并为下一次发布收集反馈。
◆协作小组的协作小组(Scrum of Scrums):在同时应付多个项目的情况下,每个项目都有自己独立运作的循环,同时又通过定期的会议保持同步——即协作小组的协作小组(Scrum of Scrums)——在会议上每个项目的代表向其他人报告,以便从企业的层次上识别和应对变化。
◆管理测试:1)与产品所有者或者业务的利益相关方一起,从较高的层次上定义他们如何测量一个项目或者发布是否成功,2)保持这些“测试”对开发团队清晰可见,以便他们的交付能达到这些期望,3)把管理测试的评估过程整合进回顾(Retrospective)过程,4)团队与产品所有者一起将这些测试分配到每次迭代。
这些循环中许多都可以彼此契合。测试驱动需求包含若干测试驱动开发循环。一次迭代包含若干测试驱动需求的循环。一次发布又包含若干次迭代,诸如此类……
更进一步,这些循环是彼此牵动的——实际上每当发生学习过程而否定了更高一级的循环,即是这些循环在持续不断地牵动彼此。测试驱动开发的循环嵌套在测试驱动需求当中`——即是说对于每项需求,要有若干测试驱动开发的循环来满足它。因此,举例来说,一个失败的测试可能意味着编码的错误,也可能意味着正在实现的需求本身并不正确,或者之前的另一项需求应该被否定。因此在测试驱动开发循环中的学习过程引发了对需求的学习过程。类似地,迭代循环嵌套在发布循环中。因此,由于无法预见的技术障碍而无法达成目标的一次迭代 ,会导致系统的一个重要部分所需的工作量大大上升。于是团队会变更部署,重新考虑这次发布要交付的工作,重新调整优先次序和目标范围来迎合新的信息。
(编辑:aniston)
|