结对编程
如今,许多从事软件开发工作的人都听说过结对编程这种实践,但它在业界仍然只是零星地被采用。它被接受程度不同的一个原因是,它的好处并不是立竿见影的,它更多的是在中长期内得到回报。而且它也不像“两个人在一台电脑上工作”那么简单,所以很多人在感到不舒服的时候会很快放弃它。然而,根据我们的经验,结对编程对于团队合作和高质量的软件至关重要。
2020 年 1 月 15 日
贝蒂·斯奈德和我,从一开始就是一对。我相信最好的程序和设计都是由两个人完成的,因为你们可以互相批评,发现彼此的错误,并采用最好的想法。
所有生产程序都应该由两个人坐在一台机器上编写。
-- 肯特·贝克
简·巴蒂克是ENIAC 女士中的一员,她们被许多人认为是最早的程序员。当“程序”这个词还没有被使用的时候,她们就承担起了编程的任务,而且当时也没有任何榜样或书籍可以告诉她们如何做——她们认为结对工作是个好主意。结对编程成为一个广泛使用的术语,又花了大约 50 年的时间,直到 20 世纪 90 年代末,肯特·贝克在他的《极限编程》一书中描述了这个术语。这本书向更广泛的受众介绍了敏捷软件开发实践,结对编程就是其中之一。
结对编程本质上意味着两个人在一台机器上一起编写代码。这是一种非常协作的工作方式,涉及大量的沟通。当一对开发人员一起完成一项任务时,他们不仅要编写代码,还要计划和讨论他们的工作。他们会在此过程中澄清想法,讨论方法,并找到更好的解决方案。
本文的第一部分,“如何结对”,概述了结对编程的不同实用方法。这部分内容适合那些希望开始结对编程或希望在这方面做得更好的人阅读。
第二部分和第三部分,“好处”和“挑战”,更深入地探讨了结对编程的目标是什么,以及如何应对阻碍我们实现这些目标的挑战。如果你想更好地理解为什么结对编程对你的软件和团队有好处,或者你想知道如何改进,那么这部分内容适合你。
第四部分和第五部分,“结对还是不结对?”和“但是说真的,为什么要费心呢?”,将总结我们对团队流程和协作大局中结对编程的看法。
如何结对
风格
乒乓球
这种技术采用了测试驱动开发(TDD),当你有一个可以通过测试驱动的方式实现的明确定义的任务时,这种技术就非常完美。
- “乒乓”:开发人员 A 编写一个失败的测试
- “乓”:开发人员 B 编写实现代码使其通过。
- 然后,开发人员 B 开始下一个“乒乓”,即下一个失败的测试。
- 在进行下一个失败的测试之前,每个“乓”之后还可以对代码进行重构。这样你就遵循了“红-绿-重构”的方法:编写一个失败的测试(红色),用最少的必要手段使其通过(绿色),然后重构。
强力结对
这是一种对知识转移特别有用的技术,Llewellyn Falco 在这里对此进行了更详细的描述。
规则:“要想让一个想法从你的脑袋里进入电脑,它必须经过别人的手”。在这种风格中,领航员通常是更有经验的人,而驾驶员则是新手(语言、工具、代码库等)。有经验的人大多扮演领航员的角色,指导新手。
这其中一个重要方面是,驾驶员完全信任领航员,并且应该“对不完整的理解感到自在”。“为什么”的问题以及对解决方案的挑战应该在实施会议之后再讨论。在一个人是完全新手的情况下,这可以使结对编程更加有效。
虽然这种技术近乎于微观管理,但它可以成为一种有用的入门工具,有利于积极的“边做边学”,而不是被动的“边看边学”。这种风格非常适合最初的知识转移,但不宜过度使用。请记住,目标是在一段时间后能够轻松地切换角色,并轻松地退出微观管理模式。这将是知识转移成功的标志。
结对开发
“结对开发”与其说是一种特定的结对技术,不如说是一种关于结对的思维方式。(我们第一次接触到这个词是在 Sarah Mei 的推特账号上的这条推文中。)开发一个用户故事或一个功能通常不仅需要编码,还需要许多其他任务。作为一对搭档,你要对所有这些事情负责。
为了帮助你进入这种思维方式,以下是一些在故事生命周期中非编码活动中受益于结对的例子。
计划——我们的目标是什么?
当你第一次开始一起做某件事时,不要立即跳到编码阶段。功能生命周期的早期阶段是避免浪费的好机会。在这么早就用四只眼睛盯着问题,发现误解或遗漏的先决条件可以为你以后节省大量时间。
- 理解问题:通读故事,并互相回放你对故事的理解。与产品负责人一起澄清未解决的问题或潜在的误解。如果你的团队中有完成的定义,请再次仔细阅读,确保你拥有开始工作所需的一切。
- 想出一个解决方案:集思广益,找出解决问题的潜在方案。你可以一起做这件事,也可以分开做,然后互相展示你的想法。这取决于解决方案的定义程度,也取决于你个人的风格。有些人喜欢自己思考一段时间,有些人喜欢在思考的时候大声说出来。如果你们中有人对领域或技术不太熟悉,那就花点时间互相分享必要的背景信息。
- 计划你的方法:对于你选择的解决方案,你需要采取哪些步骤来实现它?是否有特定的任务顺序需要牢记?你将如何测试它?理想情况下,把这些步骤写下来,写在一个共享文档中或便利贴上。这将帮助你跟踪你的进度,或者当你需要其他人来帮助你完成任务时。把这些写下来也有助于记住需要做的事情——在当下,我们常常低估了自己即使在第二天也会忘记多少事情......
研究和探索
当实现一个功能需要你使用你们都不熟悉的技术时,你需要先做一些研究和探索。这项工作不适合整齐划一的“驾驶员-领航员”或“乒乓球”方法。例如,一起在同一个屏幕上浏览搜索引擎结果通常不是很有效。
以下是一种在结对开发模式下解决这个问题的方法
- 列出你需要回答的问题清单,以便想出一个合适的解决方案。
- 分开行动——要么在你们之间分配问题,要么尝试分别找到相同问题的答案。在互联网或组织内的其他资源中搜索答案,或者阅读你们都不熟悉的新概念。
- 在事先约定的时间结束后,重新聚在一起,讨论和分享你们的发现。
文档
除了代码之外,另一个需要共同努力的事情是文档。一起反思一下,你们所做的工作是否需要任何文档。同样,根据具体情况和个人喜好,你们可以一起创建文档,或者由一个人创建,然后另一个人审查和润色。
文档是一个很好的例子,说明结对编程可以使彼此保持自律。它通常是一项留到最后才做的任务,当它是阻碍我们获得将故事纳入“完成”的成就感的最后一件事时,我们往往会跳过它,或者“即兴发挥”。结对工作让我们对一些有价值但又烦人的事情保持诚实,而这些事情在未来我们会非常感激。
时间管理
除了结对编程的一般风格之外,还有一些其他的小工具和技巧可以让它变得更容易。
番茄工作法就是其中之一。这是一种时间管理方法,将工作分解成若干时间段(传统上是 25 分钟),中间穿插着短暂的休息。该技术几乎可以应用于所有描述的结对编程方法,并将使你保持专注。结对编程可能是一种令人疲惫的做法,因此,提醒自己休息一下并定期交换键盘是很有帮助的。
下面是一个使用番茄工作法的实际例子。
- 决定下一步要做什么
- 设置一个 25 分钟的计时器,例如,借助许多番茄工作法浏览器扩展程序,甚至是一个真实的番茄形厨房计时器……
- 不受干扰地做一些工作
- 当计时器响起时暂停工作 - 从短暂的休息开始(5-10 分钟)
- 在 3 或 4 个这样的“番茄钟”之后,进行一次较长的休息(15-30 分钟)
- 利用短暂的休息时间*真正地*休息一下,补充能量,喝点水或咖啡,上厕所,呼吸一下新鲜空气。避免利用这些短暂的休息时间做其他工作,比如写电子邮件。
结对轮换
轮换搭档意味着在合作一段时间后,搭档中的一方离开故事,而另一个人则加入新人继续。留下的人通常被称为故事的“锚”。
轮换的原因之一是后勤方面的原因。你的搭档可能会生病或休假。或者你们中的一个人有一天要远程工作,而这项工作需要在现场进行,例如,因为涉及到硬件设置。
轮换的另一组原因是为了改变现状。要么是你们两个人在一起工作了一段时间,并且开始出现“幽居病”的迹象,因为你们在一起的时间太长了。要么是你们正在做一些非常乏味和消耗精力的事情——轮换可以让你们中的一个人得到休息,而新人可以带来一些新的视角和能量。
最后,轮换搭档最常见的原因是为了避免知识孤岛,增加集体代码所有权,并在进行中获得更多代码审查。结对编程本身已经有助于实现这些目标,但轮换可以进一步增加投入生产的每一行代码的平均查看次数。
至于轮换的理想频率,这是意见分歧的地方。有些人认为,每 2-3 天轮换一次对于确保充分的知识传播和质量至关重要。然而,每一次轮换都会带来一些成本。其中包括让新人熟悉情况的时间,以及两人中的一人进行环境切换的成本。如果没有持续的锚来保持连续性,则会增加关于问题和解决方案空间的隐性知识丢失并引发返工的风险。对于资历较浅的开发人员来说,有时在某件事上停留更长时间更有益,这样他们就有足够的时间沉浸在一个主题中,并给新知识留出沉淀的时间。
想想这些成本和收益之间的权衡。例如,假设你已经拥有高质量的知识共享,包括团队“展示和讲述”、可读代码和良好的文档。在这种情况下,也许坚持频繁轮换只会略微改善你的集体代码所有权,同时造成大量的摩擦和开销。
计划一天
结对编程需要一定程度的日程安排和日历协调。如果你不花时间承认并适应这一点,它会在当天晚些时候回来困扰你。
从查看日历开始新的一天 - 与你的搭档就你们要结对编程多少小时达成一致,并看看你是否需要围绕会议或在结对编程任务之外处理其他事情所需的时间进行计划。如果事实证明,你们中的一个人必须独自工作一段时间,那么请确保做好准备,以便在没有对方的情况下继续工作,例如,不要使用对方的电脑编写代码。
如果你在白天有会议或其他承诺,请确保你有一个提醒,你会注意到,尤其是在你的搭档的机器上工作时。如果你的团队默认情况下是结对编程,请考虑为每个人商定定期的“核心编码时间”。这使得日程安排更容易。
物理设置
结对编程意味着你需要在一个共享办公桌的物理空间里非常密切地合作。这与拥有自己的桌子可以铺开东西大不相同。彼此如此亲近需要一定程度的尊重和关注对方的需要。这就是为什么值得花些时间为你们俩找到一个舒适的设置。
- 确保你们俩都有足够的空间,必要时清理桌面。
- 桌子前面是否有足够的空间容纳两把椅子?把垃圾桶和背包都拿开。
- 你想用两个键盘还是一个?鼠标也是,一个还是两个?没有明确的规则总是有效,我们建议你尝试一下哪种方法最适合每种情况。影响这一点的一些因素包括卫生、你分享键盘时间的熟练程度,或者你有多大的可用空间。
- 你是否有可用的外接显示器,甚至两个?如果没有,你也可以考虑设置某种屏幕共享,就像你在远程结对编程一样。在这种设置中,你们每个人都将使用自己的笔记本电脑键盘。
- 与你的搭档确认他们是否有任何特定的偏好或需求(例如,更大的字体大小、更高的对比度……)。
- 如果你有一个不寻常的键盘/IDE 设置,请与你的搭档确认他们是否可以接受。看看你是否可以有一个简单的机制,在这些情况下将你的设置切换回更标准的配置。
如果你的团队能够就默认设置达成一致,那就更好了,这样你就不用一次又一次地讨论这些事情了。
远程结对
你是分布式团队的一员,还是有些团队成员偶尔在家工作?你仍然可以练习结对编程,只要你们俩都有相当稳定的互联网接入。
设置
对于远程结对编程,你需要一个屏幕共享解决方案,它不仅允许你查看,还可以控制对方的机器,这样你就可以切换键盘。如今,许多视频会议工具已经支持这一点,因此,如果你在一家拥有商业 VC 工具许可证的公司工作,请先尝试一下。也有一些用于远程控制视频通话的开源工具,例如 jitsi。对于在较低带宽下工作的解决方案,请尝试使用 ssh 和 tmux 或 Visual Studio Code 的 Live Share 扩展。
提示
- **使用视频:**由于人们通过手势和面部表情进行大量交流,因此能够同时看到共享屏幕和你的搭档的视频是很好的。一些视频会议解决方案自带此功能;如果你的解决方案没有,请考虑打开另一个通话以查看对方。
- **计划和设计:**使用协作在线可视化工具,重现纸上或白板上勾勒事物的体验。
- **音频体验:**寻找一个安静的区域,并使用良好的耳机,甚至可以使用定向麦克风。如果你无法摆脱噪音,“按下说话”功能也会有所帮助。为了避免你身边的干扰,降噪耳机是你的朋友。
- 处理网络延迟:当网络延迟时,在远程计算机上长时间工作可能会很累。因此,请确保定期更换计算机,以便你们每个人都有机会在自己的机器上工作而不会出现延迟。当你滚动浏览文件时,网络延迟也会很烦人,因为它很难跟上。避免在长文件中滚动会有所帮助,尝试使用键盘快捷键打开文件的不同部分,或者使用折叠/展开功能。
人为因素
如果你在一个并非整个团队都是分布式的环境中工作,而只有你一个人或几个人是远程的,请尽量让远程伙伴参与到办公室里发生的所有讨论中。我们往往会忘记,仅仅因为坐在同一个房间里,我们就无意中分享了多少东西。
与你从未见过面也不认识的人远程工作会带来额外的挑战。一方面,结对编程是一个在远程团队中彼此更加亲近的机会。另一方面,有时很容易忘记合作的那一部分。如果没有机会见面,可以想想其他方法来更好地了解彼此,例如,试着一起远程喝杯咖啡。
最后,虽然远程结对编程有其挑战,但它也比现场结对编程更容易集中注意力,因为戴上耳机更容易屏蔽干扰。
一起吃甜甜圈
当你和搭档一起完成一项任务时,要庆祝一下!互相击掌可能看起来很老套,但这实际上是你们可以一起做的一个小小的“力量姿势”,它可以让你充满活力,为下一个任务做好准备。或者,你也可以创造自己的庆祝成功的方式,比如 Lara Hogan,她用一个甜甜圈来庆祝职业成就。
要避免的事情
不同的方法和技巧可以帮助你获得更好的结对编程体验。以下是一些要避免的常见陷阱
渐行渐远
当你结对编程时,避免阅读电子邮件或使用手机。这些干扰可能会让你的搭档觉得你不尊重他,而且它们会分散你对正在进行的任务的注意力。如果你真的需要查看一些东西,请明确说明你在做什么,以及为什么。确保每个人都有足够的时间阅读他们的电子邮件,方法是进行足够的休息,并每天留出一些个人时间。
微观管理模式
警惕微观管理模式:它没有给对方留下思考的空间,而且如果有人不断给你指示,比如
- “现在输入‘System,点,print,“……”
- “现在我们需要创建一个名为……的新类”
- “按 command shift O……”
不耐烦
应用“5 秒规则”:当领航员看到驾驶员做错了什么并想发表评论时,至少等 5 秒钟再说些什么——驾驶员可能已经想到了,那么你就是在不必要地打断他们的思路。
作为领航员,不要立即指出任何错误或即将出现的障碍:等一下,让驾驶员纠正或写一张便利贴,以便稍后记住。如果你立即干预,可能会扰乱驾驶员的思维过程。
键盘霸占
如果你在“霸占键盘”,就要小心了:你是否一直控制着键盘,不让你的结对伙伴也打字?
这对你的搭档来说可能是一次非常恼人的经历,并且可能会因为“积极参与”有限而导致他们难以集中注意力。尝试前面描述的方法之一,以确保你经常切换键盘。
每天结对编程 8 小时
真正致力于使结对编程发挥作用的团队有时最终会每天结对编程 8 个小时。根据我们的经验,这是不可持续的。首先,这太累了。其次,这在实践中甚至行不通,因为除了编码之外,你还有很多其他事情要做,例如查看电子邮件、进行一对一会议、参加会议、研究/学习。因此,在计划你的一天时要记住这一点,不要认为 100% 的时间都在一起编码。
没有“唯一”的正确方法
结对编程的方法有很多,没有“一种”正确的方法。这取决于你的风格、个性、经验、情况、任务和许多其他因素。最后,最重要的问题是:你是否从中获得了承诺的好处?如果不是,请尝试其他方法,反思、讨论和调整以获得它们。
好处
结对编程有什么好处?了解其所有潜在好处对于决定何时进行、如何做好以及在充满挑战的时期激励自己进行至关重要。结对可以帮助你实现的主要目标是软件质量和团队流畅性。
那么结对如何帮助你实现这些目标呢?
知识共享
让我们从结对最明显和最少争议的好处开始:知识共享。让两个人处理一段代码有助于团队每天传播技术和领域知识,并防止知识孤岛。此外,当两个人理解和讨论一个问题时,我们找到好的解决方案的机会就会增加。不同的经验和观点将导致考虑更多的选择。
实用技巧
当你对所涉及的技术或领域一无所知时,不要回避结对处理任务。如果你一直在你感觉最舒服的领域工作,你将错过学习新事物的机会,最终无法在你的团队中传播知识。
如果你注意到某个团队成员倾向于一直处理相同的主题,请要求他们将其混合起来以传播他们的专业知识。创建团队技术和业务主题以及每个人在每个领域的优势和差距的技能矩阵也很有帮助。如果你把它放在团队区域的墙上,你们可以一起努力,以获得良好的知识传播。
反思
结对编程迫使我们讨论方法和解决方案,而不是仅仅在自己的脑海中思考它们。大声说出和解释事物会促使我们反思我们是否真的理解正确,或者我们是否真的有一个好的解决方案。这不仅适用于代码和技术设计,也适用于用户故事和故事带来的价值。
实用技巧
它需要你们双方之间的信任,以创造一种氛围,让你们双方都可以自由地提出问题,并公开谈论你们不理解的事情。这就是为什么当你结对时,建立团队内部的关系变得更加重要。花时间进行定期的 1:1 和反馈会议。
保持专注
当你们两个人在一起时,更容易采用结构化的方法:你们每个人都必须明确地说明为什么要做某件事以及你的目标是什么。当单独工作时,你更容易分心,例如“只是快速地”尝试不同的方法而不加思考,然后几个小时后才从兔子洞里出来。你的结对伙伴可以阻止你进入这些兔子洞,并专注于完成你的任务或故事的重要事项。你们可以互相帮助保持正轨。
实用技巧
一起制定计划!讨论你手头的任务,并考虑你需要采取哪些步骤来实现你的目标。将每个步骤写在便利贴上(或者,如果是远程的,则写在你的票务管理系统的子任务中),按顺序排列并逐一进行。尝试将其与番茄工作法结合使用,并尝试在一个番茄时间内完成其中一个目标。
永远不要忘记沟通是关键。谈论你正在做什么,并要求对方解释。
实时代码审查
当我们结对时,我们有 4 只眼睛盯着大小事情,因为我们在进行过程中会发现更多错误,而不是在我们完成后才发现。
代码的重构始终是编码的一部分,因此也是结对编程的一部分。当你身边有人时,改进代码会更容易,因为例如,你可以讨论方法或事物的命名。
事后进行代码审查有一些缺点。我们将在后面的“结对还是不结对?”中更详细地讨论这一点。
实用技巧
互相提问!问题是理解你正在做什么并找到更好解决方案的最强大的工具。如果你们中的一方不容易阅读和理解代码,请尝试一种更容易理解的方式。
如果你觉得需要对结对编程的代码进行更多代码审查,请反思你是否可以改进你的结对方式。你在结对编程期间是否能够提出所有问题和疑虑?为什么?你需要改变什么?
两种思维模式的结合
正如我们之前在描述经典的驾驶员/领航员风格时提到的,结对编程允许你从不同的角度看待代码。驾驶员的大脑通常处于“战术”模式,思考细节,当前的代码行。同时,领航员的大脑可以更具战略性地思考,考虑全局,将后续步骤和想法停放在便利贴上。一个人可以结合这两种思维模式吗?可能不行!将战术和战略观点结合起来将提高你的代码质量,因为它将允许你在关注细节的同时牢记全局。
实用技巧
记住要定期切换键盘,从而切换角色。这有助于你保持清醒,避免感到无聊,并练习两种思维方式。
作为领航员,要避免“战术”思维模式,将编码的细节留给驾驶员——你的工作是退后一步,用中期思维来补充你搭档的战术模式。
集体代码所有制
集体代码所有权放弃了对模块的任何个人所有权的概念。代码库由整个团队拥有,任何人都可以对任何地方进行更改。
持续的结对编程确保了每行代码至少有 2 个人接触或看到过。这增加了团队中任何人都可以放心地更改几乎任何地方的代码的可能性。这也使得代码库比只有单个编码器时更加一致。
实用技巧
仅靠结对编程并不能保证你实现集体代码所有权。你需要确保你还轮换不同人员到不同的结对和代码区域,以防止知识孤岛。
保持团队的 WIP 低
限制在制品是看板的核心原则之一,旨在改善团队流程。拥有在制品 (WIP) 限制有助于你的团队专注于最重要的任务。如果团队有适当的在制品限制,那么整体团队生产力通常会提高,因为多任务处理不仅对个人效率低下,对团队层面也是如此。特别是在较大的团队中,结对编程限制了团队可以并行处理的事情的数量,因此提高了整体关注度。这将确保工作持续流动,并立即解决障碍。
实用技巧
将你的团队的在制品限制在团队中开发人员对的数量,并使其在你的团队空间中可见(或者,如果你远程工作,则在你的在线项目管理工具中可见)。在开始新任务之前,请注意限制。在制品限制原则可能会自然而然地迫使你养成结对编程的习惯。
新团队成员快速融入
由于结对编程促进了知识共享,因此它可以帮助新团队成员融入团队。新加入者可以在其搭档的帮助下了解项目、业务和组织。团队中的变化会影响团队流程。人们只是需要一些时间来互相了解。结对编程可以帮助最大限度地减少这种影响,因为它迫使人们比单独工作时进行更多沟通。
实用技巧
仅仅把新加入者安排到一个结对中,然后他们就“神奇地”融入并加入了团队,这是不够的。确保在他们第一次结对编程之前提供全局和更广泛的背景,并为融入团队预留一些额外的时间。这将使他们更容易在结对编程期间跟随并做出贡献,并从中获得最大收益。结对编程时,始终使用新加入者的机器,以确保他们也准备好 samodzielnie 工作。
制定一个包含要涵盖的主题列表的融入计划。对于某些主题,你可能希望安排专门的会议,而对于其他主题,新团队成员可以随身携带融入计划,从一个结对到另一个结对。如果在结对编程期间涵盖了某些内容,你可以将其从列表中勾选掉。这样一来,团队中的每个人都可以看到融入进度。
挑战
虽然结对编程有很多好处,但它也需要练习,并且可能不会从一开始就很顺利。以下列出了一些团队遇到的常见挑战,以及一些关于如何应对这些挑战的建议。当你遇到这些挑战时,请牢记这些好处,并记住你为什么要结对编程。了解你想通过实践实现什么非常重要,这样你就可以调整你的做事方式。
结对编程可能会很累
当你独自工作时,你可以随时休息,你的大脑可以在需要的时候放松或关闭一段时间。结对编程迫使你在可能更长的时间内保持专注,并找到与另一个人的节奏和思维方式的共同点。更加专注是结对编程的好处之一,但也可能使其变得非常紧张和疲惫。
应对方法
充足的休息是应对这一挑战的关键。如果你注意到你忘记了定期休息,请尝试使用闹钟安排休息时间,例如每小时 10 分钟。或者使用像番茄工作法这样的时间管理技巧。不要跳过午餐休息时间:离开显示器,真正休息一下。无论是否结对编程,休息都很重要,而且可以提高工作效率。
防止疲惫的另一个重要方法是不要每天结对编程 8 个小时。将其限制为每天最多 6 个小时。定期从驾驶员角色切换到领航员角色也有助于保持精力充沛。
密切合作可能很困难
长时间与另一个人如此密切地合作是十分紧张的。你需要不断沟通,这需要同理心和人际交往技巧。
你可能在技术、知识、技能、外向性、个性或解决问题的方法方面存在差异。这些因素的某些组合可能不太匹配,并给你一个艰难的开始。在这种情况下,你需要投入一些时间来改善协作,并使其成为一种相互学习的体验,而不是一场斗争。
应对方法
在结对编程开始时进行对话可以帮助你了解你们风格之间的差异,并计划适应这种差异。以“我们想如何合作?”、“你喜欢如何结对编程?”之类的问题开始你的第一次会议。了解你喜欢如何工作以及你如何高效,但也不要排斥其他方法——也许你会发现新的东西。
在一天的结对编程结束时,互相进行一轮反馈。如果提供反馈的想法让你感到害怕,可以将其更多地视为一次小型回顾。反思一下你在结对编程期间的感受。你是否保持警觉?你累了吗?是什么让你感到舒服,什么让你感到不舒服?你是否经常 enough 切换键盘?你是否实现了你的目标?你下次想尝试什么吗?最好尽早养成这种习惯,这样在出现问题时你就有提供反馈的经验。
有很多很棒的培训和书籍可以帮助您处理人际冲突和困难,例如关于如何进行困难对话的书籍。
将挑战视为团队共同面对,不要将冲突留给个人。例如,您可以组织一个关于结对编程的会议,在会议中讨论如何共同处理困难。在会议开始时,先收集结对编程的好处,以便您了解每个人想要从中得到什么。之后,收集每个人在结对编程时遇到的挑战。现在,团队可以思考哪些行动可能有助于改进。您还可以收集团队成员的“热点触发器”:在结对编程时,是什么让您立即感到不舒服?
会议中断
您是否经历过整天开会,却感觉什么也没做成?这可能发生在每个交付团队中。会议对于讨论、计划和商定要构建的内容是必要的,但另一方面,它们也会打断工作流程。当团队实行结对编程时,过多会议的影响会变得更加严重。如果结对的每个人都在不同的时间开会,那么中断就会成倍增加。
应对方法
一种方法是限制会议的时间段,例如,通过定义没有会议的核心结对时间,或者通过“下午之后不开会”之类的规则来屏蔽不开会的时间。
同样值得思考的是会议时长和总体数量。您真正需要哪些会议?它们的目的是什么?如何提高会议质量,例如做好准备工作、安排主持人和制定明确的议程。
但有一件事是肯定的:会议总是会有的。那么,作为一对搭档,如何处理这个问题呢?在结对编程开始时,一起查看你们的日历,确保你们有足够的时间开始结对。如果有任何会议,请考虑一起参加。依靠您的产品负责人或其他非结对团队成员,在核心结对时间内将干扰挡在团队之外。
不同的技能水平
当两个经验水平不同的人就一个主题进行结对编程时,这通常会导致对彼此贡献程度的错误假设,或者因为进度差异而感到沮丧。
应对方法
如果您的搭档在该主题上经验更丰富:不要认为他们什么都知道。也许需要解释他们为什么以这种方式做事会给他们带来新的见解。询问如何做以及为什么这样做的问题,可以引发富有成效的讨论并找到更好的解决方案。
如果您的搭档在该主题上经验较少:不要认为他们对解决方案的贡献不大。您可能被困在自己的思维定势中,而不同的观点可以帮助您找到更好的解决方案。此外,请记住,必须解释一个概念是检验您是否真正理解它并彻底思考它的绝佳机会。
了解不同的学习阶段也有助于理解从新手到专家的学习过程是如何运作的。Dan North 在他的演讲“高效团队的模式”中很好地描述了这一点。他介绍了“德雷福斯技能获取模型”作为理解不同学习阶段的一种方式,以及在结对编程的背景下,将它们结合起来意味着什么。
权力动态
处理权力动态可能是这份清单中最困难的挑战之一。结对编程并非发生在一个没有等级制度的空间里。存在着正式的等级制度,例如经理和下属之间,以及非正式的等级制度。非正式等级制度的例子有:
- 初级 - 高级
- 非男性 - 男性
- 职业转换者 - 拥有计算机科学学位的人
- 有色人种 - 白人
这些只是一些例子。权力动态是流动和交叉的。当两个人结对时,这些动态中的多个可能会同时发挥作用并相互重叠。为了了解权力失衡如何影响结对编程,以下是一些例子。
- 一个人通过霸占键盘而不给搭档留出空间来主导结对编程环节。
- 一个人始终保持着教学的立场和态度。
- 一个人不听另一个人说话,并无视他们的建议。
有时,将这些情况与等级制度联系起来可能很微妙,您常常只是认为你们彼此合不来。但潜在的问题往往是受结对双方之间不平衡的影响。
Sarah Mei 就这个主题写了一系列精彩的推文,并且还发表了一个更广泛地涵盖敏捷中的权力动态的演讲。
应对方法
解决这个问题的第一步是,处于权力动态上风的人要承认并承认自己的地位。只有这样,您才能诚实地反思您与搭档之间的互动,以及权力动态如何影响他们。试着想想您自己的立场和处境。您可以积极做些什么来消除权力失衡?
认识到这些类型的差异并调整我们的行为以改善协作可能很困难。这需要大量的自我反省。有一些培训可以帮助个人或团队做到这一点,例如“反偏见”或盟友技能培训。
与许多未知因素配对
当您处理一个你们俩都不知道如何解决的大问题时,通常的结对编程风格往往就不那么有效了。假设您需要首次使用一项技术,或者尝试一种新的方法或模式。在某些情况下,一起研究和实验是可行的,但它也可能令人沮丧,因为我们每个人都有不同的方法来弄清楚事情是如何运作的,而且我们阅读和学习的速度也不同。
应对方法
当存在很多未知因素时,例如,您正在使用一项新技术,请考虑进行技术预研来探索该主题并在实际开始工作之前了解该技术。不要忘记与团队分享您的发现,也许您可以举行一次知识交流会议,并绘制一些可以张贴在团队空间的图表。
在这种情况下,请记住要采取结对开发的心态,而不是结对*编程*。分开进行研究是可以的,也许是在就需要共同回答的一系列问题达成一致之后。
没有时间给自己
我们已经讨论过,持续不断地交谈会让人非常疲惫。大多数人一整天也需要一些独处的时间。对于更内向的人来说尤其如此。
当我们独自工作时,我们很自然会在需要的时候花时间深入研究一个主题或学习。但这在结对编程中可能会让人感觉被打断。那么,如何在需要的时候获得独处和学习的时间呢?
应对方法
同样,不要每天结对编程 8 个小时,与您的团队商定核心编码时间,并将其保持在每天最多 6 个小时。也许您还想分配几个小时的自我学习时间。
当一对搭档觉得他们没有足够的集体知识来解决问题时,就分开阅读并分享,然后再继续实施。
轮换会导致上下文切换
知识共享是结对编程的好处之一,我们已经提到轮换如何能进一步增强这种效果。另一方面,过多的轮换会导致频繁的上下文切换。
应对方法
在轮换频率和新搭档获得足够的故事背景并做出适当贡献的可能性之间找到平衡。不要为了轮换而轮换,要考虑是否以及为什么共享某个上下文很重要,并给它足够的时间来发挥作用。
结对编程需要坦诚相待
结对编程需要脆弱性。这意味着分享你所知道的一切和你所不知道的一切。这对我们来说很难。程序员应该很聪明,非常非常聪明。大多数人看着我们做的事情,会说“我永远做不到”。这让我们觉得自己有点特别,给了我们一种自豪感,而自豪感会让人变得刀枪不入。
-- Tom Howlett
当你结对编程时,你很难表现出你不知道什么,或者对某个决定感到不安全。尤其是在一个像10 倍工程师这样的神话经常流传的行业,以及我们倾向于根据我们使用的语言或 5 年前做出的设计决定来评判彼此的行业。
脆弱性常常与软弱联系在一起,在大多数现代文化中,表现出力量才是常态。但正如研究员布琳妮·布朗在几次演讲和书籍中所阐述的那样,脆弱性实际上是创新和变革的一个非常重要的因素。
脆弱性是创新、创造力和变革的诞生地。
-- 布琳妮·布朗
应对方法
表现出脆弱性需要勇气,并创造一个让人们感到更安全的环境来展现他们的脆弱性。同样,这都是关于建立一个人们彼此信任的团队(定期的 1:1 会议、反馈、人们可以提问的文化等等)。
对于团队中拥有更多权威的人来说,无论是天生拥有(例如,因为他们已经备受尊敬),还是制度赋予(例如,因为他们拥有“技术主管”这样的头衔),表现出脆弱性都更容易,风险也更低。因此,重要的是,这些人要带头树立榜样,使其成为常态,从而让其他人也能更安全地表现出脆弱性。
说服经理和同事
结对编程的倡导者往往难以说服他们的经理或同事将结对编程作为团队日常工作的一部分。
应对方法
没有一个简单的秘诀可以说服其他人相信结对编程的功效。然而,一个关键因素应该是始终花时间先讨论它,并确保每个人都有相同的理解(例如,通过阅读本文 :-))。然后,找到一种尝试的方法,要么从一对搭档开始,让他们与其他人分享他们的经验,要么提出一个团队实验,比如“让我们在接下来的 2 个冲刺中默认结对编程”。确保创造机会进行反馈和回顾,以分享进展顺利的地方和遇到的困难。
最终,你不能强迫人们接受一种做法,而且它也不适合所有人。你最终可能只与团队中的一部分人结对编程,至少在开始的时候是这样。根据我们的经验,说服人们的最佳方式是让他们定期接触这种做法,体验他们的团队成员在结对编程时获得的好处和乐趣。
在这种情况下,最常出现的一个问题是这种做法的经济效益:结对编程的成本是否是单人编程的两倍,最终,由于提高了质量和团队效益,额外的成本是否值得?有一些关于这个主题的研究,最著名的是这篇,它被引用来证明结对编程是值得的。然而,我们对试图“科学地证明”结对编程的有效性持谨慎态度。软件开发是一个充满变化和不确定性的过程,很多结果都超出了代码行,难以比较和衡量,比如分析、测试或质量。结对编程的坚定反对者总能找到方法,对任何旨在证明开发效率的“科学”实验的可重复性吹毛求疵。最终,你需要证明它对你是有效的,而做到这一点的唯一方法是在你的环境中进行尝试。
结对还是不结对
我们的经验清楚地表明,结对编程是以可持续的方式创建高质量、可维护软件的关键实践(参见“好处”)。然而,我们也不认为教条地对待它并*始终*结对编程是有帮助的。结对编程究竟如何对您有效,需要多少时间,以及适用于哪些任务,这些都是因人而异的。我们发现,将结对编程设置为团队的“默认选项”是有用的,然后在我们想要例外的时候再进行讨论。
让我们来看几个例子,说明在何时以及如何结对编程方面取得平衡是有帮助的。
无聊的任务
一些编码任务很“无聊”,例如,因为它们是关于使用一些定义明确的样板方法 - 所以也许你不需要结对编程?整个团队都已经知道这种方法,或者它很容易掌握,所以知识共享并不那么重要?而且实时代码审查也没那么有用,因为手头已经建立的模式在过去已经成功使用过?所以,是的,也许你不需要结对编程。
然而,始终要考虑到死记硬背的任务可能是设计不良的征兆:结对编程可以帮助你找到无聊代码的正确抽象。当你的大脑进入“这很容易”的自动驾驶状态时,也更容易错过东西或犯下粗心的错误。
“我真的可以自己做吗?”
结对编程对于刚开始编程的人来说有很多好处,因为它是一个从团队中更有经验的成员那里快速学习的机会。然而,初级程序员在结对编程时也可能会对自己能力失去信心。“如果没有人在我身后监督,我真的能做到吗?”。他们也错过了学习如何自己解决问题的机会。我们都经历过调试和错误分析的挫折和无人观察的实验,这些最终使我们成为更好的程序员。自己遇到问题通常比别人告诉我们我们会遇到问题更有效。
有几种方法可以抵消这种情况。一种是让初级程序员自己工作一段时间,由一位导师定期检查并进行一些代码审查。另一种方法是让团队中比较初级的程序员互相结对。他们可以一起寻找解决方案,并且仍然比他们自己编码时更快地从困境中走出来。最后,如果你是结对中更有经验的程序员,请确保大部分时间都坐在导航员的位置上。给驾驶员留出空间去解决问题 - 有时只是等待一段时间,直到你们一起撞到下一堵墙,而不是事先就指出它。
代码审查与结对编程
结对编程的优势在于其强大的即时性:当审阅者就坐在你旁边时,你不可能忽视他或她。
-- Jeff Atwood
许多人认为代码审查流程的存在足以说明不需要结对编程。我们不同意代码审查是结对编程的良好替代方案。
首先,通常有一些动态因素会导致草率或肤浅的代码审查。例如,当编码员和审阅者过于依赖对方而没有明确说明时:编码员可能会推迟一些小的决定和改进,认为问题会在审查中被发现。而审阅者则依赖于编码员的勤奋,信任他们的工作,而没有仔细查看代码。另一个起作用的动态因素是沉没成本谬误:我们通常不愿意对团队已经投入的东西进行返工。
其次,代码审查流程会扰乱团队的流程。接受审查任务需要某人进行上下文切换。因此,代码审查发生的频率越高,对审阅者造成的干扰就越大。而且它们应该经常发生,以确保小改动的持续集成。因此,审阅者可能会成为集成和部署的瓶颈,这增加了时间压力 - 同样,这会导致潜在的审查效率降低。
通过持续集成(和交付),我们希望通过频繁交付小块更改来降低风险。在其原始定义中,这意味着实践基于主干的开发。使用基于主干的开发,延迟的代码审查效率甚至更低,因为代码更改无论如何都会立即进入主分支。因此,结对编程和持续集成是两种相辅相成的实践。
我们看到团队有效使用的一种方法是默认情况下结对编程,但在特殊情况下,当有人必须在不结对编程的情况下更改生产代码时,使用拉取请求和代码审查。在这些设置中,你应该作为一个团队仔细监控你的拉取请求不要存在太长时间,以确保你仍然在实践持续集成。
但是说真的,为什么要费心呢?
我们谈了很多关于结对编程的好处,但更多的是关于它的挑战。结对编程需要很多不同的技能才能做好,甚至可能会影响团队中的其他流程。那么为什么要费心呢?真的值得这么麻烦吗?
为了让团队适应并成功地进行结对编程,他们必须努力掌握所有有助于克服其挑战的技能:专注力和注意力、任务组织、时间管理、沟通、给予和接受反馈、同理心、脆弱性 - 而这些实际上都是有助于成为一个运作良好、协作和高效团队的技能。结对编程让团队中的每个人都有机会一起锻炼这些技能。
如今,另一个被广泛谈论为高效团队成功因素的因素是多样性。观点、性别、背景和技能的多样性已被证明可以提高团队的绩效 - 但它通常会首先增加摩擦。它甚至会增加我们谈到的结对编程的一些挑战。例如,我们建议的关键要素之一是表现出脆弱性,这对代表性不足的群体中的团队成员来说尤其困难。
请看《哈佛商业评论》的这篇头条新闻:“多元化团队感觉不那么舒服 - 这就是他们表现更好的原因”。作者的观点是“同质化团队感觉更容易 - 但容易对绩效不利。(…) 这种想法与许多人的直觉相悖”。为了解释这一点,他们指出了一个被称为流畅性启发式的认知偏差:“我们更喜欢更容易处理的信息,并认为它更真实,或更美观。”
这种偏见使我们追求简单性,这在软件开发的许多情况下都对我们很有帮助。但我们认为在结对编程的情况下,这对我们没有好处。结对编程感觉很难 - 但这并不一定意味着它对团队不好。最重要的是,它不必一直都很难。在这个演讲中,Pia Nilsson 描述了她在 Spotify 的团队为克服最初引入结对编程等实践所造成的不舒服的摩擦而采取的措施。除此之外,她还提到了反馈文化、非暴力沟通、心理安全、谦逊和目标感。
结对编程、极限编程和敏捷软件开发作为一个整体都是关于拥抱变化的。敏捷软件从业者承认变化是不可避免的,因此他们希望为此做好准备。
我们建议,我们应该拥抱和准备的另一件事是摩擦,因为它在成为一个高效、多元化的团队的过程中也是不可避免的。拥抱摩擦,我们的意思并不是说,“让我们有很多冲突,我们会变得更好”。我们的意思是,团队应该为自己配备处理摩擦的必要工具,并在默认情况下将它们放在工具箱中,而不仅仅是在团队已经遇到问题时才这样做。练习反馈,改进团队沟通,采取措施创造一个心理安全的环境。
我们认为,结对编程经常被避免,因为它会造成摩擦,但我们希望你给它一个机会。如果你有意识地将其视为一项可以改进的技能,并努力改进它,你最终将拥有一个更有弹性的团队。
致谢
本文最初是 Thoughtworks 内部的一套幻灯片,我们对其进行了整理和众包,以分享有关结对编程实践的知识。在整个过程中,我们收到了来自这么多人的反馈和很好的建议,以至于很难说出每个人的名字,我们可能会忘记很多名字。因此,我们要感谢所有与我们讨论、向我们指出更多资源、在原始幻灯片上留下评论或审阅本文的人,特别是我们在 Thoughtworks 的同事。这真正成为了许多人经验的集合,谢谢大家!
特别感谢 Stefanie Grewenig 为本文创作了大部分插图。
重大修订
2020 年 1 月 15 日:发布