UML软件工程组织

 

 

提高软件开发生产力的秘方
 
作者: Amr Elssamadisy/Deborah Hartmann/郭晓刚 编译 出处:InfoQ中文站 
 

专业训练者和导师们一再发现:许多敏捷团队囿于同一种模式的困扰——在平庸的“照本宣科(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)团队与产品所有者一起将这些测试分配到每次迭代。

这些循环中许多都可以彼此契合。测试驱动需求包含若干测试驱动开发循环。一次迭代包含若干测试驱动需求的循环。一次发布又包含若干次迭代,诸如此类……

更进一步,这些循环是彼此牵动的——实际上每当发生学习过程而否定了更高一级的循环,即是这些循环在持续不断地牵动彼此。测试驱动开发的循环嵌套在测试驱动需求当中`——即是说对于每项需求,要有若干测试驱动开发的循环来满足它。因此,举例来说,一个失败的测试可能意味着编码的错误,也可能意味着正在实现的需求本身并不正确,或者之前的另一项需求应该被否定。因此在测试驱动开发循环中的学习过程引发了对需求的学习过程。类似地,迭代循环嵌套在发布循环中。因此,由于无法预见的技术障碍而无法达成目标的一次迭代 ,会导致系统的一个重要部分所需的工作量大大上升。于是团队会变更部署,重新考虑这次发布要交付的工作,重新调整优先次序和目标范围来迎合新的信息。

循环:必要但不充分

循环是“识别并应对变化”的两项基本要求之一——它提供了机会。第二项要求是沟通,通过涵盖所有的人来放大学习的效果,也就是敏捷方法中强调的“信息辐射”。

信息需要被传播给团队中的其他人,甚至更远。没有沟通,问题可能无法被识别出来。没有沟通,发现问题但找不到解决方案的人,就没有机会从其他不同技术背景的人那里得到解决的方案。在团队的环境中,对一个特定的问题,并不总是能够确定解决问题的最佳人选——但把问题告诉每个人等于在邀请所有感兴趣的人都提出他们的建议,而不局限于团队中的“专家”——有时候新手和外行反而能够“跳出框框”而令我们有意外之喜!

沟通能加速并强化学习过程,暂停工作来一次正式的反省可以确保沟通的声音不会被耳边“快点,快点!”的催促声所掩盖。暂停可以像“回顾(Retrospective)”过程那么正式,也可以像迭代结束后的聚餐那么不正式——只要学习与提高被列入议程。虽然沟通按计划应该发生在每次循环的开头(对目标的沟通)和结尾(对结果的检验),但在循环过程中的非正式、“渗透性的”[6]沟通也会有戏剧性的效果。

相当数量的敏捷实践直接针对沟通问题——包括正式的和“渗透性的”沟通:

自组织的团队(Self-organizing team):团队成员一起工作,一起应对发生的变化。他们作为一个集体去进行构建软件所需的工作。

同处一室的团队(Co-located team):团队成员坐在一起,并经常性地举行小组会谈,有意无意地听到成员间的谈话,每个人都由这种信息渗透而知晓正在发生的事情。

功能交错的团队(Cross-functional team):受过不同训练的团队成员聚在一起工作,从头到尾地解决一个问题。通过在一起工作,他们分享彼此的专门知识。

结对编程:两个人一起完成一项任务,是一种分享经验与专门知识的非常深入的形式。

信息辐射体(Information radiators):是一个“人人可见的大图表”,它存在的唯一目的就是把一些重要的数据传播给任何路过的人。

唤起回忆的文档(Evocative documents):敏捷团队建立文档的时候聚在一起以便能够互相交谈。以后阅读文档的时候就会唤起对整个讨论和语境的回忆。这比起传统的文档要有价值得多。传统的文档完成之后就被丢到隔壁从此事不关己,它们本质上就是用来装点门面的——也就是说,因为已经在这些文档身上投入了那么多,我们可以(虚假地)相信这些正式文档实际上就是它们所描述的对象。

站立会议(Stand up meeting):敏捷团队每天就刚刚完成的任务、遇到的障碍、以及第二天的任务计划进行沟通以达到同步。

沟通,当它被加入循环,会使得整个团队学习得更快,并且更加成为一个整体。毕竟,软件开发是一种集体工作。如果学习过程对团队来说是一个瓶颈,那么整个团队都需要尽可能更多更快地学习。

如果能妥善运用这些实践,敏捷团队无论学习什么都很快——不仅是技术:设计(TDD、结对编程、功能交错的团队),需求(演示产品和TDR),产品(迭代、发布),以及人(回顾、站立会议)。

为什么这么重要?从理论到实践

好吧,学习过程看起来是软件开发当中一个非常重要的环节。它可能比我们认为的更加重要——那又怎么样?关注学习过程云云又如何能帮助你我的团队生产出更好的软件?

虽然前面列举了众多“学习”方面的实践的例子,敏捷团队并不是对学习瓶颈免疫的。有时候在紧迫的任务的压力下,我们会为了短期的利益而跳过学习的步骤(“哦,我们以后会补上”)。在学习上失败的敏捷团队会表现出以下部分或全部症状:

◆疲劳(没有依照可持续的节奏来工作)导致士气问题

◆再三地无力在一次迭代中“搞定”

◆持续地让承诺落空(甚至有些承诺的特性和用户故事根本还没动手)

◆部署的软件持续地出现缺陷,缺陷之多使得开发计划完全脱离轨道

◆否定回顾的价值,放弃回顾(“因为我们从来都没办法解决发现的问题”)

让你的眼睛盯住瓶颈

请看下图:在每个步骤的名称下面的括号里是一个表示速度的数值。两个步骤速度不一致的时候就会产生积压(Inventory)——由于步骤A比步骤B快,A的过剩产品要等待B的处理。整个系统的产出总是为瓶颈所限[5]。也就是说,如果你希望提高整个系统的总体产出,你几乎只需要提高瓶颈部分的表现。在其他地方花功夫纯属浪费,甚至可能起到反作用。这意味着对于下图中的简单顺序过程(SSP),我们唯一的改进办法是调整步骤D。处理其他地方并不能改进系统产出。

简单顺序过程

如果学习过程确实是软件开发的瓶颈,那么它应该排在我们优先队列的第一位。也就是说我们花功夫在其他地方只会得到非常有限的生产力提高。倒过来说,这意味着任何提高我们的生产力的事物都在某种程度上提高了我们的学习效率。

现在,当我们反思我们的过程的时候,留心观察能传递更多价值的途径,我们要记住通常我们向业务方传递以下几种价值:当然首先是正常运作的软件,另外还有可维护的、可改变的软件,以及一个快速反应的团队来继续这些工作。前两项的优先次序看业务的需要,而第三项纯粹属于职业素养上的期望,通常也掌握在开发团队的手中。最后一项价值——团队敏捷性——实际上是组织的一项资产,它会比一个个项目存续得更久,它会创造出谋取更大利益的机会,并且加快后续项目的速度。

敏捷团队应该保持组织的相对稳定,应当存续六个月或者更长时间,以便培育出有效的合作。建立这样的团队需要有战略眼光,而且还要一并考虑每次迭代中必须立即满足的业务需求。如果不能维持这种平衡,由于速度和质量都无法预测,再加上过程中无法排除的陷阱,团队很快就会在业务上失败。

为了避免产生出脆弱而不协调的团队,当我们对过程进行反思的时候,应该问自己下面两个问题:

1、“这会对我们下次迭代的速度有什么影响?”

2、“这会对我们的学习过程有什么影响?”(学习又会进一步影响我们在迭代和项目中的速度和响应能力。)

因此,与其问说“结对编程会不会降低我们的速度?”,我们应该问,“结对编程会不会降低我们的学习效率?还是它会提高学习效率?“与其问“我们真的需要每两个星期做一次产品演示吗?虽然产品所有者一个月才能来一次?”,我们应该问,“把产品演示的频率降低到一个月一次会对我们的学习有什么影响?”与其问“为了支持敏捷,我应该安装哪个工具?”,我们应该问“,工具ABC促进了我们的学习吗?还是它让我们交流得更少,因而阻碍了我们的学习?”,或者采取更好的做法,一直等到真的需要跟踪某些信息(以便从中学习)的时候,才选择一件对你来说最省事的工具[7]。

当我们了解到学习是会增强“团队”这项公司资产的时候,现在我们可以有理有据地向利益相关方解释我们采取学习实践的益处了。“是的,结对编程乍看上去很奢侈——让我来解释一下它是怎样抵消风险的,而且长远来说这样做是物有所值……”别忘了——相对敏捷的短周期来说,“长远”可能说的只是3到6个星期。

下一步何去何从

我们写这篇文章只有一个目的。我们不是要分析学习的理论和机制,这个题目需要长篇大论才能解决,而且已经有了许多长篇大论。我们也不是要分类和验证各种敏捷实践——这里提到的实践只是作为简单的举例,提醒我们只要想学习,已经存在许多学习的方法。

我们的目标是把学习过程放在我们思考和行动的显著地位,因为我们相信它是敏捷实践成功的关键。不要仅仅潜移默化地学习,要把学习放到注意力的中心。本文的作用是提醒诸位,敏捷方法已经提供了许多学习方面的实践和机制——你是否已经在团队以及业务中有效地利用了它们?

用“学习过程是瓶颈”的眼光来看世界。这样可以大大减少出现“货机崇拜”[8]式的敏捷实践的机会,并让你的努力集中在如何令敏捷实践的效果最大化上面。

注释

1、初起炉灶—暴风骤雨—照本宣科—大放光彩(Forming–Storming–Norming–Performing)的团队发展模型由Bruce Tuckman最先提出。http://en.wikipedia.org/wiki/Forming-storming-norming-performing

2、这个假想实验和“学习过程是瓶颈”的说法来自Ashley Johnson,他是Valtech Skill Development公司负责商业计划及策略的副总裁。

3、关于早期轻量级方法的共通性的历史,请查阅Jim Highsmith 的作品 http://www.agilemanifesto.org/history.html

4、另一份早期轻量级方法的共通性的历史,请查阅Uncle Bob Martin的作品 http://blog.objectmentor.com/articles/2007/07/10/the-founding-of-the-agile-alliance

5、敏捷团队中的“渗透性沟通”: http://www.agilemodeling.com/essays/communication.htm

6、熟悉约束理论(Theory of Constraints)的读者可能会觉得这个论点很熟悉,如果考虑到非线性过程,这个论点在很大程度上是过于简化了。

7、参见 《Appropriate Agile Metrics: Using Metrics and Diagnostics to Deliver Business Value》,Deborah Hartmann and Robin Dymond,2006 http://www.berteigconsulting.com/archives/2005/01/agile_work_reso.php

8、货机崇拜:在第二次世界大战期间,许多空军基地建造在偏僻的热带岛屿上,岛上的土著还未进入工业时代。在战争期间军人在岛上建造机场和控制塔,从事各种活动,不时有大型货机降落及卸货。当地土著也会分得一些货物。战后军人撤离,土著也就不再有货物可得。于是他们按照自己的一知半解,模仿出机场跑道、控制塔等等形式,并举行仪式希望能召唤装满货物的飞机归来。于是“货机崇拜”被用来指一群人仅仅采纳了事物表面的形式而非实质,并相信这样做会带来他们期望的结果。

关于作者

Amr Elssamadisy是软件业里的实践者。构建软件是难的——大写的难——Amr的使命是找出开发软件的更好方法,并与社区分享他的发现。今天他在帮助他的客户成功采用敏捷实践构建出更好的软件,只要我们继续学习构建软件的更好方式,明天肯定会不一样。Amr还是《Patterns of Agile Practice Adoption: The Technical Cluster》这本书的作者,同时也是InfoQ的敏捷社区的编辑。

Deborah Hartmann是说两种语言的敏捷实践者、训练者和导师,她以多伦多为大本营从事国际性的工作。Deborah的使命是让工作既对业务有价值又能给团队带来乐趣。她指导过大企业和小公司转向敏捷的过程,从2006年4月起担任InfoQ的敏捷社区的主编,她还推动了XP社区的OpenSpace会议,并帮助加拿大及美国的BarCamp社区发展。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号