第2章 态度决定一切

选定了要走的路,就是选定了它通往的目的地。——Harry Emerson Fosdick(美国基督教现代主义神学家)

传统的软件开发图书一般先介绍一个项目的角色配置,然后是你需要产生哪些工件(artifact)——文档、任务清单、甘特(Gantt)图等,接着就是规则制度,往往是这么写的:汝当如此(1)这般……本书的风格不是这样的。欢迎进入敏捷方法的世界,我们的做法有些不同。

例如,有一种相当流行的软件方法学要求对一个项目分配35种不同的角色,包括架构师、设计人员、编码人员、文档管理者等。敏捷方法却背道而驰。只需要一个角色:软件开发者,也就是你。项目需要什么你就做什么,你的任务就是和紧密客户协作,一起开发软件。敏捷依赖人,而不是依赖于项目的甘特图和里程表。

图表、集成开发环境或者设计工具,它们本身都无法产生软件,软件是从你的大脑中产生的。而且它不是孤立的大脑活动,还会有许多其他方面的因素:个人情绪、办公室的文化、自我主义、记忆力等。它们混为一体,态度和心情的瞬息变化都可能导致巨大的差别。

因此态度非常重要,包括你的和团队的。专业的态度应该着眼于项目和团队的积极结果,关注个人和团队的成长,围绕最后的成功开展工作。由于很容易变成追求不太重要的目标,所以在本章,我们会专注于那些真正的目标。集中精力,你是为做事而工作。(想知道怎样做吗?请见下一页。)

软件项目时常伴有时间压力——压力会迫使你走捷径,只看眼前利益。但是,任何一个有经验的开发者都会告诉你,欲速则不达(我们在第15页将介绍如何避免这个问题)。

我们每个人或多或少都有一些自我主义。一些人(暂且不提他们的名字)还美其名曰“健康”的自我主义。如果要我们去解决一个问题,我们会为完成任务而感到骄傲,但这种骄傲有时会导致主观和脱离实际。你也很可能见过设计方案的讨论变成了人身攻击,而不是就事论事地讨论问题。对事不对人(第18页)会让工作更加有效。

反馈是敏捷的基础。一旦你意识到走错了方向,就要立即做出决策,改变方向。但是指出问题往往没有那么容易,特别当它涉及一些政治因素的时候。有时候,你需要勇气去排除万难,奋勇前进(第23页)。

只有在你对项目、工作、事业有一个专业的态度时,使用敏捷方法才会生效。如果态度不正确,那么所有的这些习惯都不管用。有了正确的态度,你才可以从这些方法中完全受益。下面我们就来介绍这些对你大有裨益的习惯和建议。

1 做事

“出了问题,第一重要的是确定元凶。找到那个白痴!一旦证实了是他的错误,就可以保证这样的问题永远不会再发生了。”

有时候,这个老魔头的话听起来似乎很有道理。毫无疑问,你想把寻找罪魁祸首设为最高优先级,难道不是吗?肯定的答案是:不。最高优先级应该是解决问题。

也许你不相信,但确实有些人常常不把解决问题放在最高优先级上。也许你也没有。先自我反省一下,当有问题出现时,“第一”反应究竟是什么。

如果你说的话只是让事态更复杂,或者只是一味地抱怨,或者伤害了他人的感情,那么你无意中在给问题火上浇油。相反,你应该另辟蹊径,问问“为了解决或缓解这个问题,我能够做些什么?”在敏捷的团队中,大家的重点是做事。你应该把重点放到解决问题上,而不是在指责犯错者上面纠缠。

世上最糟糕的工作(除了在马戏团跟在大象后面打扫卫生)就是和一群爱搬弄是非的人共事。他们对解决问题并没有兴趣,相反,他们爱在别人背后议论是非。他们挖空心思指手画脚,议论谁应该受到指责。这样一个团队的生产力是极其低下的。如果你发现自己是在这样的团队中工作,不要从团队中走开——应该跑开。至少要把对话从负面的指责游戏引到中性的话题,比如谈论体育运动(纽约扬基队最近怎么样)或者天气。

指责不能修复bug Blame doesn’t fix bugs

