在敏捷软件开发的各种实践中,结对编程(Pair Programming,下文简称Pair)是特别有争议的。Pair有一个特点,那就是还没有进行过任何Pair实践前,你很可能对它已经有了“喜欢”或者是“讨厌”的印象。如果有人问你,你喜欢持续集成吗?你多半会回答:不是很肯定,需要试试看。但如果有人问你,你喜欢Pair吗?我猜你会马上给予明确的肯定或否定的回答。喜欢它的人会觉得好处多多而成本低低,不喜欢它的人会觉得讨厌得难以想象。喜欢与不喜欢都可以形成强大的阵营,两边都不乏重量级的高手。Pair的优点说起来都很明显,比如:快速反馈,更好的设计,甚至更高的效率等等。但据我了解,不喜欢Pair的人基本上都承认Pair有优点,只是他们通常会提出反对意见,比如:Pair要求两个人协调一致,每个人的作息时间需要同步,如果一个人请假,那另一个人怎么办?等待就太浪费时间了,如果自己一个人做那就不是Pair了;或者也有很多人感觉到Pair的时候特别累,不像一个人工作那样自主,写一小时程序,上十分钟微博,两个人同一台电脑工作自由就小多了。
我所在的公司是Pair的实践者,对于喜欢Pair或者不喜欢Pair的各种原因和心态我都有切身的体会。在实践中,我们并没有完全一成不变地照搬Kent Beck在《Extreme Programming Explained》书中所定义的Pair模式(Driver Observer模式)。因为,Pair毕竟是一种实践方法,可以供我们在软件开发中参考,而不必像对待科学理论一样教条。另外,我认为要让这种实践方法发挥它的威力,消除人们心中的讨厌甚至是恐惧情绪是首先要解决的问题,甚至我们可以为此修改一些Pair的传统做法。下面我先来大致描述一下我们的实践模式(Owner Supporter模式):
<阶段1-Pair形成>
Team在接到一个大的Story后经过讨论并分解任务形成一个个粒度适中的Task。Task的粒度一般在几小时到一两天这种级别,涉及的代码规模大约是几行到几个类/函数。每一个Task,都有一个Owner作为责任人,这是和传统单兵开发模式相同的。不过,Owner的产生一般不是由ScrumMaster来分配指定,而是Team成员自发地去协商选择适当的任务。Owner拿到Task以后会从Team中找一个Supporter,形成Pair。这也是一个自发协商过程,没有强制指定,也没有固定的搭档,这样形成的Pair一般会是配合最默契也最适合相应Task的。
<阶段2-初始讨论>
Pair形成以后, 一般是由Owner先分析需求,设计测试,并初步分析思考得出初步的设计思路。这时,Supporter可以暂时先不了解需求。然后Owner邀请Supporter一起,向Supporter介绍需求和自己的设计思路。Supporter这时需要帮助Owner理解需求,设计测试,对设计方案提出反馈意见,或者提出新的设计。讨论的理想结果是两人达成一致形成最终的设计方案,如果两人分歧严重不能达成一致则需要更多的人参与,比如:可以举行一个小型的技术讨论会议把讨论扩大到整个Team。
<阶段3-具体实现>
<阶段4-Review>
具体实现完成以后,Owner又会邀请Supporter来review自己的工作。这个过程一般是借助版本控制工具对比代码改动,Supporter从实现的角度来审查实现是否符合设计,有没有可以直接观察到的bug和潜在的问题。如果发现问题并讨论确认,Owner再做相应的改动,再Review。最终Supporter不能review出问题了,Owner提交代码。
<阶段5-维护>
虽然经过了讨论和review, 但在后续的测试或者上线后仍然会陆续发现问题,这时就涉及到维护工作。维护工作依然是由Owner牵头,分析log,看代码,重现,修正;但是,Owner也可以随时邀请Supporter参与分析讨论,至少在Owner准备修改原先的代码时应该要告诉Supporter“原先我们某个地方没有考虑周全,有什么什么问题,我打算怎么怎么改动”。这个过程好像又回到了阶段2,然后阶段3,阶段4 ... 开发中还有一种可能是,原先的Supporter或Owner已经不在Team内了,这时就需要加入一个新人形成Pair,有经验的人应该向自己的新搭档介绍Task的相关背景。当然,还很有可能碰到根本没有熟悉该模块的人了,这时就必须形成一个新的Pair,把维护作为一项新Task。
上面基本上就是我们的Pair实践模式了。这种模式基本上还是保留了大部分经典Pair模式的要点,主要的区别在于:
1. 把Pair的形成纳入Scrum框架的任务划分过程。Scrum通常会在Sprint的计划会议中将大的User Story拆分成许多小的Task,每个Task一般对应几小时到一两天的工作量。Task列表出来以后,Team自发协商每个Task形成一个Pair,整个过程一般没有管理者的直接干预。Story的背景需要Team中的每个人都了解,Task的细节由Pair负责。Pair的效果通常也依赖于Task的合理划分。
2. 避免全程Pair。因为,全程Pair在实践和心理上会遇到很多问题,比如:时间同步问题,并行工作问题,不自在感问题。避免全程Pair可以使得Owner和Supporter的时间安排更加灵活,增加并行性,减少不自在感。大部分不喜欢Pair的人基本上都是尤其不喜欢全程Pair,所以这项改动对于吸引更多的人采用Pair具有重要的意义。
3. 区分Owner和Supporter的职责。经典Pair模式中Driver和Observer角色是随时交换的,也就是说对于一个Task来讲两个人的职责是相同的,这样可以减小每一个人的压力,但又容易造成缺乏责任感。Owner Supporter模式是单兵模式和经典Pair模式之间的一种妥协,试图在责任和压力之间找到一种平衡。Owner是驱动整个过程的主导,并且是主要的具体实现者;Supporter以辅助为主,在讨论和review的时候可以站在不同的角度帮助Owner。Supporter并非没有责任,熟悉Task的所有细节是Supporter的责任。当Owner由于请假或忙于其他事物时,Supporter应该立即可以代替Owner处理Task的实现或维护。如果Supporter做不到完全的backup,这就是Supporter的失职,在整个Team和领导的心中应该有这样的认识。
最后,除了显而易见的“引入反馈,提高质量”等很“官方”的优点外,我从管理者和开发人员的角度分别总结一下Owner Supporter模式的“不为人知”却真正很有价值的优点。
管理者角度:
1. 应对人员变动:每个模块至少两个人熟悉,这样在有人请假的时候都有backup,不会造成严重的耽误工作进度。如果有人要离职,交接工作通常也会非常轻松,几乎没有什么需要特别交接的。很多项目都缺乏文档,只有实现的人知道怎么回事,而即使有文档也总有不清楚的地方,所以留住熟悉的人就是留下了活文档。
2. 增加对质量的信心:相比单兵作战模式,即使是不懂技术的管理者,Pair也可以自然地增加他们对于软件质量的信心,毕竟“三个臭皮匠顶个诸葛亮”。另外,需要特别提到的是安全性问题。我曾经听说过一个电信计费项目,一个开发人员故意留了一个漏洞,把钱自动打入自己的银行卡,最终为公司带来了麻烦。如果采用Pair方式,这种情况就不那么容易发生。
3. 过程透明,增加反馈:信息反馈是敏捷方法的重要支柱,而及时准确的反馈又依赖于过程的透明。在Pair模式下,整个软件开发过程是透明的,开发人员不容易有意或无意的隐藏项目的细节和风险,因而管理者更容易获得及时准确的反馈。另外,对于同一个事物,不同的人也会有不同的看法,这是很正常的现象。比如,在对问题进行估计时一个人乐观,一个人悲观,这时管理层可以同时参考他们的意见,对问题掌握得更加全面。
4. 代替培训:相比传统课程或讲座式培训,Pair能让新员工更快地适应项目。Pair方法也和一些公司采用的导师制有明显的区别。导师制更强调师傅带徒弟,而Pair则不然。在实践中,我们更鼓励基础比较好的新员工多做Owner,老员工作为Supporter支持;只对基础比较差的新员工先从Supporter做起,然后逐步过渡到Owner。这样做是因为新员工一般积极性比较高,适合承担更多的具体工作,而老员工则应该避免重复劳动,而是以辅助和指导为主。另外,新员工也可能带来一些新的思想、方法和技能,这些反而是老员工应该学习的。
开发人员角度:
1. 分担压力,增加默契:Task依然具有明确的责任人,但Supporter可以分担Owner的压力,即使是Team内的技术高手有一个人做backup也是有益的。有人讨论有人支持更容易达到良好的工作效果,而大家相互Support也可以让团队更加默契。
2. 消除经典Pair的不自在感: Owner Supporter模式与经典Pair的Driver Observer模式相比更少存在不自在感,因为整个过程是Owner主导的,他有更多的自由安排什么时候讨论,什么时候独立工作。
3. 分享知识和技能:在Pair的过程中Owner和Supporter会自然地相互传播和分享知识和技能,有助于学习提高。
4. 培养组织能力和表达能力: Owner相当于一个微型团队的leader,在Pair过程中可以锻炼组织能力和表达能力。这个过程可以让一些不善于交流和组织的技术人员逐渐改善交流和组织能力。
Owner Supporter模式在我们的实践中收到了良好的效果,而且成本很低,值得推荐!