-
频繁更换结对之惑 - [Agile]
2008-07-12
【本文发表于《程序员》杂志2008年第7期,是在《频繁的Switch Pair是一种浪 费吗?》一文的基础上修改而成的。】
敏捷软件开发中有一项实践是结对编程(Pair Programming),指的是两个人坐在一起共同完成一项任务,这样做的目的常常被解释为便于共享和传递知识,提高代码质量。
结对的两个人是需要更换(Switch Pair)的,否则时间长了就跟一个人没什么区别了。不同的团队往往有不同的更换策略,即使同一个团队的策略也经常变化。有的团队做完一个故事(Story)[1]再换,一般要1到3天或更长;有的团队每天换一次,把在Story上时间长的那个人换走,另外一些团队则可能相反,确保每个故事有一个所有者,他始终不离开这个故事;还有些团队坚持每半天换一次。
每次更换的时候就有一半的人要做“任务切换”,这往往被认为会浪费时间,降低工作效率,妨碍知识积累,不容易集中精力做好一件事情。浪费是指从客户的角度来看增加了成本但没有增加附加价值的活动。精益生产[2]中有8种典型的浪费[3],其中一种是“运输”,因为运输增加了成本但并未增加产品的使用价值。Mary Poppendieck和Tom Poppendieck在他们的《精益软件开发 – 敏捷工具箱》一书中阐述了软件开发中与之对应的几种浪费,与运输对应的就是任务切换。这么看来,频繁地更换结对就是一种浪费了吗?
先看看任务切换时到底浪费了什么。浪费出现在新人可能要花时间弄清楚要做什么,已经做了那些,用什么方法做的,还要了解那些未落实到代码中的隐性知识,对应的,另一个人则要花时间解释这一切。这带来了所谓的“切换时间”,在更换结对的过程中就是指从停止编写前一个故事的代码到能够顺畅开始编写下一个故事代码之间的时间。
因此说频繁地更换结对是浪费,它是通过切换时间的积累体现的。既然如此,是不是简单地降低更换频率就好了呢?事实上,这样做会带来更多潜在浪费,因为这会减弱结对本应起的作用,引起由于知识传递不及时带来的更长的切换时间,代码由于审查不足变得复杂又会增加切换时间、减慢开发进度。复杂性还会导致需要更多时间发现、登记、跟踪和修复由此带来的Bug。这就是为什么我们说频繁更换结对是“必要的浪费”。
浪费分为“纯粹的浪费”和“必要的浪费” 两种,前者可以而且应该被彻底消除,办法不是消极地忽略不做,而是积极地将其要达到的目的变为不必要,或是通过更直接的方式来实现其目的。比如对于部分设计文档,如果交付方式和开发流程允许设计和开发在时间和地点上足够接近或者干脆由同一组人来做,那么通过文档的交流方式就显得没太大必要了。对于“必要的浪费”,有如下要点:- 从客户的角度看虽然没有价值,但却可以避免更大的潜在浪费,降低项目的系统性风险,比如测试和重构等工作。
- 只有在“理想的环境”下才可能被彻底消除,比如设想在绝对理想的环境下,是可以不需要管理和测试工作的。
- 像对待“纯粹的浪费”一样,如果暂时无法消灭它,也要设法将它压缩到最少的程度。很多“最佳实践”都是引入并 压缩必要浪费的结果。比如重构,设想两个极端的情况,一个是强调“提前做、频繁做、小量做”,另一个是开发几个月后实在做不下去了再来个大重构。显然前者积累的重构工作量要远小于后者,而且更能达到重构的目的,是我们推荐的最佳实践。记得我的项目经理曾经说过:“如果有人在站立会议中说他昨天做了某某重构,这很可能说明这个重构做的太晚了。”
频繁地更换结对并不一定会导致更多的“切换时间”,团队要努力压缩这一时间,减少这一必要浪费。这方面的方法很多,比如:
- 尽量小而独立的故事。这样的故事需要较少的上下文知识,更换结对时无需复杂的交接。
- 标准化的工作环境。这包括编码风格、开发环境、配置脚本、版本控制环境和知识技能等等。标准化减少了很多低级的时间浪费和重复劳动。标准化就是智慧共享,标准是被用来挑战的,它并不意味着很少变化,相反要持续改进。
- 通过重构维持简单设计。简单设计一定是重构出来的,而不是“设计”出来的。保持敏锐的嗅觉,充满勇气地捍卫简单设计。
归根结底是要保持“简单”。谨慎地为复杂的业务逻辑建模,谨慎地为复杂的建模编写代码,因为业务模型可能并非真的有必要这么复杂,客户也未必会真正受益于这个复杂的模型。代码设计要简单,没有重复的逻辑,团队中的每个成员都能看懂任何部分的代码,简单到只需要极短的切换时间。简单意味着高质量,它是频繁更换结对的目的,频繁更换结对则是实现简单性的手段之一。
频繁更换结对便于及时地暴露和消除浪费。设想如果不需要更换,就算再努力地在低质量的代码中重新清理自己思路,这些浪费的时间也不易被察觉到。当更换的过程中出现下列现象时,就说明有问题了:- 接手故事的人开始发牢骚,甚至骂人。这可能是因为遇到了责任不清的类,不知所云的函数命名,重复的代码段,含义不清的测试或没有被测试覆盖到的逻辑等等。
- 紧皱眉头,不断在文件间切换,没有写代码。同样是面对不知如何下手的代码,内向一些的人反映可能没那么激烈。
- 故事总是一拖再拖,不愿意结束。这可能是因为前期积累了太多的问题,没及时处理,导致这个故事遇到了很大麻烦。
- 低级Bug增多。
这些现象往往说明代码中有问题了,问题还可能来自于不合适的进度压力,知识的缺乏,低落的士气和责任感的淡化等等。出现问题时要勇敢地去解决问题,而不是首先想到降低更换的频率,很低的更换频率容易让问题隐藏起来。
很多敏捷实践都是这样的,把所有的缓冲和保险关掉,恨不得将一点点“小问题”迅速放大,让开发过程变得很“脆弱”。比如,某一个失败的测试会被持续集成环境捕获并报警,团队其他成员因此不能提交代码,甚至不得不停下手头的工作。而有些开发过程,可能更善于缓冲、转移或吸收掉一个个小问题和小浪费,但最终注定会给整体带来伤害。
更换结对的频率是多少才合适呢?这主要取决于目前代码的质量,项目类型和团队成员的能力等。简单地说,这个频率要能逼出问题,但不能太多,必须是团队可控的程度,否则容易造成混乱。
更换结对作为发现和消除浪费,保持代码简单性和高质量的手段之一,重点不仅仅是要找到适合团队的更换方式和更换频率,而且还要在团队进步的同时不断调整更换方式和频率,视较长的“切换时间”为复杂性的表现,持续逼出并消除浪费。
[1] Story在敏捷开发中代表了一项功能需求,在迭代过程中用于将团队所有角色的工作组织在一起。
[2] 精益生产是与大量生产方式相对立的,它强调从用户的角度出发,将一切未产生附加价值的活动定义为浪费,并通过消除浪费达到低成本、零缺陷和迅速交付的目的。
[3] 精益生产中的8种典型的浪费分别指过量生产、等待、库存、过度加工、运输、无畏的动作、缺陷以及未被利用的人的创造力。
收藏到:Del.icio.us