敏捷团队中,情形截然不同。如果你向敏捷团队中的同事抱怨,他们会说:“好,我能帮你做些什么?”他们把精力直接放到解决问题上,而不是抱怨。他们的动机很明确,重点就是做事,不是为了自己的面子,也不是为了指责,也无意进行个人智力角斗。

你可以从自己先做起。如果一个开发者带着抱怨或问题来找你,你要了解具体的问题,询问他你能提供什么样的帮助。这样简单的一个行为就清晰地表明你的目的是解决问题,而不是追究责任,这样就会消除他的顾虑。你是给他们帮忙的。这样,他们会知道每次走近你的时候,你会真心帮助他们解决问题。他们可以来找你把问题解决了,当然还可以继续去别处求助。

符合标准不是结果

许多标准化工作强调遵从一个过程,按符合的程度作评判,其理由是:如果过程可行,那么只要严格按这个过程行事,就不会有问题。

但是,现实世界并不是如此运行的。你可以去获得ISO-9001认证,并生产出一件漂亮的铅线织就的救生衣。你完全遵循了文档中约定的过程,糟糕的是到最后所有的用户都被淹死了。

过程符合标准并不意味结果是正确的。敏捷团队重结果胜于重过程。

如果你找人帮忙,却没有人积极响应,那么你应该主动引导对话。解释清楚你想要什么,并清晰地表明你的目的是解决问题,而不是指责他人或者进行争辩。

指责不会修复bug。把矛头对准问题的解决办法,而不是人。这是真正有用处的正面效应。

切身感受

勇于承认自己不知道答案,这会让人感觉放心。一个重大的错误应该被当作是一次学习而不是指责他人的机会。团队成员们在一起工作,应互相帮助,而不是互相指责。

平衡的艺术

  • “这不是我的错”,这句话不对。“这都是你的错”,这句话更不对。
  • 如果你没有犯过任何错误,就说明你可能没有努力去工作。
  • 开发者和质量工程师(QA)争论某个问题是系统本身的缺陷还是系统增强功能导致的,通常没有多大的意义。与其如此,不如赶紧去修复它。
  • 如果一个团队成员误解了一个需求、一个API调用,或者最近一次会议做的决策,那么,也许就意味着团队的其他成员也有相同的误解。要确保整个团队尽快消除误解。
  • 如果一个团队成员的行为一再伤害了团队,则他表现得很不职业。那么,他就不是在帮助团队向解决问题的方向前进。这种情况下,我们必须要求他离开这个团队(2)。
  • 如果大部分团队成员(特别是开发领导者)的行为都不职业,并且他们对团队目标都不感兴趣,你就应该主动从这个团队中离开,寻找更适合自己发展的团队(这是一个有远见的想法,没必要眼睁睁地看着自己陷入一个“死亡之旅”的项目中[You99])。

2 欲速则不达

“你不需要真正地理解那块代码,它只要能够工作就可以了。哦,它需要一个小小的调整。只要在结果中再加上几行代码,它就可以工作了。干吧!就把那几行代码加进去,它应该可以工作。”

我们经常会遇到这种情况,出现了一个bug,并且时间紧迫。快速修复确实可以解决它——只要新加一行代码或者忽略那个列表上的最后一个条目,它就可以工作了。但接下来的做法才能说明,谁是优秀的程序员,谁是拙劣的代码工人。

拙劣的代码工人会这样不假思索地改完代码,然后快速转向下一个问题。

优秀的程序员会挖掘更深一层,尽力去理解为什么这里必须要加1,更重要的是,他会想明白会产生什么其他影响。

也许这个例子听起来有点做作,甚至你会觉得很无聊。但是,真实世界中有大量这样的事情发生。Andy以前的一个客户正遇到过这样的问题。没有一个开发者或者架构师知道他们业务领域的底层数据模型。而且,通过几年的积累,代码里有着成千上万的+1和-1修正。在这样脏乱的代码中添加新的功能或者修复bug,就难逃脱发的噩运(事实上,很多开发者因此而秃顶)。

千里之堤,溃于蚁穴,大灾难是逐步演化来的。一次又一次的快速修复,每一次都不探究问题的根源,久而久之就形成了一个危险的沼泽地,最终会吞噬整个项目的生命。

