第8章 敏捷协作

    我不仅发挥了自己的全部能力,还将我所仰仗的人的能力发挥到极致。
    ——伍德罗·威尔逊,美国第28任总统(1856—1924)

    只要是具备一定规模的项目,就必然需要一个团队。靠单打独斗在车库里面开发出一个完整产品的日子早已不再。然而,在团队中工作与单兵作战,二者是完全不同的。一个人会突然发现,自己的行为会对团队以及整个项目的生产效率和进度产生影响。

    项目的成功与否,依赖于团队中的成员如何一起有效地工作,如何互动,如何管理他们的活动。全体成员的行动必须要与项目相关,反过来每个人的行为又会影响项目的环境。

    高效的协作是敏捷开发的基石,下面这些习惯将会帮助所有的团队成员全心投入到项目中,并且大家一起向着正确的方向努力。

    首先要做的是定期安排会面时间,见第148页。面对面的会议仍然是最有效的沟通方式,所以我们将以此作为本章的开篇。接下来,希望每个人都能投入到开发过程中来。也就是说架构师必须写代码(我们会在第152页看到为什么要这样做)。既然整个团队都是项目工作的一部分,我们希望实行代码集体所有制(见第155页),以保证任何团队成员的缺席不会对项目造成影响。这就是协作的效果,还记得吗?

    但是高效的协作并不只是写出代码就好了。随着时间的流逝,团队中每个人都要强化和提高他们的技能,并且推进各自的职业发展。即使一个人刚刚加入团队,他也可以成为指导者,将会在第157页谈到应该怎么做。团队中一个人的知识,经常可以解决另外一名团队成员的问题。只要允许大家自己想办法,就可以帮助团队不断成长,就像在第160页上看到的那样。

    最后,由于大家都是在团队中一起工作,每个人就要修改自己的个人编码习惯,来适应团队的其他成员。对于初学者来说,准备好后再共享代码才是有礼貌的做法(见第162页),这样才不会用未完成的工作来给团队成员造成麻烦。当准备好之后,我们应该与其他团队成员一起做代码复查(见第165页)。随着项目的推进,我们会不断地完成旧任务,并且领取新任务。应该及时通报进展与问题,让大家了解彼此的进度、遇到的问题,以及在开发过程中发现的有意思的东西。我们将在第168页讨论该习惯并结束本章。

    38 定期安排会面时间

    “会议安排得越多越好。实际上,我们要安排更多的会议,直到发现为什么工作总是完不成。”

    也许你个人很讨厌开会,但是沟通是项目成功的关键。我们不只要跟客户谈话,还应该与开发人员进行良好的沟通。要知道其他人在做什么——如果Bernie知道如何解决你的问题,你肯定希望早点搞清楚她是怎么做的,不是吗?

    立会(站着开的会议,Scrum最早引入并被极限编程所强调的一个实践)是将团队召集在一起,并让每个人了解当下进展状况的好办法。顾名思义,参与者们不允许在立会中就坐,这可以保证会议快速进行。一个人坐下来之后,会由于感到舒适而让会议持续更长的时间。

    Andy曾遇到一个客户,他和Dave Thomas通过电话远程参与客户的站立会议。一切都看起来很顺利,直到有一天,会议时间比平时多了一倍。你猜怎么着?客户那边,与会者都挪到了会议室,舒舒服服地坐在扶椅上开会。

    坐着开的会议通常会持续更久,大部分人不喜欢站着进行长时间的谈话。

    要保证会议议题不会发散,每个人都应该只回答下述三个问题。

    □ 昨天有什么收获?

    □ 今天计划要做哪些工作?

    □ 面临着哪些障碍?

    只能给予每个参与者很少的时间发言(大约两分钟)。也许要用计时器来帮助某些收不住话头的人。如果要详细讨论某些问题,可以在立会结束之后,再召集相关人员(在会议中说“我需要跟Fred和Wilma讨论一下数据库”是没有问题的,但是不要深入讨论细节)。

    通常,立会都是在每个工作日的早些时候,且大家都在上班时举行。但是不要把它安排为上班后的第一件事。要让大家有机会从刚才混乱的交通状况中恢复状态,喝点咖啡,删除一些垃圾邮件什么的。要保证会议结束后有足够的时间,让大家在午餐之前做不少工作,同时也不要开始得过早,让每个人都巴不得赶紧结束会议,去喝点东西。一般来说,在大家到公司之后的半个小时到一个小时之内举行,是个不错的选择。

    猪 与 鸡

    Scrum将团队成员与非团队成员这两种角色命名为猪和鸡。团队成员是猪(自尊何在啊),非团队成员(管理层、支持人员、QA等)是鸡。这两个用语来自一个寓言,讲的是农场里的动物们打算一起开饭店,并且准备用熏肉和鸡蛋作为早餐提供。对于鸡来说,当然是要参与进来了,可对于猪来讲,可就是放血投入了。
    只有“猪”才允许参与Scrum的每日立会。

    参加会议的人要遵守一些规则,以保证彼此不会分神,而且会议也不会跑题。这些规则有:只有团队成员——开发人员、产品所有者和协调者可以发言(查看上面对“猪”和“鸡”的描述)。他们必须回答上面的3个问题,而且不能展开深入讨论(讨论可以安排在会后进行)。管理层可以把要解决的问题记下来,但是不能试图将会议从每个人要回答的三个问题引开。

    每日立会有诸多好处。

    □ 让大家尽快投入到一天的工作中来。

    □ 如果某个开发人员在某一点上有问题,他可以趁此机会将问题公开,并积极寻求帮助。

    □ 帮助团队带头人或管理层了解哪些领域需要更多的帮助,并重新分配人手。

    □ 让团队成员知道项目其他部分的进展情况。

    □ 帮助团队识别是否在某些东西上有重复劳动而耗费了精力,或者是不是某个问题有人已有现成的解决方案。

    □ 通过促进代码和思路的共享,来提升开发速度。

    □ 鼓励向前的动力:看到别人报告的进度都在前进,会对彼此形成激励。

    使用厨房计时器

    开发者Nancy Davis告诉我们她使用厨房计时器召开立会的经验。
    “我们使用了妹妹去年圣诞节送给我的一个厨房计时器。它在运行时不会发出‘嘀哒’的声音,只会在时间到达后发出‘叮’的一声。如果计时器停止了,我们就再加两分钟,并让下一个成员发言。有时会忘掉计时器的存在,并让会议持续需要的时间,但是大部分情况下,我们都会遵守计时器的提醒。”

    采取立会的形式需要管理层的承诺和参与。不过,团队中的开发人员可以帮助推行这个实践。如果开发人员无法说服管理层的参与,他们自己可以用非正式的形式召开立会。

    使用立会。立会可以让团队达成共识。保证会议短小精焊不跑题。

    切身感受

    大家都盼望着立会。希望彼此了解各自的进度和手上的工作,而且不怕把各自遇到的问题拿出来公开讨论。

    平衡的艺术

    □ 会议会占用开发时间,所以要尽量保证投入的时间有较大的产出。立会的时间最长不能超出30分钟,10~15分钟比较理想。

    □ 如果要使用需提前预定的会议室,就把预定的时间设定为一个小时吧。这样就有机会在15分钟的立会结束后,马上召开更小规模的会议。

    □ 虽然大多数团队需要每天都碰头,但对于小型团队来说,这样做可能有点过头了。不妨两天举行一次,或者一周两次,这对小团队来说足够了。

    □ 要注意报告的细节。在会议中要给出具体的进度,但是不要陷入细节之中。例如,“我在开发登录页面”就不够详细。“登录页面目前接受guest/guest作为登录用户名和密码,我明天会连接数据库来做登录验证”,这样的详细程度才行。

    □ 迅速地开始可以保证会议短小。不要浪费时间等着会议开始。

    □ 如果觉得立会是在浪费时间,那可能是大家还没有形成真正的团队意识。这并不是坏事,有利于针对问题进行改进。

    39 架构师必须写代码

    “我们的专家级架构师Fred会提供设计好的架构,供你编写代码。他经验丰富,拿的薪水很高,所以不要用一些愚蠢的问题或者实现上的难点来浪费他的时间。”

    软件开发业界中有许多挂着架构师称号的人。作为作者的我们不喜欢这个称号,为什么呢?架构师应该负责设计和指导,但是许多名片上印着“架构师”的人配不上这个称号。作为架构师,不应该只是画一些看起来很漂亮的设计图,说一些像“黑话”一样的词汇,使用一大堆设计模式——这样的设计通常不会有效的。

    不可能在PowerPoint幻灯片中进行编程
    You can’t code in PowerPoint

    这些架构师通常在项目开始时介入,绘制各种各样的设计图,然后在重要的代码实现开始之前离开。有太多这种“PowerPoint架构师”了,由于得不到反馈,他们的架构设计工作也不会有很好的收效。

    一个设计要解决的是眼前面临的特定问题,随着设计的实现,对问题的理解也会发生改变。想在开始实现之前,就做出一个很有效的详细设计是非常困难的(见第48页习惯11)。因为没有足够的上下文,能得到的反馈也很少,甚至没有。设计会随着时间而演进,如果忽略了应用的现状(它的具体实现),要想设计一个新的功能,或者完成某个功能的提升是不可能的。

    作为设计人员,如果不能理解系统的具体细节,就不可能做出有效的设计。只通过一些高度概括的、粗略的设计图无法很好地理解系统。

    这就像是尝试仅仅通过查看地图来指挥一场战役——一旦开打,仅有计划是不够的。战略上的决策也许可以在后方进行,但是战术决策——影响成败的决策需要对战场状况的明确了解。[1]

    可 逆 性

    《程序员修炼之道》中指出不存在所谓的最终决策。没有哪个决策做出之后就是板上钉钉了。实际上,就时间性来看,不妨把每个重要的决策,都看作沙上堆砌的城堡,它们都是在变化之前所做出的预先规划。

    新系统的设计者

    新系统的设计者必须要亲自投入到实现中去。
    ——Donald E. Knuth[2]

    正像Knuth说的,好的设计者必须能够卷起袖子,加入开发队伍,毫不犹豫地参与实际编程。真正的架构师,如果不允许参与编码的话,他们会提出强烈的抗议。

    有一句泰米尔谚语说:“只有一张蔬菜图无法做出好的咖喱菜。”与之类似,纸上的设计也无法产生优秀的应用。应该根据设计开发出原型,经过测试,当然还有验证——它是要演化的。实现可用的设计,这是设计者或者说架构师的责任。

    Martin Fowler在题为“Who Needs an Architect?”[3]的文章中提到:一个真正的架构师“……应该指导开发团队,提升他们的水平,以解决更为复杂的问题”。他接着说:“我认为架构师最重要的任务是:通过找到移除软件设计不可逆性的方式,从而去除所谓架构的概念。”增强可逆性是注重实效的软件实现方式的关键构成部分。

    要鼓励程序员参与设计。主力程序员应该试着担任架构师的角色,而且可以从事多种不同的角色。他会负责解决设计上的问题,同时也不会放弃编码的工作。如果开发人员不愿意承担设计的责任,要给他们配备一个有良好设计能力的人。程序员在拒绝设计的同时,也就放弃了思考。

    优秀的设计从积极的程序员那里开始演化。积极的编程可以带来深入的理解。不要使用不愿意编程的架构师——不知道系统的真实情况,是无法展开设计的。

    切身感受

    架构、设计、编码和测试,这些工作给人的感觉就像是同一个活动——开发的不同方面。感觉它们彼此之间应该是不可分割的。

    平衡的艺术

    □ 如果有一位首席架构师,他可能没有足够的时间来参与编码工作。还是要让他参与,但是别让他开发在项目关键路径上的、工作量最大的代码。

    □ 不要允许任何人单独进行设计,特别是你自己。

    40 实行代码集体所有制

    “不用担心那个烦人的bug,Joe下周假期结束回来后会把它解决掉的。在此之前先想个权宜之计应付一下吧。”

    任何具备一定规模的应用,都需要多人协作进行开发。在这种状况下,不应该像国家宣称对领土的所有权一样,声明个人对代码的所有权。任何一位团队成员,只要理解某段代码的来龙去脉,就应该可以对其进行处理。如果某一段代码只有一位开发人员能够处理,项目的风险无形中也就增加了。

    相比找出谁的主意最好、谁的代码实现很烂而言,解决问题,并让应用满足用户的期望要更为重要。

    当多人同时开发时,代码会被频繁地检查、重构以及维护。如果需要修复bug,任何一名开发人员都可以完成这项工作。同时有两个或两个以上的人,可以处理应用中不同部分的代码,可以让项目的日程安排也变得更为容易。

    在团队中实行任务轮换制,让每个成员都可以接触到不同部分的代码,可以提升团队整体的知识和专业技能。当Joe接过Sally的代码,他可以对其进行重构,消除待处理的问题。在试图理解代码的时候,他会问些有用的问题,尽早开始对问题领域的深入理解。

    另一方面,知道别人将会接过自己的代码,就意味着自己要更守规矩。当知道别人在注意时,一定会更加小心。

    可能有人会说,如果一个开发者专门应对某一个领域中的任务,他就可以精通该领域,并让后续的开发任务更加高效。这没错,但是眼光放长远一点,有好几双眼睛盯着某一段代码,是一定可以带来好处的。这样可以提升代码的整体质量,使其易于维护和理解,并降低出错率。

    要强调代码的集体所有制。让开发人员轮换完成系统不同领域中不同模块的不同任务。

    切身感受

    项目中绝大部分的代码都可以轻松应对。

    平衡的艺术

    □ 不要无意间丧失了团队的专家技能。如果某个开发人员在某个领域中极其精通,不妨让他作为这方面的驻留专家,而且系统的其他部分代码也对他开放,这样对团队和项目都很有帮助。

    □ 在大型项目中,如果每个人都可以随意改变任何代码,一定会把项目弄得一团糟。代码集体所有制并不意味着可以随心所欲、到处破坏。

    □ 开发人员不必了解项目每一部分的每个细节,但是也不能因为要处理某个模块的代码而感到惊恐。

    □ 有些场合是不能采用代码集体所有制的。也许代码需要某些特定的知识、对特定问题域的了解,比如一个高难度的实时控制系统。这些时候,人多了反而容易误事。

    □ 任何人都可能遭遇到诸如车祸等突发的灾难事故,或者有可能被竞争对手雇佣。如果不向整个团队分享知识,反而增加了丧失知识的风险。

    41 成为指导者

    “你花费了大量的时间和精力,才达到目前的水平。对别人要有所保留,这样让你看起来更有水平。让队友对你超群的技能感到恐惧吧。”

    我们有时会发现自己在某些方面,比其他团队成员知道得更多。那要怎么对待这种新发现的“权威地位”呢?当然,可以用它来质疑别人,取笑他人做出的决策和开发的代码——有些人就是这样做的。不过,我们可以共享自己的知识,让身边的人变得更好。

    教学相长
    Knowledge grows when given
    好的想法不会因为被许多人了解而削弱。当我听到你的主意时,我得到了知识,你的主意也还是很棒。同样的道理,如果你用你的蜡烛点燃了我的,我在得到光明的同时,也没有让你的周围变暗。好主意就像火,可以引领这个世界,同时不削弱自己。[4]

    与团队其他人一起共事是很好的学习机会。知识有一些很独特的属性;假设你给别人钱的话,最后你的钱会变少,而他们的财富会增多。但如果是去教育别人,那双方都可以得到更多的知识。

    通过详细解释自己知道的东西,可以使自己的理解更深入。当别人提出问题时,也可以发现不同的角度。也许可以发现一些新技巧——听到一个声音这样告诉自己:“我以前还没有这样思考过这个问题。”

    与别人共事,激励他们变得更出色,同时可以提升团队的整体实力。遇到无法回答的问题时,说明这个领域的知识还不够完善,需要在这方面进一步增强。好的指导者在为他人提供建议时会做笔记。如果遇到需要花时间进一步观察和思考的问题,不妨先草草记录下来。此后将这些笔记加入到每日日志中(见第129页习惯33)。

    成为指导者,并不意味着要手把手教团队成员怎么做(见第160页习惯42),也不是说要在白板前进行讲座,或是开展小测验什么的,可以在进行自备午餐会时展开讨论。多数时候,成为指导者,是指在帮助团队成员提升水平的同时也提高自己。

    这个过程不必局限于自己的团队。可以开设个人博客,贴一些代码和技术在上面。不一定是多么伟大的项目,即使是一小段代码和解释,对别人也可能是有帮助的。

    成为指导者意味着要分享——而不是固守——自己的知识、经验和体会。意味着要对别人的所学和工作感兴趣,同时愿意为团队增加价值。一切都是为了提高队友和你的能力与水平,而不是为了毁掉团队。

    然而,努力爬到高处,再以蔑视的眼神轻视其他人,这似乎是人类本性。也许在没有意识到的情况下,沟通的障碍就已经建立起来了。团队中的其他人可能出于畏惧或尴尬,而不愿提出问题,这样就无法完成知识的交换了。这类团队中的专家,就像是拥有无数金银财宝的有钱人,却因健康原因无福享受。我们要成为指导别人的人,而不是折磨别人的人。

    成为指导者。分享自己的知识很有趣——付出的同时便有收获。还可以激励别人获得更好的成果,而且提升了整个团队的实力。

    切身感受

    你会感到给予别人教导,也是提升自己学识的一种方式,并且其他人亦开始相信你可以帮助他们。

    平衡的艺术

    □ 如果一直在就同一个主题向不同的人反复阐述,不妨记录笔记,此后就此主题写一篇文章,甚至是一本书。

    □ 成为指导者是向团队进行投资的一种极佳的方式。(见第31页习惯6。)

    □ 结对编程(见第165页习惯44)是一种进行高效指导的、很自然的环境。

    □ 如果总是被一些懒于自己寻找答案的人打扰(查看下一页习惯42)。

    □ 为团队成员在寻求帮助之前陷入某个问题的时间设定一个时限,一个小时应该是不错的选择。

    42 允许大家自己想办法

    “你这么聪明,直接把干净利落的解决方案告诉团队其他人就好了。不用浪费时间告诉他们为什么这样做。”

    “授人以鱼,三餐之需;授人以渔,终生之用。”告诉团队成员解决问题的方法,也要让他们知道如何解决问题的思路,这也是成为指导者的一部分。

    了解上个实践——成为指导者——之后,也许有人会倾向于直接给同事一个答案,以继续完成工作任务。要是只提供一些指引给他们,让他们自己想办法找到答案,又会如何?

    这并不是多么麻烦的事情;不要直接给出像“42”这样的答案,应该问你的队友:“你有没有查看在事务管理者与应用的锁处理程序之间的交互关系?”

    这样做有下面几点好处。

    □ 你在帮助他们学会如何解决问题。

    □ 除了答案之外,他们可以学到更多东西。

    □ 他们不会再就类似的问题反复问你。

    □ 这样做,可以帮助他们在你不能回答问题时自己想办法。

    □ 他们可能想出你没有考虑到的解决方法或者主意。这是最有趣的——你也可以学到新东西。

    如果有人还是没有任何线索,那就给更多提示吧(或者甚至是答案)。如果有人提出来某些想法,不妨帮他们分析每种想法的优劣之处。如果有人给出的答案或解决方法更好,那就从中汲取经验,然后分享你的体会吧。这对双方来说都是极佳的学习经验。

    作为指导者,应该鼓励、引领大家思考如何解决问题。前面提到过亚里士多德的话:“接纳别人的想法,而不是盲目接受,这是受过教育的头脑的标志。”应该接纳别人的想法和看问题的角度,在这个过程中,自己的头脑也得到了拓展。

    如果整个团队都能够采纳这样的态度,可以发现团队的知识资本在快速提升,而且将会完成一些极其出色的工作成果。

    给别人解决问题的机会。指给他们正确的方向,而不是直接提供解决方案。每个人都能从中学到不少东西。

    切身感受

    感觉不是在以填鸭式的方式给予别人帮助。不是有意掩饰,更非讳莫如深,而是带领大家找到自己的解决方案。

    平衡的艺术

    □ 用问题来回答问题,可以引导提问的人走上正确的道路。

    □ 如果有人真的陷入胶着状态,就不要折磨他们了。告诉他们答案,再解释为什么是这样。

    43 准备好后再共享代码

    “别管是不是达到代码签入的要求,要尽可能频繁地提交代码,特别是在要下班的时候。”

    让你猜个谜语:相对不使用版本控制系统,更坏的状况是什么?答案是:错误地使用了版本控制系统。使用版本控制系统的方式,会影响生产力、产品稳定性、产品质量和开发日程。特别地,诸如代码提交频率这样简单的东西都会有很大影响。

    完成一项任务后,应该马上提交代码,不应该让代码在开发机器上多停留一分钟。如果代码不能被别人集成使用,那又有什么用处呢?应该赶紧发布出去,并开始收集反馈。[5]

    很明显,每周或每月一次提交代码,并不是令人满意的做法——这样源代码控制系统就不能发挥其作用了。也许总有种种原因来为这种懒散的做法解释。有人说开发人员是采取异地开发(off-site)或离岸开发(offshore)的方式,访问源代码控制系统的速度很慢。这就是环境黏性(environmental viscosity)的例子——把事情做糟要比做好更容易。很明显,这是一个亟待解决的简单技术问题。

    另一方面,如果在任务完成之前就提交代码又会如何?也许你正在开发一些至关重要的代码,而且你想在下班回家晚饭之后再继续开发。要想在家里得到代码,最简单的方式就是将其提交到源代码控制系统,到家之后再把代码签出。

    向代码库中提交仍在开发的代码,会带来很多风险。这些代码可能还有编译错误,或者对其所做的某些变化与系统其他部分的代码不兼容。当其他开发者获取最新版本的代码时,也会受到这些代码的影响。

    通常情况下,提交的文件应该与一个特定的任务或是一个bug的解决相关。而且应该是同时提交相关的文件,并注有日志信息,将来也能够知道修改了哪些地方,以及为什么要做修改。一旦需要对变更采取回滚操作,这种“原子”提交也是有帮助的。

    要保证在提交代码之前,所有的单元测试都是可以通过的。使用持续集成是保证源代码控制系统中代码没有问题的一种良好方式。

    代码不执行提交操作的其他安全选择

    如果需要将尚未完成的源代码传输或是保存起来,有如下选择。
    使用远程访问。将代码留在工作地点,然后在家里使用远程访问获取,而不是将完成了一半的代码提交,再从家里签出。
    随身携带。将代码复制到U盘、CD或DVD中,以达到异地开发的目的。
    使用带有底座扩展的笔记本电脑。如果是由于在多台电脑上开发造成的延续性问题,不妨考虑使用带有底座扩展的笔记本电脑,这样就可以带着代码到处走了。
    使用源代码控制系统的特性。Microsoft Visual Team System 2005有一个“shelving”特性,因为有些产品的某些代码在提交之前,需要被其他部分调用。
    在CVS和Subversion中,可以将尚未允许合并到主干的代码,设定为开发者的分支(查看[TH03]和[Mas05])。
    准备好后再共享代码。绝不要提交尚未完成的代码。故意签入编译未通过或是没有通过单元测试的代码,对项目来说,应被视作玩忽职守的犯罪行为。

    切身感受

    感觉好像整个团队就在源代码控制系统的另一端盯着你。要知道一旦提交代码,别人就都可以访问了。

    平衡的艺术

    □ 有些源代码控制系统会区分“提交”和“可公开访问”两种代码权限。此时,可以进行临时的提交操作(比如在工作地点和家之间来回奔波时),不会因为完全提交未完成的代码,而让团队的其他成员感到郁闷。

    □ 有些人希望代码在提交之前可以进行复查操作。只要不会过久拖延提交代码的时间就没有问题。如果流程的某个部分产生了拖延,那就修正流程吧。

    □ 仍然应该频繁提交代码。不能用“代码尚未完成”作为避免提交代码的借口。

    44 做代码复查

    “用户是最好的测试人员。别担心——如果哪里出错了,他们会告诉我们的。”

    代码刚刚完成时,是寻找问题的最佳时机。如果放任不管,它也不会变得更好。

    代码复查和缺陷移除

    要寻找深藏不露的程序bug,正式地进行代码检查,其效果是任何已知形式测试的两倍,而且是移除80%缺陷的唯一已知方法。
    ——Capers Jones的《估算软件成本》[Jon98]

    正如Capers Jones指出的,代码复查或许是找到并解决问题的最佳方式。然而,有时很难说服管理层和开发人员使用它来完成开发工作。

    管理层担心进行代码复查所耗费的时间。他们不希望团队停止编码,而去参加长时间的代码复查会议。开发人员对代码复查感到担心,允许别人看他们的代码,会让他们有受威胁的感觉。这影响了他们的自尊心。他们担心在情感上受到打击。

    作者参与过的项目中,只要实施了代码复查,其成果都是非常显著的。

    Venkat最近参与了一个日程安排非常紧凑的项目,团队不少成员都是没有多少经验的开发者。通过严格的代码复查过程,他们可以提交质量极高而且稳定的代码。当开发人员完成某项任务的编码和测试后,在签入源代码控制系统之前,会有另一名开发人员对代码做彻底的复查。

    这个过程修复了很多问题。噢,代码复查不只针对初级开发者编写的代码——团队中每个开发人员的代码都应该进行复查,无论其经验丰富与否。

    那该如何进行代码复查呢?可以从下面这些不同的基本方式中进行选择。

    □ 通宵复查。可以将整个团队召集在一起,预定好美食,每个月进行一次“恐怖的代码复查之夜”。但这可能不是进行代码复查最有效的方式(而且听起来也不太敏捷)。大规模团队的复查会议很容易陷入无休止的讨论之中。大范围的复查不仅没有必要,而且有可能对整个流程造成损害。我们不建议这种方式。

    □ 捡拾游戏。当某些代码编写完成、通过编译、完成测试,并已经准备签入时,其他开发人员就可以“捡拾”起这些代码开始复查。类似的“提交复查”是一种快速而非正式的方式,保证代码在提交之前是可以被接受的。为了消除行为上的惯性,要在开发人员之间进行轮换。比如,如果Joey的代码上次是由Jane复查的,这次不妨让Mark来复查。这是一种很有效的技术。[6]

    □ 结对编程。在极限编程中,不存在一个人独立进行编码的情况。编程总是成对进行的:一个人在键盘旁边(担任司机的角色),另一个人坐在后面担任导航员。他们会不时变换角色。有第二双眼睛在旁边盯着,就像是在进行持续的代码复查活动,也就不必安排单独的特定复查时间了。

    在代码复查中要看什么呢?你可能会制订出要检查的一些特定问题列表(所有的异常处理程序不允许空,所有的数据库调用都要在包的事务中进行,等等),不过这里是一个可供启动的最基本的检查列表。

    □ 代码能否被读懂和理解?

    □ 是否有任何明显的错误?

    □ 代码是否会对应用的其他部分产生不良影响?

    □ 是否存在重复的代码(在复查的这部分代码中,或是在系统的其他部分代码)?□ 是否存在可以改进或重构的部分?

    此外,还可以考虑使用诸如Similarity Analyzer或Jester这样的代码分析工具。如果这些工具产生的静态分析结果对项目有帮助,就把它们集成到持续构建中去吧。

    复查所有的代码。对于提升代码质量和降低错误率来说,代码复查是无价之宝。如果以正确的方式进行,复查可以产生非常实用而高效的成果。要让不同的开发人员在每个任务完成后复查代码。

    切身感受

    代码复查随着开发活动持续进行,而且每次针对的代码量相对较少。感觉复查活动就像是项目正在进行的一部分,而不是一种令人畏惧的事情。

    平衡的艺术

    □ 不进行思考、类似于橡皮图章一样的代码复查没有任何价值。

    □ 代码复查需要积极评估代码的设计和清晰程度,而不只是考量变量名和代码格式是否符合组织的标准。

    □ 同样的功能,不同开发人员的代码实现可能不同。差异并不意味着不好。除非你可以让某段代码明确变得更好,否则不要随意批评别人的代码。

    □ 如果不及时跟进讨论中给出的建议,代码复查是没有实际价值的。可以安排跟进会议,或者使用代码标记系统,来标识需要完成的工作,跟踪已经处理完的部分。

    □ 要确保代码复查参与人员得到每次复查活动的反馈。作为结果,要让每个人知道复查完成后所采取的行动。

    45 及时通报进展与问题

    “管理层、项目团队以及业务所有方,都仰仗你来完成任务。如果他们想知道进展状况,会主动找你要的。还是埋头继续做事吧。”

    接受一个任务,也就意味着做出了要准时交付的承诺。不过,遇到各种问题从而导致延迟,这种情形并不少见。截止日期来临,大家都等着你在演示会议上展示工作成果。如果你到会后通知大家工作还没有完成,会有什么后果?除了感到窘迫,这对你的事业发展也没有什么好处。

    如果等到截止时间才发布坏消息,就等于是为经理和技术主管提供了对你进行微观管理(micromanagement)的机会。他们会担心你再次让他们失望,并开始每天多次检查你的工作进度。你的生活就开始变得像呆伯特的漫画一样了。

    假定现在你手上有一个进行了一半的任务,由于技术上的难题,看起来不能准时完成了。如果这时积极通知其他相关各方,就等于给机会让他们提前找出解决问题的方案。也许他们可以向另外的开发人员寻求帮助,也许他们可以将工作重新分配给更加熟悉相关技术的人,也许他们可以提供更多需要的资源,或者调整目前这个迭代中要完成的工作范围。客户会愿意将这个任务用其他同等重要的任务进行交换的。

    及时通报进展与问题,有情况发生时,就不会让别人感到突然,而且他们也很愿意了解目前的进展状况。他们会知道何时应提供帮助,而且你也获得了他们的信任。

    发送电子邮件,用即时贴传递信息,或快速电话通知,这都是通报大家的传统方式。还可以使用Alistair Cockburn提出的“信息辐射器”。[7]信息辐射器类似于墙上的海报,提供变更的信息。路人可以很方便地了解其中的内容。以推送的方式传递信息,他们就不必再来问问题了。信息辐射器中可以展示目前的任务进度,和团队、管理层或客户可能会感兴趣的其他内容。

    也可以使用海报、网站、Wiki、博客或者RSS Feed。只要让人们可以有规律地查看到需要的信息,这就可以了。

    整个团队可以使用信息辐射器来发布他们的状态、代码设计、研究出的好点子等内容。现在只要绕着团队的工作区走一圈,就可以学到不少新东西,而且管理层也就可以知道目前的状况如何了。

    及时通报进展与问题。发布进展状况、新的想法和目前正在关注的主题。不要等着别人来问项目状态如何。

    切身感受

    当经理或同事来询问工作进展、最新的设计,或研究状况时,不会感到头痛。

    平衡的艺术

    □ 每日立会(见第148页习惯38)可以让每个人都能明确了解最新的进展和形势。

    □ 在展示进度状况时,要照顾到受众关注的细节程度。举例来说,CEO和企业主是不会关心抽象基类设计的具体细节的。

    □ 别花费太多时间在进展与问题通报上面,还是应该保证开发任务的顺利完成。

    □ 经常抬头看看四周,而不是只埋头于自己的工作。

    【注释】

    [1]第一次世界大战中,所门战役(the Battle of the Somme)本应成为一个有决定性意义的突破。实际上,它却成为了20世纪最愚蠢的军事行动。最重要的原因是,由于断绝了通信联系,面对的战场情况与早先的预测已经完全不同了,指挥官仍然坚持按照原计划展开战役。请查看http://www.worldwar1.com/sfsomme.htm。

    [2]计算机科学大师,图灵奖得主,经典著作《计算机程序设计艺术》作者。——编者注

    [3]http://www.martinfowler.com/ieeesoftware/whoNeedsArchitect.pdf。

    [4]托马斯·杰弗逊,美国第三任总统,独立宣言起草人。

    [5]而且,不应该将仅有的一份代码保存在只有“90天有限质保”的硬盘中。

    [6]要了解这种方式的更多细节,查看Ship It![RG05]一书。

    [7]查看http://c2.com/cgi-bin/wiki?InformationRadiator。