在我做咨询工作的三年半时间里,我(跟客户)谈论结对编程的时间比其他任何话题都多。一般来讲,客户的开发人员都从来没有结对过,也根本没有这个念头。而且更糟的是,那些搞商务的总觉得两个人坐在一台机器前面是浪费。
不过即使有着这些成见,等我们离开的时候,业务人员跟开发人员也已经坐到一起结对了。
成功实施结对是很困难的,不过一旦你用上我学到的经验,一切皆可能。
我假设本文的读者曾经有过一些结对的经验,也正在寻找帮助组织实施结对的方法。身处不同角色的人都可以从本文中得到一些启发,不过它的主要目标受众还是那些致力于在团队中推广结对编程的开发人员或是团队领导。
这篇文章没打算去告诉你是否应该去做结对。跟大多数事物一样,结对编程也是有利有弊,而且针对这个话题也有一些比较严谨的分析了。讨论结对编程的利弊会让本文偏离重点,本文所要讲述的是,如果你已经信任结对编程了,那怎么把它带到团队中来?
结对空间的配置
就我的经验来看,把结对环境配置好可以向成功实施结对迈出很大的一步。下面描述了我最喜欢的配置,不过你还是得按照自己的具体情况进行调整:
- 方桌可以让大家挨着坐得很舒服。Nat Pryce 也用过圆桌,结果也挺不错。不过你面前正好对着一条弧线,干活的时候就会难受;最好别这样干。
- 买到性能最好的开发机器。如果结对用的机器比他们自己用的机器更好,他们就会更喜欢用前者。此外,你只需要给两个人买一台机器就行了,这样购买能力还能高点。
- 带双 DVI 输出的视频卡。分离器也行,不过效果不好。最好是能够有双 DVI 输出,可以把分辨率调到最高。
- 两个 24"或者 30"的显示器。两个大显示器(跟两套键盘鼠标配合起来),显示同一个桌面,可以让结对的人感觉到他们都是在用自己的机器。
- 两套键盘鼠标。每个人都可以用自己的那套,不过可能有的人就喜欢两个人用一套键盘鼠标。
我的想法是,一定要让大家有一个尽量舒服的工作环境,至少要跟他们用自己的机器干活一样舒服。
让大家离开自己的桌子(和耳机)是很难的,不过要是让他们在一个条件更差的环境里工作可就太蠢了。你应该把地方搞得舒舒服服的,让大家可以心甘情愿的过来。
把环境弄好以后,你便向成功迈出了第一步,也是很大的一步;然后你还要保证把机器配置好。我一般都是给一台机器做个镜像,然后用它配置其他机器。每 周重镜像一次也挺好的。开发人员常常会在需要的时候在结对机器上装一个新工具,或者加一个别名(alias),但是其他结对机器上不一定也有这工具或是别 名。我发现定期重镜像会强迫团队成员去更新镜像,而不是每次只管自己的机器。
镜像里面放哪些东西也要看具体团队。从理论上讲,要想让整个团队在编辑器、桌面布局等方面达成一致,你非得弄个头昏脑胀不可。不过实际来看,任 何一个配得上自己工资的程序员都希望能够用最理想的工具工作。有的时候,每个人的心中都有一个自己看来最理想的工具,不过总会有一个工具胜出。如果没有明 确的最佳选择,那就先选一个标准工具,用起来再说。有一个装满了各种最佳工具的镜像,就可以保证每个人在任何结对机器上都可以高效工作了,这个结果很令人 满意。
不过别想一开始就能把镜像搞定。创建镜像也是一个迭代式的过程,团队应当持续改进。
我在布置同地结对环境上做 的很成功。几乎每个人都有一个会议室,当你许诺开放的办公环境能提高 20% 的生产率的时候,人们自然愿意放弃些私人空间(同地结对的情况下,20% 的提高 是很容易做到的)。在 DRW(我当前的雇主)里,我们拆掉了一个个小格子,坐在桌前望过去可以一马平川。我们放弃了“个人”空间来换取结对环境。想干点自 己私事的时候,就可以拿个笔记本到会议室里去。
我坚信,如果没有很好的结对环境,即便是你采取了本文中的其他建议,那估计也会是失败的结局;不过有些读过这篇文章的人也不同意我的观点。但我们都 对一点保持意见一致:一定要尽可能的消除过程实施中的障碍,这绝对忽视不得。以我的个人经验看来,最大的障碍莫过于给大家提供舒适的工作环境,但就像读者 所说的那样,万事万物并无绝对。
一次只关注一个人
我从不相信强迫人们做结对能有效果。有些人喜欢结对,有些人不喜欢。无论如何,要是强迫人们去做结对,他们就不会给你好脸色的。
我用的是慢慢生根发芽的方式。一般我先专门跟那些不太确定结对是否有效,但是能够接受这想法的人结对。用不了多久他们就会喜欢结对胜于单独工作了。这时候我就可以去跟下一个不太接受这想法的人结对。先摘下触手可及的果实,然后再去找最排斥结对的人。
通常我不会放弃那些拒绝结对的“顽固分子”。在我去努力“劝降”他们之前,你往往会发现以下两种变化中的一种
- 他们注意到除了他们以外,整个团队的生产力都有显著提高,所以他们来询问他们是不是也能参加到结对中。
- 他们注意到除了他们以外,整个团队的生产力都有显著提高,他们不得不最终离开。显而易见,不能融入团队的人往往表现较差。通常,这些人也能看到这一切,而选择新的方向。
根据我的经验,90% 的情况下,那些你想保留住的员工,最终会喜欢上结对。当然,你也得好好考虑一下,由于失去那些坚决反对结对的员工,而给你 的项目和业务所带来的影响。有的时候,你可以把他们换到别的一些让他们不觉得抵触或者有压力的岗位,这也合情合理。另外的时候,让他们离开可能更加合适。
澄清一点,这世上还是有很多不错的开发人员不相信结对的。但是我认为他们当中的绝大多数很可能从来没有正确地使用过结对。当然即使正确地结对过,他 们中的大部分人仍然有可能还是更倾向一个人编程。我往往会认为这些人都是不错的,只是并不合适那些主要有结对组成的团队。倾向于结对,并不表示你就是天 才。同样地,不喜欢结对也不表示你就不是天才了。
从长远的角度来看,我相信在一个团队内部,如果对结对编程有着不同的理解是不利的。根据我的经验,不管是支持结对的,还是反对结对的,大家都认为在 一个团队里面有两种不同的观点是极度低效的。通常的结果就是团队中人与人之间不断地为了这个话题进行斗争,把宝贵的时间都花在了争吵和互相责怪、推委上面 去了。
交换结对
如果你跟某个人一起工作,他不排斥结对,但是不确定结对是否可行,那就可以常常跟他结对,甚至一直结对。最开始的几次结对尝试算是适应阶段,这个时 候应该给他们提供最佳的结对工作环境。不过一旦有人度过了实验阶段,开始享受结对的乐趣,那么最好就换个人来结对。按我的个人经验来看,结对的时间最少一 天,最多两天。当然这也要看具体环境,但在我过去的编程生涯中,这个数据倒还是保持恒定的。
跟一个人结对多长时间效率最高?从不同的视角来看会得到不同的结论。我在上面那段话中提到过,我一般跟同一个人至少结对一天,最多不超过两天。从其 他角度出发,我可能会建议每个迭代交换一次结对,随意混乱结对。这两样我都觉得有点极端,但我也认识到,在特定的环境下他们也可能是明智的选择。藉由频繁 交换结对,我们会在两处收获颇丰:
人们在工具(如 grep、IDE)、领域、模式、测试等等方面各有专长。在实现你当前的特征时,跟这方面的专家一起结对就是个相当不错的主意。
在团队中,你有时会从别人身上学习,有时会去指导别人;跟不同的团队成员一起工作,会让你更好地掌握扮演不同角色所花的时间。
交换结对能够大幅度提升效率,从而带来了更高的实施成功率。
谁来当驾驶员
新接触结对的人常问这种问题:你们难道不是同时编码么?简单一句话:不。我们可以通过“乒乓结对编程”的方式来解决有人编码太少或者太多的问题。
“乒乓结对编程”(又称“乒乓结对”)指的是两个人交换编写测试和实现代码。也许用“乒乓结对测试驱动开发”这个冗长一些的名字会更为准确,不过也就更麻烦了。结对的流程看起来可能是下面这个样子:
1. 开发者 1 号写一个失败的测试。
2. 开发者 2 号编写仅仅够用的代码,让测试通过
3. 开发者 2 号写一个失败的测试
4. 开发者 1 号编写仅仅够用的代码,让测试通过
5. 回到第一步。周而复始。
在引入结对的时候,“乒乓结对”可以很见成效。
无论如何,过了开头几天以后,结对中每个人所扮演的角色往往就会决定了“谁应该敲键盘”。
结对中的指导者
不管给了什么任务,在一对人里面,几乎都会有一个对问题了解的更加清楚一些。在这种情况下,让了解问题更少的人来当驾驶员则更有意义。指导者应该把 思维模式调整一下,从“我怎么来交付这个功能”变成“我该怎么领导我这对人,从而针对这个问题给出正确的解决方案”。在扮演指导者这个角色的时候,他应该 是传道授业解惑,而不是鞠躬尽瘁实现代码。你该听说过这句话:授人以鱼不若授人以渔。
注意:我发现无论是学习、实施,还是已经进入结对的稳定阶段以后,这角色都很有价值。在学习和实施结对编程的时候,意识到自己是一名指导者,并有效地进行知识传播,就可以跟另一半建立起强烈的自信和信任关系。在稳定阶段,当一个指导者可以在完成特征的过程中传播知识。
扮演指导者的角色可以有很好的机会从宏观上考虑全局。你也许会想出一个更简单的办法可以解决手头的问题。如果我想出一个更有效的解决问题的方式,我 首先会教会另一半怎么用最开始的方式完成任务(如果有时间限制的话),然后对另一个念头做一个快速探测,如果解决方案较优,那对方就可以理解为什么我们要 切换过去,如果还不如最开始那一个,我们也可以很容易做还原。
结对中的学习者
有了指导者,另外一个人就肯定是学习者了。学习者的任务只有一个:控制结对用的机器,彻底弄清楚问题,用自己觉得最显而易见的解决方式来实现任务。 如果这个方案可行,指导者就可以提供一些必要的指导。如果方案中有缺陷,指导者也很容易对方案中的不足之处加以指点,为改进指明方向。
无论是指导者还是学习者,这种分类都不是长期固定的。这得看当前所进行的活动。我可能是 RoR 的专家,另一个伙伴则可能是 SQL 和 JavaScript 的专家。实现一个特征的时候我们会多次交换角色。重要的是得弄清楚什么时候作交换,并对自己的角色进行调整。
这里有一条规则,学习者应该承担大部分驾驶员的任务。
不感兴趣的 & 精力不集中的结对伙伴
有时候,你的结对伙伴对手头的任务不感兴趣。跟了无生趣的结对伙伴一起工作决不会让人兴趣盎然。只有两个人齐心协力,才能从结对编程中得到最佳的解 决方案。很显然,无精打采的人是不会好好协作的,所以解决方案也很难是最优解。最好的情况下,即使解决方案已经足够完美,但另外一个人的时间却被完全浪费 了。
一个结对伙伴不感兴趣必然是有些原因。当然,要弄清楚为什么一个结对伙伴不感兴趣,就涉及到个人问题,是不能按照一般方式来解释。
精力不集中的结对伙伴,就像描述的那样,很容易被周围的环境所影响。他们往往花在打字上的时间比较少,因为他们总是忙着帮助别人。他们不是没有 价值的。事实上,他们通常能对他们的结对伙伴正在编写的代码做出巨大的贡献。当然,他们很少 100% 投入,因为他们经常在同一时间尝试去帮助多对结对伙伴 解决问题。有一些精力不集中的结对伙伴也会花很多时间在查邮件和别的辅助工作上。
Joel Spolsky 相信开放式的工作环境很有意思,但不够高效。虽然我不同意这个观点,但我也明白一些精力不集中的结对伙伴是怎么样很容易地就让经理们得出这个结论的。
我曾经是个精力不集中的结对伙伴。我也曾经和几个精力不集中的结对伙伴们一起工作过。我相信我们每个人都不时地会遇到这种情况。过去我看到过通 过制订一些政策来管理精力不集中的结对伙伴。比如,“个人的笔记本不能带入开发区域”这条规则就在我之前的项目中出现过。这条规则能帮助大家解决些问题, 但它也带来了新的别的问题。
不感兴趣和精力不集中的结对伙伴所带来的最大的问题是他们不能 100% 投入到结对编程中去,这样就影响到了生产力的提高。如果你发现你遇到了一个不感兴趣或者精力不集中的结对伙伴,不烦尝试一下这几个方法,可能能帮到你。
我个人比较喜欢的对付精力不集中结对伙伴的方法是运用乒乓结对。乒乓结对迫使双方都同时关注同一点。这个建议的另外一个好处在于它不需要制订任 何别的可能带来副作用的规定。乒乓结对编程可以帮助一个不感兴趣的结对伙伴转变成一个有贡献的结对伙伴。当然,通常他们的贡献跟他们 100% 专注所应有的 贡献还是有差距的。我发现,当你跟一个精力不集中的结对伙伴打交道的时候,假装混乱会更有帮助。
假装混乱可能很枯燥,真的很枯燥。你一般已经知道需要去做什么。通常你脑子里也有了解决方案。你可以独自一人实现这个方案,而把精力不集中的那 个结对伙伴晾在一边。不幸的是,根据我的经验,人们实际上往往就是这么干的。但是,简单的把他们晾在一边不是最佳的长期解决方案。那个结对伙伴可能在某天 被要求去改进你当时的那个解决方案。如果他们做不到,因为他们当时没关注过,你可能被从你新的任务中调回来解决这个问题。更糟糕的情况是,你可能已经离开 了,那么关于这段代码一切情况也就成了永远的迷了。
所以,假装混乱很枯燥,但它对团队最有利。不去实现那个方案,相反地,你去问你的结对伙伴:我们怎么来完成这个?不要接受他们的第一个答案。他 们的第一个答案很可能很快给出,但难以置信的丑陋。给出些你的观点,但不是告诉他们你有方案了。相反地,一直问问题。这样很有可能,你将会慢慢明白他们的 方案了。即使你很快明白了他们的方案也没关系,你还是假装不明白。你得迫使你那个不感兴趣的结对伙伴写出前 3 个测试,并且编写代码让测试通过。此时,你可 以接过键盘,写些测试方法,但要求他们来实现。随后慢慢地一步步地转入乒乓结对的方式,但不要很快地跳过而直接采用乒乓结对。你需要迫使你那个不感兴趣的 结对伙伴去编写大概 65% 的代码。不用花很长时间,一个不感兴趣的结对伙伴就能转变成一个 100% 起作用的结对伙伴了。这对大家都有好处。
检查语法的结对伙伴
智力上的鸿沟可能会导致一个伙伴除了做点语法检查以外其他什么都干不了。过去这些年里我着实见过很多次了,而且这最近也发生在了我身上。我接了个新 工作,在第二天上,我跟公司里最资深的一个工程师结对,把他从前给.net 写的一个库用 java 实现。我对这个领域很陌生。我从前也没做过真正专业的 java 开发。我从来没见到过这个库的.net 版本。我从来没用过跟这个库进行交互的通信系统。我从来没用过 IntelliJ 来编 java 程序。等等。
我对我的结对伙伴一点作用都起不上。这个任务还相当紧迫,我们之间的信息差异实在太大,所以除了指出点语法错误之外,我一直都保持安静。偶尔我也会敲键盘,但也是在结对伙伴告诉我要敲些啥的情况下。
虽然我一般都提倡结对,但是如果一个人除了语法建议以外什么忙都帮不上,那就该换个方式来使用他的时间。另外,如果一个有经验的伙伴被要求把速 度降下来去解释一些东西,那当前的任务就肯定要多花些时间才能完成。当经验上的差距能被弥补,这通常来说是最有效的学习。当然如果经验上的差距太大,以至 于没法弥补或者影响到了成功地交付,那么就应该拆散这对重新组合。
不该结对的时候
有几种情况下最好不要使用结对编程,包括一个结对成员的作用仅仅是检查语法。另外一些可能更合适你一个人去搞定的场景包括:
- 当读一个未来可能有帮助的代码库的时候
- 当你和一个跟你经验上差距太大的人一起专研一个复杂的解决方案或者查一个怪异的错误的时候。通常结对编程不适合既探索问题又带新人。
记住这些,很重要的,上述情况都不是进行结对的最好时机。但这不意味着你永远不应该在这些情况下去结对。当然你如果正在结对做这些事情,你应该 意识到你的结对伙伴可能不感兴趣或者不能产生价值。要知道一种快速的阻碍团队引入结对编程的方法就是让一些人去做些不产生价值的事情。
不是每个任务都需要一个结对伙伴的。当然,需要结对的任务的数量远远多于不需要的。
Dan North 在审阅这篇文章的时候指出:可能不是任务的数量,而是任务的种类。任何跟交付直接相关的开发活动(编写代码,编写测试,构建)在做了结对后往往 都有更好的质量。至于那些跟交付间接相关的,比如研究,调查和行政琐事,如果用结对,那么回报率就小了很多。所以通常不这么做。
核心结对时段(Core Pairing Hours)
平均而言,你如果觉得结对不是最适合你当前的问题,那么就立刻停止结对。然而,我通过定义每天你有多少小时预留着做结对编程(核心结对时段)获得了 不少成功的例子。典型的,核心结对时段是你工作时间的 60% 到 70%。举个例子,如果你从早上 8 点开始工作到下午 5 点,那么你的核心结对时段可能从早上 10 点到下午 4 点。
在核心结对时段,你不是被要求一定要结对工作(你永远不应该“被要求一定”结对)。但是你应该腾出时间,做好结对的准备。如果你正在做的事情由 你一个人来做更好,那就别去结对。当然,如果你没在结对,但结对的话更有益,那还犹豫什么呢,或者让你自己准备好去跟别的需要结对的人一起工作。可能的 话,把一些适合一个人完成的任务拖到核心结对时段结束以后去做,也会有帮助。举个例子,你在下午 2 点被分派到了一个中等优先级的错误,你的结对伙伴对这个 错误所用到的库一无所知,那么你等到 4 点以后再去看这个问题可能更加合适一点。
所以说核心结对时段非常不错,它让你的工作时间有足够灵活性。
人们反对结对的首要原因是人们“需要自己的时间”,不可能“每天结对 8 小时”。这个反对意见我总觉得很有意思,因为我不认为大家每天就得结对 8 小 时,我认为每个人都有资格掌握他们自己的时间。我个人就倾向于每天 70% 的时间做结对。我有些朋友倾向于 60%,另外些 95%。我不确定谁是对的,但没人 是 100% 的。特别是刚刚引入结对的阶段,很重要的一点就是,一旦觉得结对有益,那么就结对,反之就停止。
做可能管用的最简单的事
“做可能管用的最简单的事”是一句被大多数敏捷开发人员常常挂在嘴边的话,但想做到却不容易。如果你做得实在太简单了,你会被批评为目光短浅。然而 如果你做得太复杂,你又会被说成浪费了你自己的时间和那些需要维护这个方案的人的时间。当然,结对编程能帮助你,让你对你的方案更有信心:简单但足够管 用。
在结对过程中,你要有恒心,坚持做可能管用的最简单的事情。如果你正在结对,你有可能正在实现一个能够满足绝大部分使用要求的优雅的方案。通过坚持做最简单的方案,你能更快地完成功能,更快地维护现有功能。这个巨大的进步会帮助你推广你的想法:使用结对编程非常棒。
强制执行代码审查
如果你想提高代码质量,就该找团队成员来审查代码。实际上,有些制度(SOX,HIPPA)要求所有产品代码都得经过审查。要是你打算推行结对编 程,而且又有一颗聪明的大脑,认识到强制执行结对很容易失败,那就不妨强制执行代码审查。在审查的时候,既可以采取传统方式,又可以作结对。结对和代码审 查之间的主要差别是,后者是评判,并且表示出某人对代码的所有权,同时令人滋生防备心理;但前者则是协作。自己的代码被别人审查通常都是个很痛苦的过程, 用不了多久大家就会换成结对的。
强制做任何工作都是有风险的,我一般把它作为下下策。
结论
被人“允许”结对不会取得成功。要想成功的话,就要让团队享受结对的乐趣,并且从中获得生产力的提升。如果你从这个角度来实施结对的话,成功的概率就会高很多。所以,在你把上面的想法付诸实践的时候,请注重一下团队建设和效率。
阅读英文原文: Successfully Adopting Pair Programming 。
参与本文翻译的还有李剑。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。
评论