在工作压力之下,不去深入了解真正的问题以及可能的后果,就快速修改代码,这样只是解决表面问题,最终会引发大问题。快速修复的诱惑,很容易令人把持不住,坠入其中。短期看,它似乎是有效的。但从长远来看,它无异于穿越一片流沙,你也许侥幸走过了一半的路程(甚至更远),一切似乎都很正常。但是转眼间悲剧就发生了……

防微杜渐 Beware of land mines

只要我们继续进行快速修复,代码的清晰度就不断降低。一旦问题累积到一定程度,清晰的代码就不复存在,只剩一片混浊。很可能在你的公司就有人这样告诉你:“无论如何,千万不能碰那个模块的代码。写代码那哥们儿已经不在这儿了,没有人看得懂他的代码。”这些代码根本没有清晰度可言,它已经成为一团迷雾,无人能懂。

要理解开发过程

尽管我们在谈论理解代码,特别是在修改代码之前一定要很好地理解它,然而同样道理,你也需要了解团队的开发方法或者开发过程。

你必须要理解团队采用的开发方法。你必须理解如何恰如其分地使用这种方法,为何它们是这样的,以及如何成为这样的。

只有理解了这些问题,你才能进行有效的改变。

如果在你的团队中有这样的事情发生,那么你是不可能敏捷的。但是敏捷方法中的一些技术可以阻止这样的事情发生。这里只是一些概述,后面的章节会有更深入的介绍。

孤立非常危险,不要让开发人员完全孤立地编写代码(见第155页,习惯40)。如果团队成员花些时间阅读其他同事写的代码,他们就能确保代码是可读和可理解的,并且不会随意加入这些“+1或-1”的代码。阅读代码的频率越高越好。实行代码复审,不仅有助于代码更好理解,而且是发现bug最有效的方法之一(见第165页,习惯44)。

不要孤立地编码 Don’t code in isolation

另一种防止代码难懂的重要技术就是单元测试。单元测试帮助你很自然地把代码分层,分成很多可管理的小块,这样就会得到设计更好、更清晰的代码。更深入项目的时候,你可以直接阅读单元测试——它们是一种可执行的文档(见第78页,习惯19)。有了单元测试,你会看到更小、更易于理解的代码模块,运行和使用代码,能够帮助你彻底理解这些代码。

使用单元测试 Use unit tests

不要坠入快速的简单修复之中。要投入时间和精力保持代码的整洁、敞亮。

切身感受

在项目中,代码应该是很亮堂的,不应该有黑暗死角。你也许不知道每块代码的每个细节,或者每个算法的每个步骤,但是你对整体的相关知识有很好的了解。没有任何一块代码被警戒线或者“切勿入内”的标志隔离开。

平衡的艺术

  • 你必须要理解一块代码是如何工作的,但是不一定需要成为一位专家。只要你能使用它进行有效的工作就足够了,不需要把它当作毕生事业。
  • 如果有一位团队成员宣布,有一块代码其他人都很难看懂,这就意味着任何人(包括原作者)都很难维护它。请让它变得简单些。
  • 不要急于修复一段没能真正理解的代码。这种+1/-1的病症始于无形,但是很快就会让代码一团糟。要解决真正的问题,不要治标不治本。
  • 所有的大型系统都非常复杂,因此没有一个人能完全明白所有的代码。除了深入了解你正在开发的那部分代码之外,你还需要从更高的层面来了解大部分代码的功能,这样就可以理解系统各个功能块之间是如何交互的。
  • 如果系统的代码已经恶化,可以阅读第23页习惯4中给出的建议。

3 对事不对人

你在这个设计上投入了很多精力,为它付出很多心血。你坚信它比其他任何人的设计都棒。别听他们的,他们只会把问题变得更糟糕。”

你很可能见过,对方案设计的讨论失控变成了情绪化的指责——做决定是基于谁提出了这个观点,而不是权衡观点本身的利弊。我们曾经参与过那样的会议,最后闹得大家都很不愉快。

但是,这也很正常。当Lee先生在做一个新方案介绍的时候,下面有人会说:“那样很蠢!”(这也就暗示着Lee先生也很蠢。)如果把这句话推敲一下,也许会好一点:“那样很蠢,你忘记考虑它要线程安全。”事实上最适合并且最有效的表达方式应该是:“谢谢,Lee先生。但是我想知道,如果两个用户同时登录会发生什么情况?”

