《代码之殇》(原书第2版)——第2章 过程改进,没有灵丹妙药 2006年3月1日

    xiaoxiao2021-04-18  206

    2006年3月1日:“敏捷子弹”

    我很难做出判断。也许你可以帮我。我不能断定以下两种观点,哪种更糟心:一种观点认为使用“敏捷”方法,并且恨不得微软在全公司范围内采用它,用它解决我们面临的所有麻烦;另一种观点认为敏捷是被一些无知的学者鼓吹出来的,它实际上是一种改头换面的愚昧方法,它让开发者不用承担任何责任。这是个两难的决定,两种观点都会使我有种作呕的感觉。

    作者注:这是我最喜欢的栏目之一,因为其间爱恨交错不能自已。尽管它并不完美,但我在这个主题上的评论还是相当公允的。

    现在让我们来纠正这两种观点: 如果你认为敏捷方法解决了产品开发过程中的所有错误,那你真是愚蠢至极。雇用成千上万个人来开发高度复杂、深度集成的软件给上亿客户来使用,这不是件容易的事。这世界上没人比我们对这个任务知道得更多,包括敏捷联盟的那些聪明家伙。并不是我们现在做的所有事情都是错的,也不是所有事情都能用敏捷方法来满足我们的需要。

     如果你是极端的反敏捷人士,你认为Scrum是System of Clueless Reckless Untested Methods(一个毫无头绪、不计后果、未经试验的方法系统)的缩写,那你也是个蠢蛋,而且更加无知。不假思索就随便以什么理由妄加否定,本身就是带有偏见且不专业的做法。像敏捷这样的草根运动总是有一些事实基础的,它可以使我们的团队和客户受益。那些概念可能并不一定直接适合于我们的业务,但当你停下来理解它们的时候,那些事实基础总是有一些用武之地的。

    作者注:敏捷是在微软整个公司范围内,也就是由一小撮人和一些小团队牵头的一次草根运动。

    该是破除敏捷方法的神话的时候了。我还将解释如何去使用这些方法背后的创新思维,以构建我们自己的优势。

    真理的敌人首先,让我们来破除下面这些敏捷神话…… 传说之一:敏捷就等于极限编程(结对编程、Scrum、测试驱动开发、用户需求描述或者其他敏捷方法)。敏捷方法实际上是软件开发方法的一个集合,这些方法遵从一套统一原则,但除此之外其他方面都没什么关联性,有时甚至还是对立的。你可以从敏捷联盟(wwwAgileAlliancecom)了解到关于敏捷的更多真实信息。

     传说之二:敏捷方法不适合大型组织。这种说法很荒谬。敏捷是各种方法的一个集合。这些方法中有一些不适合大型组织,但有一些适合,还有一些如果你创新一下的话也可能适合。在做出一个武断的结论之前,你必须先好好研究一下具体的方法。

     传说之三:敏捷方法很适用于大型组织。敏捷哲学崇尚“客户合作胜过合同谈判”和“随机应变胜过循规蹈矩”。但跟1亿多个客户合作是艰难的。合同谈判在跨团队依赖关系管理方面是至关重要的。(参见第8章的“各走各的路——谈判”栏目。)循规蹈矩对于商业承诺来说是必要的,因为合作伙伴在上百万美元面前会变得难以相处。在大规模项目上应用敏捷方法,要求你灵活而有创造力地去处理好这些问题。

     传说之四:敏捷意味着不写文档。敏捷哲学崇尚“能用上手的软件胜过面面俱到的文档”。很多敏捷的狂热者看到这个就认为,“啊,不需要文档了!”这么认为的话只能说你是井底之蛙。敏捷哲学声称,“精益求精。”换句话说,能用上手的软件比文档更重要,但必要的文档对客户、合作伙伴和跨部门的依赖方仍然是有价值的。

     传说之五:敏捷意味着不需要前期设计。敏捷哲学崇尚“随机应变胜过循规蹈矩”。很多敏捷的狂热者将其误解为,“没必要思考或做计划,设计到时候将突然出现!”突然从哪里出现?一个放射性污水池吗?这里的要点是说,随机应变胜过过分死板地遵循原来的计划——而不是撞了南墙后再回头。

     传说之六:敏捷意味着没有个人责任。敏捷哲学崇尚“个体性与交互性胜于过程管理与工具”和“随机应变胜于循规蹈矩”。很多管理者看到这个感到很恐惧,认为这个意味着完全没有责任可言。实际上,敏捷在这个领域收到的效果恰恰与管理者担心的相反。敏捷使个体对团队负责,而团队对管理层负责。责任性大大地加强了,并且这种独特的哲学理念让敏捷团队更加有效、有弹性并且名副其实地“敏捷”。

     传说之七:Scrum是个缩写词。这是个很无聊的传说,但它几乎让我发疯。Scrum是最有名的并且使用得最广泛的敏捷方法之一,但它绝不是一个缩写词。Scrum是根据橄榄球术语来命名的,代表球队聚在一起,手挽手,准备争球。它也是Scrum团队每日例会的名字。在微软,我们已经使用了一种类似Scrum的方法达数十年,比这个术语的出现要早得多。Scrum是最简单的敏捷方法之一,也最接近于微软的很多团队在现实中采用的方法。稍后再对Scrum作更详细介绍。

    拨乱反正抽象地谈论敏捷只会招来漫无边际的争论,但是实际中的应用才是重点。我们已经知道,敏捷实际上是软件开发方法的一个集合,问题是,“哪种方法适合在大规模项目中应用呢?”很多人对这个问题已经思考过或者撰稿论述过,但写这样的栏目的人却不多。在我给出我的观点之前,请看下面的几条基本规则:

     能不变就不变。如果一个团队已经在业务注重的标准上表现得很好,那就没有必要再改变了。改变总是有代价的,哪怕它的结果可能会很美好。你改变的目的只能是为了在最后获得改进。因此如果不需要改进,也就不需要改变。

     不要陷入太深。如果改变是必要的,也不要一下子改变所有的东西。让功能团队每次只挑一两样改进,并且留意它们的进展状况。不是所有的团队都要一起改变,也不是所有的团队都要做相同的改变。当然,如果你改变的是一个像创建系统这样的中心服务,那么所有的团队最终都必须接受它。但即使是这种改变,我们也可以选择让它对某个团队公开或先隐瞒。推荐的方法是:一点一点尝试,一点一点学习,循序渐进。

     区分项目级别和功能级别。人们感到困惑最大的地方,尤其对于敏捷方法,是在项目级别和功能级别上的差异。在项目级别,团队之间需要严格的日期和协议约束。在功能级别,你…好吧,事实上…管它呢。很多管理者都不能理解这个奇异的概念——你的团队可以在你规定的任何期限内完成工作;问题只是在于在这个日期之前要完成多少功能模块而已。只要项目级别的计划能够被跟踪和控制,那么你的功能团队应该选择任何一种能够让他们工作得最有效率的方法。

    作者注:这一段话体现这样一个理念,环环相扣。它给我们的警示是,当组织中的几个小团队使用相似的方法时,通常这个组织会工作得更好。这些方法不需要完全相同,但如果团队步调一致的话,他们会在一起工作得非常好。否则,因为团队之间的预期时间各不相同,结果他们之间的协调和沟通就会变得一团糟。

    想尝试不一样的感觉了吗

    现在你想尝试敏捷方法了。但也许你只是想安抚一下部门里的那几个敏捷疯子,在他们喝着醉人的KoolAid饮料时,给他们送上一些Scrum“小吃”。那么,你应该怎么做呢?你又怎样才能把它最好地集成到正常的工作中去呢?眼下有大量的敏捷方法可供选择,因此我在这里只能谈一谈最流行的那几个:Scrum、极限编程、测试驱动开发、结对编程、用户需求描述、重构和持续集成。

    译者注:KoolAid是美国本土的饮料,里面含维生素C,是美国人在成长时最喜爱的饮品,不含咖啡因,口味多变。

    首先,有两个方法在微软我们已经用了十几年了,它们是“重构”和“持续集成”。重构只是简单地重新组织你的代码,并不改变它原有的功能。重构用来将复杂的函数(面条代码)打散,或者在现有代码的基础上增加新的功能,就像把一个只能读取CSV文件的类改造成一个抽象类,以便能够同时读取CSV文件和XML文件。持续集成的想法是,让新代码总是定期集成到完整的工程创建(理想情况下是每天)中去,以便所有人都能对它进行测试。

    译者注:面条代码(Spaghetti Code)是一种经典的反模式,如频繁使用GOTO语句,用来形容看上去很乱、几乎没有软件结构的一段程序或者一个系统。

    让他说话

    其次是“用户需求描述”,它们就像是应用流程和一页功能规范书的组合。用户需求描述的想法是,提供足够的信息来估计需要执行与测试什么样的功能特性。

    比较麻烦的是,用户需求描述应该是由用户来创建的。很多敏捷方法假设用户经常就坐在功能团队的旁边。遗憾的是,当你有1亿个目标用户时这就成了问题。

    不管你喜不喜欢,我们需要有人代表用户的角色。像市场、产品计划、用户体验、销售和客户支持部门都可以充当这样的角色。他们对用户大量的调研成果可以写入价值主张和远景规划书。然而,当对那些高瞻远瞩的远景规划和端对端的应用流程进行细化时,我们在功能级别上仍然能够使用用户需求描述的概念,以提供足够的事实依据来对一个功能集合的实现和可行性进行评估。

    合作共赢

    “结对编程”是指两个人共用一张桌子和一个键盘在一起编码。它的想法是,当一个人在打字的时候,另外一个人则更有全局观,可以发现设计或实现的不足之处。这一对人常常交换角色。虽然两个脑袋比一个脑袋好,但这样的成本也是双倍的。我更愿意把这两个人分别用在设计和代码审查上面,这样价值会更高。然而,结对编程对于熟悉新代码库效果显著,这时通常把一个熟悉代码库的人和一个不熟悉代码库的人结成一对。

    作者注:在一些陌生的领域,我的团队曾经为创建一些新鲜的事物使用过结对编程。这种方式相当好!

    除了重构和持续集成外,“测试驱动开发”和Scrum已经被证明是在微软应用的最简易、最有效的敏捷方法。我在我的栏目中描述精益工程的时候(本章的第二个栏目),关于测试驱动开发我是这么论述的:你首先为一个类定义它的功能和函数,然后给公共函数编写单元测试,再然后才是写实现代码。这是个反复的过程,并且每次都只写几个单元测试和一点点代码。这种方法相当流行,因为这样为开发者提供了所有开发所需的单元测试代码,从而为实现代码提供即精简又高效的设计框架。

    请参阅本书第5章的“复审一下这个——审查”栏目。

    先写测试也更有趣。当你先写实现代码的时候,单元测试就成了一种花样翻新的痛苦,而且它只会带来坏消息;测试不能真正起到增强代码的作用。当你先写单元测试的时候,编写代码去适应测试会比较容易,而且当测试通过的时候你的心情会非常愉悦。

    作者注:很多业内人士都认为,测试驱动开发的真实目的是一种优秀的实现代码设计方法。虽然我同意这种观点,但单元测试的积极作用不应该被夸大。

    测试驱动开发可以和结对编程组合起来使用:一个开发者负责写一些测试,另一个负责写足够的代码来通过这些测试。他们两人的角色还可以常常交换。最后,测试驱动开发让开发者对“什么时候才算是真正完成了实现”有了清晰的认识。也就是,所有的需求都经过测试,并且所有这些测试都通过。

    有点极端

    “极限编程”是一个完整的开发方法论。它把用户描述、结对编程、测试驱动开发、重构、持续集成和另外一些实践组合在一起,其非常适合应用在跟客户紧密合作的小团队中。

    极限编程大量依赖团队知识,以及与客户之间的直接交互,而且几乎没有文档。如果你的团队是独立的,并且你的客户就在你的走廊边上,这种方法会很有效,但在微软这种现象并不普遍。如果我们不能每年赚到数10亿美元,我们的形势就会很悲惨。然而,就像我已经说过的那样,极限编程中的很多单个方法在我们的产品开发中应用得非常好。

    准备玩橄榄球!

    最后我将讨论的(可能也是被人误解最深的)敏捷方法是Scrum。人们可能把Scrum和极限编程(它实际上不用Scrum)混淆在一起,也可能认为敏捷就等于Scrum(哈?!)。除此之外,Scrum最令人困惑的部分就是下面这些相关的术语了:Scrum大师(Scrum Master)、产品备忘录(Backlog)、burndown图、冲刺(Sprint),甚至包括猪和小鸡——这些足以吓跑任何一位管理者。真是极大的误会啊!

    无论好坏,Scrum是由一个喜欢有趣名字和故事的人发明的。其实施起来既不复杂,也无争议。因此,除了重构和持续集成之外,Scrum是多年来我们内部一直在做的、最接近于敏捷方法的一种实践,并且我们还有一些重大的改进。

    接下去,让我们先来澄清那些令人困惑的术语。“Scrum”是指每天的例会,“Scrum大师”是指功能团队的组织者,“产品备忘录”(backlogs)是一些功能或者工作条目的列表,“实施进程”(burndown)图用于展示剩余的工作,“冲刺”是指小型的里程碑,“猪”和“小鸡”则是指企业家的农场动物(故事很长,但很好笑)。这些概念没有一个是创新出来的,但Scrum的确带来了一些大的改进:

     Scrum的每日例会组织得非常好,用于收集有用的数据。团队的组织者(Scrum大师)简单地问所有的团队成员3个问题:从昨天到现在完成了哪些工作(并且花费多久时间),现在到明天到来之前准备做什么(并且告知剩余的工作量),阻碍工作进展的问题。

    作者注:跟踪每个任务完成所花费的时间,是我的团队对微软Scrum作的一点小小贡献。通过把这些信息加入到实施进程数据中(还剩下多少工作量),你就可以画出美妙的累积流线图,度量花在进展中的任务和工作上的时间,并且更好地估计团队的生产力。典型情况下,产品团队花在任务上的时间大概为42%,花在沟通上的时间为30%,拿我来说,我花在一起协作的功能团队上的时间有60%之多。

     在Scrum会议上收集到的数据输入到一个电子表格或者数据库中。基于这个电子表格,你可以分析花在任务上的时间、完成日期、进展中的工作、计划变更和许多项目问题。这种情况下很流行使用实施进程图——一种展示时间和总剩余工作量之间的动态关系的图表。

    在线资料:冲刺任务清单(SprintBacklogExample.xls,SprintBacklogTemplate.xlt) Scrum大师是一个独立于团队之外的力量。他甚至最好不是组织中的一员,但这通常不太现实。Scrum大师有权力排除阻碍每日例会进程的因素,保持会议的简短。

     功能列表或者时间表称为“产品任务清单”(Product Backlog),而工作条目列表或时间表称为“冲刺任务清单”(Sprint Backlog)。通过分离这两个列表,管理层可以只关注他们想要完成的工作(产品任务清单),而团队则只关注手头的工作(冲刺任务清单)。为了保证所有事情都在正常的轨道上,Scrum大师一般每周跟管理层开一次会(比如每周的主管会议),进行状态更新。

    在线资料:产品任务清单(ProductBacklogExamplexls,ProductBacklogTemplatexlt),冲刺任务清单(SprintBacklogExamplexls,SprintBacklogTemplatexlt)

     冲刺作为小型的里程碑,它的时间长度是固定的。当一个特定的时间段用完了,一个冲刺也就结束了。典型情况下,一个冲刺大概30天。

    作者注:写这个专栏6年以来,我在一个团队里采用过1周冲刺、2周冲刺及30天冲刺。现在,我们的团队都采用2周冲刺,这是我最喜欢的。2周的时间正合适,而且无需额外的经费——所有的进展情况可以描绘在一块白板上。但是,对于完成一项重要任务来说,2周有些过长。我现在正打算采用卡班(KanBan)法或一种持续冲刺法,但这又是另一个主题了。

     每次冲刺结束后,功能团队会跟管理层一起复审工作(不错的改变,哈?),总结本轮冲刺做得好的地方,以及下轮冲刺需要改进的地方(这总比等上1年或者10年、产品最终出货后再做总结好),然后为下轮冲刺制订计划并重新评估工作条目(改变原先的计划和评估?决不!)。

    通过使用每天、每周或每月的反馈机制,Scrum使团队在一个多变的环境中保持高效的工作,并且富有弹性。通过收集一些关键数据,Scrum使团队和管理层知道团队的运作状况,在问题还没有成为问题之前就把它解决掉。通过分离管理层掌管的功能列表和功能团队掌管的工作条目列表,Scrum使团队实现自我指导,工作更加投入,使团队内的每个成员和团队外的管理层都很有责任感。

    最后你要知道的

    不是所有的敏捷方法都适合于每一个人。很多根本就不适合微软的大型项目。但Scrum、测试驱动开发、重构和持续集成可以被很多微软的团队使用,并且会有显著的效果。结对编程和用户需求描述的适用程度要低一点,但如果条件适宜的话,应用这些方法也能很有效,只要你不是很心急,一下子走得太远,或者强迫你的团队采用敏捷,应用这些方法定会大有收获的。

    作者注:几乎在我任职过的所有地方,我都见到过有管理者强迫工程师改变他们的方法论的情况。这绝对行不通,哪怕像Scrum这样很受大众欢迎的东西。管理者可以建议、支持、资助行为方式的改变,但绝对不要强制。

    如果你想有更多的了解,可在我们的内部网络或者互联网上搜索敏捷方法,同时也请留意关于敏捷方法的课程。如果你的团队方方面面都表现得很出色,那建议你不要做任何改变。但如果你希望看到更高质量,或者更好的基于功能团队的项目管理,你自己思量一下是否要采取一些行动,尝试一下敏捷方法。

    相关资源:七夕情人节表白HTML源码(两款)

    最新回复(0)