看出其中的不同了吧!下面我们来看看对一个明显的错误有哪些常见的反应。

  • 否定个人能力。
  • 指出明显的缺点,并否定其观点。
  • 询问你的队友,并提出你的顾虑。

第一种方法是不可能成功的。即使Lee是一个十足的笨蛋,很小的问题也搞不定,但你那样指出问题根本不会对他的水平有任何提高,反而会导致他以后再也不会提出自己的任何想法了。第二种方法至少观点明确,但也不能给Lee太多的帮助,甚至可能会让你自己惹火上身。也许Lee能巧妙地回复你对非线程安全的指责:“哦,不过它不需要多线程。因为它只在Frozbot模块的环境中使用,它已经运行在自己的线程中了。”哎哟!忘记了Frozbot这一茬了。现在该是你觉得自己蠢了,Lee也会因为你骂他笨蛋而生气。

现在看看第三种方法。没有谴责,没有评判,只是简单地表达自己的观点。让Lee自己意识到这个问题,而不是扫他的面子(3)。由此可以开始一次交谈,而不是争辩。

要专业而不是自我

多年以前,在我担任系统管理员的第一天,一位资深的管理员和我一起安装一些软件,我突然按错了一个按钮,把服务器给关掉了。没过几分钟,几位不爽的用户就在敲门了。

这时,我的导师赢得了我的信任和尊重,他并没有指责我,而是对他们说:“对不起,我们正在查找是什么地方出错了。系统会在几分钟之内启动起来。”这让我学到了难忘的重要一课。

在一个需要紧密合作的开发团队中,如果能稍加注意礼貌对待他人,将会有益于整个团队关注真正有价值的问题,而不是勾心斗角,误入歧途。我们每个人都能有一些极好的创新想法,同样也会萌生一些很愚蠢的想法。

如果你准备提出一个想法,却担心有可能被嘲笑,或者你要提出一个建议,却担心自己丢面子,那么你就不会主动提出自己的建议了。然而,好的软件开发作品和好的软件设计,都需要大量的创造力和洞察力。分享并融合各种不同的想法和观点,远远胜于单个想法为项目带来的价值。

负面的评论和态度扼杀了创新。现在,我们并不提倡在设计方案的会议上手拉手唱《学习雷锋好榜样》,这样也会降低会议的效率。但是,你必须把重点放在解决问题上,而不是去极力证明谁的主意更好。在团队中,一个人只是智商高是没有用的,如果他还很顽固并且拒绝合作,那就更糟糕。在这样的团队中,生产率和创新都会频临灭亡的边缘。

消极扼杀创新 Negativity kills innovation

我们每个人都会有好的想法,也会有不对的想法,团队中的每个人都需要自由地表达观点。即使你的建议不被全盘接受,也能对最终解决问题有所帮助。不要害怕受到批评。记住,任何一个专家都是从这里开始的。用Les Brown(4)的一句话说就是:“你不需要很出色才能起步,但是你必须起步才能变得很出色。”

团体决策的骆驼

集体决策确实非常有效,但也有一些最好的创新源于很有见地的个人的独立思考。如果你是一个有远见的人,就一定要特别尊重别人的意见。你是一个掌舵者,一定要把握方向,深思熟虑,吸取各方的意见。

另一个极端是缺乏生气的委员会,每个设计方案都需要全票通过。这样的委员会总是小题大作,如果让他们造一匹木马,很可能最后造出的是骆驼。

我们并不是建议你限制会议决策,只是你不应该成为一意孤行的首席架构师的傀儡。这里建议你牢记亚里士多德的一句格言:“能容纳自己并不接受的想法,表明你的头脑足够有学识。”

下面是一些有效的特殊技术。

设定最终期限。如果你正在参加设计方案讨论会,或者是寻找解决方案时遇到问题,请设定一个明确的最终期限,例如午饭时间或者一天的结束。这样的时间限制可以防止人们陷入无休止的理论争辩之中,保证团队工作的顺利进行。同时(我们觉得)应现实一些:没有最好的答案,只有更合适的方案。设定期限能够帮你在为难的时候果断做出决策,让工作可以继续进行。

逆向思维。团队中的每个成员都应该意识到权衡的必要性。一种客观对待问题的办法是:先是积极地看到它的正面,然后再努力地从反面去认识它(5)。目的是要找出优点最多缺点最少的那个方案,而这个好办法可以尽可能地发现其优缺点。这也有助于少带个人感情。

设立仲裁人。在会议的开始,选择一个仲裁人作为本次会议的决策者。每个人都要有机会针对问题畅所欲言。仲裁人的责任就是确保每个人都有发言的机会,并维持会议的正常进行。仲裁人可以防止明星员工操纵会议,并及时打断假大空式发言。

如果你自己没有积极参与这次讨论活动,那么你最好退一步做会议的监督者。仲裁人应该专注于调停,而不是发表自己的观点(理想情况下不应在整个项目中有既得利益)。当然,这项任务不需要严格的技术技能,需要的是和他人打交道的能力。

支持已经做出的决定。一旦方案被确定了(不管是什么样的方案),每个团队成员都必须通力合作,努力实现这个方案。每个人都要时刻记住,我们的目标是让项目成功满足用户需求。客户并不关心这是谁的主意——他们关心的是,这个软件是否可以工作,并且是否符合他们的期望。结果最重要。

设计充满了妥协(生活本身也是如此),成功属于意识到这一点的团队。工作中不感情用事是需要克制力的,而你若能展现出成熟大度来,大家一定不会视而不见。这需要有人带头,身体力行,去感染另一部分人。

对事不对人。让我们骄傲的应该是解决了问题,而不是比较出谁的主意更好。

切身感受

一个团队能够很公正地讨论一些方案的优点和缺点,你不会因为拒绝了有太多缺陷的方案而伤害别人,也不会因为采纳了某个不甚完美(但是更好的)解决方案而被人忌恨。

平衡的艺术

  • 尽力贡献自己的好想法,如果你的想法没有被采纳也无需生气。不要因为只是想体现自己的想法而对拟定的好思路画蛇添足。
  • 脱离实际的反方观点会使争论变味。若对一个想法有成见,你很容易提出一堆不太可能发生或不太实际的情形去批驳它。这时,请先扪心自问:类似问题以前发生过吗?是否经常发生?
  • 也就是说,像这样说是不够的:我们不能采用这个方案,因为数据库厂商可能会倒闭。或者:用户绝对不会接受那个方案。你必须要评判那些场景发生的可能性有多大。想要支持或者反驳一个观点,有时候你必须先做一个原型或者调查出它有多少的同意者或者反对者。
  • 在开始寻找最好的解决方案之前,大家对“最好”的含义要先达成共识。在开发者眼中的最好,不一定就是用户认为最好的,反之亦然。
  • 只有更好,没有最好。尽管“最佳实践”这个术语到处在用,但实际上不存在“最佳”,只有在某个特定条件下更好的实践。
  • 不带个人情绪并不是要盲目地接受所有的观点。用合适的词和理由去解释为什么你不赞同这个观点或方案,并提出明确的问题。

4 排除万难,奋勇前进

“如果你发现其他人的代码有问题,只要你自己心里知道就可以了。毕竟,你不想伤害他们,或者惹来麻烦。如果他是你的老板,更要格外谨慎,只要按照他的命令执行就可以了。”

有一则寓言叫“谁去给猫系铃铛”(Who Will Bell the Cat)。老鼠们打算在猫的脖子上系一个铃铛,这样猫巡逻靠近的时候,就能预先得到警报。每只老鼠都点头,认为这是一个绝妙的想法。这时一只年老的老鼠问道:“那么,谁愿意挺身而出去系铃铛呢?”毫无疑问,没有一只老鼠站出来。当然,计划也就这样泡汤了。

有时,绝妙的计划会因为勇气不足而最终失败。尽管前方很危险——不管是真的鱼雷或者只是一个比喻——你必须有勇气向前冲锋,做你认为对的事情。

假如要你修复其他人编写的代码,而代码很难理解也不好使用。你是应该继续修复工作,保留这些脏乱的代码呢,还是应该告诉你的老板,这些代码太烂了,应该通通扔掉呢?

也许你会跳起来告诉周围的人,那些代码是多么糟糕,但那只是抱怨和发泄,并不能解决问题。相反,你应该重写这些代码,并比较重写前后的优缺点。动手证明(不要只是嚷嚷)最有效的方式,是把糟糕的代码放到一边,立刻重写。列出重写的理由,会有助于你的老板(以及同事)认清当前形势,帮助他们得到正确的解决方案。

再假定你在处理一个特定的组件。突然,你发现完全弄错了,你需要推翻重来。当然,你也会很担心向团队其他成员说明这个问题,以争取更多的时间和帮助。

当发现问题时,不要试图掩盖这些问题。而要有勇气站起来,说:“我现在知道了,我过去使用的方法不对。我想到了一些办法,可以解决这个问题——如果你有更好的想法,我也很乐意听一听——但可能会花多些时间。”你已经把所有对问题的负面情绪抛诸脑后,你的意图很清楚,就是寻找解决方案。既然你提出大家一起努力来解决问题,那就不会有任何争辩的余地。这样会促进大家去解决问题。也许,他们就会主动走近,提供帮助。更重要的是,这显示出了你的真诚和勇气,同时你也赢得了他们的信任。

践行良好习惯

我曾经开发过一个应用系统。它向服务器程序发送不同类型的文件,再另存为另外一种格式的文件。这应该不难。当我开始工作的时候,我震惊地发现,处理每种类型文件的代码都是重复的。所以,我也配合了一下,复制了数百行的代码,改变了其中的两行代码,几分钟之内就让它工作起来,但我却感觉很失落。因为我觉得这有悖于良好的工作习惯。

后来我说服了老板,告诉他代码的维护成本很快就会变得非常高,应该重构代码。一周之内,我们重构了代码,并立即由此受益,我们需要修改文件的处理方式,这次我们只需要改动一个地方就可以了,而不必遍查整个系统。

你深知怎样做才是正确的,或者至少知道目前的做法是错误的。要有勇气向其他的项目成员、老板或者客户解释你的不同观点。当然,这并不容易。也许你会拖延项目的进度,冒犯项目经理,甚至惹恼投资人。但你都要不顾一切,向着正确的方向奋力前进。

美国南北战争时的海军上将David Farragut曾经说过一句名言:“别管他妈的鱼雷,Drayton上校,全速前进。”确实,前面埋伏着水雷(那时叫鱼雷),但是要突破防线,只有全速前进(6)。

他们做得很对!

做正确的事。要诚实,要有勇气去说出实情。有时,这样做很困难,所以我们要有足够的勇气。

切身感受

勇气会让人觉得有点不自在,提前鼓足勇气更需要魄力。但有些时候,它是扫除障碍的唯一途径,否则问题就会进一步恶化下去。鼓起你的勇气,这能让你从恐惧中解脱出来。

平衡的艺术

  • 如果你说天快要塌下来了,但其他团队成员都不赞同。反思一下,也许你是正确的,但你没有解释清楚自己的理由。
  • 如果你说天快要塌下来了,但其他团队成员都不赞同。认真考虑一下,他们也许是对的。
  • 如果设计或代码中出现了奇怪的问题,花时间去理解为什么代码会是这样的。如果你找到了解决办法,但代码仍然令人费解,唯一的解决办法是重构代码,让它可读性更强。如果你没有马上理解那段代码,不要轻易地否定和重写它们。那不是勇气,而是鲁莽。
  • 当你勇敢地站出来时,如果受到了缺乏背景知识的抉择者的抵制,你需要用他们能够听懂的话语表达。“更清晰的代码”是无法打动生意人的。节约资金、获得更好的投资回报,避免诉讼以及增加用户利益,会让论点更有说服力。
  • 如果你在压力下要对代码质量作出妥协,你可以指出,作为一名开发者,你没有职权毁坏公司的资产(所有的代码)。

【注释】

  1. 或更通俗地写成:系统应当如何如何……。
  2. 不需要解雇他,但是他不能继续留在这个项目中。同时也要意识到,频繁的人员变动对整个团队的平衡也很危险。
  3. 通常,这是一个很好的技巧:引导性地提出一个疑问,让他们自己意识到问题。
  4. 莱斯·布朗,全球领军励志演讲家和作家。——编者注
  5. 参见“Debating with Knives”,在http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Random/FishBow1.rdoc。
  6. 事实上,Farragut的原话往往被简化为:“别管他妈的鱼雷,全速前进!”
下一节:即使你已经在正确的轨道上,但如果只是停止不前,也仍然会被淘汰出局。——Will Rogers(美国著名演员)