从玩扑克到软件开发
英文原文:Software Development Lessons Learned from Poker
(作者:Jay Fields,译者:李剑)
2008年5月19日
我以前不是做软件开发的。在加入ThoughtWorks两年之前,我主要靠玩扑克为生。当然,如果你曾跟我打听过我前臂上的纹身,那你肯定已然听过我的故事了。要是还没有,等下次我们一起喝一杯时,我可以讲给你听。
我从未因为花这么长时间玩牌而感到过遗憾,从中我学到了一些放之四海而皆准的知识。开发软件的时间愈久,我就愈加确信这二者之间具有令人难以置信的相似性。
学习
我学习打扑克和学习软件开发的方式是一样的:尽可能多读书。我用两年的时间,读完了所能找到的每一本有关扑克的书。最后竟至39本之多。编程亦如是。此刻,我面前仍然摆着接下来要读的5本书;而在过去三年ThoughtWorks的工作中,我翻烂的书亦不在少数。
我认为,无论编程还是玩牌,阅读书籍、博客与杂志都是要想有所成就的必备条件;而若要以二者为谋生之业,仅靠读书却是远远不够的。也许你可以把书本上的一切知识都装入脑中,但知道在何时应用何种规则,这才是真正高手的标志。
诚然,开卷有益。但总要走过万里路,方能对应用特定技术的具体环境烂熟于心。书本不可能把所有情况都囊括一空,只有通过亲身体会得来的经验,才能让你在某些状况下为自己或是雇主做出快速而正确的决策,而这些决策可能价值几千乃至数百万美元。
经验之宝贵,世间无物可代。
艺术的巅峰
你可以设计出击败普通扑克玩家的计算机程序。遵守一些基本规则,自然就可获胜。但迄今为止,还没有任何程序可以击败最好的扑克玩家。因为扑克技能达到巅峰时,也就成了一门艺术。软件开发亦如是。要想成为一个还行的开发者,只要遵循一系列最佳实践即可。如果按照经典参考指南一类的书籍行事,开发出还不错的应用程序应该不成问题,而且效果会胜过其他最常见的做法。有了这么多失败的项目作为前车之鉴,我相信,还不错的应用就足以令大多数管理层甘心掏腰包了。
当然,有些经理有更高的标准。在银行、创业公司、医疗系统等领域,标准则更为严苛。“还不错”自是远远不够。那些经理会很乐意为最佳选择买单,他们期待的是远超常人的技能。但问题在于,专家级程序员的技能与普通程序员不同。普通程序员知道做事的方式;专家知道做事的目的。普通程序员会僵化跟随模式书籍中的指示,就如遵守参考指南一般;专家则明白对模式的创新可能会带来指数级的性能改善。
他们看到的绝非同一个世界,所以普通程序员很难跟专家对面交流。做艺术评论家易,做优秀的艺术评论家难。
决策技能
在扑克和编程中有一条绝对真理:几乎没人能像他自我感觉的那么良好。有自知之明是不错的开始,但人们依然很难知道自己与专家之间的差距。程序员接触专家的机会并不多,也就无法公正评判自己的技能。在牌桌上,每个人都是为了锦标而来,可大多数人都会过高评价自己的牌技,这总是让我惊讶不已。
程序员之间亦是如此,而且大多数人可以获得的信息更少得可怜。一个从不参加任何大会的技术领导人,只能跟自己的团队成员一比高下。当然,他可能已经很优秀了,否则也不会成为技术领导。但如果与整个行业中最出类拔萃的人相比,他又处于什么位置?如果觉得在自己的圈子里已经一览众山小了,那碰到不同意见时,他又会作何反应?有些人会视之为学习的契机并为此感到兴奋,但绝大多数都会对不同意见嗤之以鼻。
团队协作
乍看上去,扑克是一种彼此对抗的游戏。但事实很少如是。即使在赌注最小的牌桌上,通常也至少会有几个人常打交道。他们不会达成条件一致对付牌桌上的其他人——他们也不必如此。大家都明白一条道理:你不是要去跟牌玩得好的人对着干,赢他们的钱,而是要从水平低的人身上赚钱。专业牌手甚至会像一个团队一样协同工作。有些人彼此利益相关,故而一人得利则众人均有收益。他们不仅互相了解,而且认识很多人。如果出现一局精彩牌局,楼层经理会跟他们打招呼;侍者会为他们的对手调制酒精度高的饮品;和手(即发牌者)会故意“犯错”以影响某人心情(很少有人在心情不好的时候能够打好牌)。每个人都在协同工作,确保大家都能挣到钱。
颇为有趣的是,程序员的情况也与之相似。很多人都坐在格子里,完全依赖自己解决问题。他们往往工作在代码个人独有制模式之下。我曾亲眼目睹,在这种程序员交付的应用中,集成问题一直都是大家的心病。而更为不幸的是,集成之痛还只是最小的问题。假设IT部门把业务需求锁定为500页的需求文档。如果公司决定改变业务方向,随之而来的系统变更需求将令人痛不欲生。数以百万计的金钱付诸东流,因为程序员开发的特性已不再具备业务价值,而IT部门还没有找到更好的方式来应对业务变化。
当然,情况并非总是如此。专家懂得协作。他们会跟其他专家协作,但也不排斥与经理、客户、业务部门、分析师、质保人员,以及所有可以为成功贡献力量的人协作。他们胸怀大局:只有协作,才能让每个人有所收获。
度量
雄心勃勃的牌手常常讨论他们赢了多少手牌,又输了多少手。他们讨论最多的还是本该赢但却输掉的那几手牌。有时人们会犯错误输钱,但他们一般都不会记得这几手是怎么输掉的。相反的是,如果有些牌局只是因为手气不好而输掉,他们就会记得那一局中的每一处细节,他们还会在故事中透露对手必然获胜的几率,来证明自己根本没有胜出的机会。真正的牌手知道他们输掉过多少手牌,以及失败的大概几率。他们懂得度量。而且专业牌手会专注于重要的度量标准。你赢了多少手,输了多少手,这无关紧要;重要的是你赢了多少钱,输了多少钱。而且,为你的狗屎运(译者注:bad beat,即开局时输家比赢家牌好,赢的几率更大,但关键时刻赢家却来了更好的牌。碰到狗屎运——take a bad beat——用来形容输家,来了狗屎运——lay a bad beat——用来形容赢家)苦恼实际上等于替你对手的牌运犯愁。既然你的收入来自于对手的错误,那你就是在抱怨为什么对手把钱给了你。
有度量标准是好事,不过专业人士懂得哪些标准重要,哪些只会分散注意力,哪些介于二者之间。
软件开发也有很多度量标准,而且有很多标准身上的光环已经远远超出了它们所应有的范围。例如,知道代码行数几乎不能带来任何价值。复杂应用需要相当多的代码,但这个“相当”到底是多少?它得依赖于语言、工具及其他因素。
修复的bug数量也是个很有趣的话题,只是略逊于前一个。为什么人们会在乎修复了多少个bug?Bug数量也许有其价值,但是修复的bug数目并不能为我们带来多少有用信息。
特性完成率是我自己最喜欢把玩的一个标准。除非我们使用特性来评估工作量,否则知道完成了多少特性又有何用?而且,如果已经对工作量做出了评估,那为什么不把剩余工作与已完成工作相比较,从而得到工作进度呢?我很难从特性完成率中看到价值所在。
代码覆盖率让我想起了记录狗屎运。这项度量是有意义的,但很多人都没抓住重点。代码覆盖率低意味着可能有问题存在,但是代码覆盖率高只能表示你有一个很大的代码覆盖率数值。高代码覆盖率与高质量之间没有必然联系。
注意人,而不是逻辑
如果看过有玩牌镜头出现的电影,你大概听过这样一句话:你不是在与扑克玩,而是与人玩。此言极是。牌手无疑都是心理学家。有时你确实需要某些牌,但拿一手好牌只是赚钱的一部分而已。一旦有了好牌,你就需要知道怎样利用好它们。你是应该加注,还是先让牌然后加注,还是彻底让牌,还是跟进?这些做法依赖于很多因素,但关键还是要了解牌桌上的对手。当你得到一手好牌,首要目标就是尽可能多地从对手那里赢钱,而达到这种目的的唯一方式则是想办法让对手给你更多的钱。了解逻辑可以帮助你赢得几手牌,了解人则可以帮助你赢钱。
在交付软件时,人处于同样重要的地位。如果软件只是让一切工作起来,那只要把它变成自动化的工作,事情就容易得多了。但软件却远非功能组合这么简单。在一场高尔夫球比赛中,人们会卖出软件包;在全家到迪斯尼免费旅游时,人们会签下软件服务合同;为了避免法律纠纷,人们会履行合同去构建已经毫无用处的软件;为了超越竞争对手,人们会使用软件来加快业务响应速度。
人们使用软件、开发软件、维护软件,或是在某种程度上依赖软件。软件开发与这个世界有着千丝万缕的联系,要把洋洋洒洒的变量组合成简单方程,生产出高质量的软件,又与登天何异?但是,软件开发高手需要考虑每个人引入的所有已知与未知的变量,做出他们力所能及的推测。知道应该做什么会让你受益,而知道必须做什么所带来的价值却是难以衡量。了解逻辑可以帮助你交付应用,了解人则可以帮助你交付价值。
在残缺的信息下工作
有关这点,刚开始打牌的人处理的非常好:打好每一手牌,老老实实押注,从不虚张声势。这便是了,新手就应该只做该做的事情,除非你的钱多得没地方花了。难点在于如何从初学者的水平提升。大量信息霎那间纷至沓来,你需要注意牌桌上每个人的每一处细节:他们怎样交流,你从前跟他们每个人打过什么交道,他们所钟爱的玩牌方式,谁在赢,谁在输,凡此种种不一而足。而且,你也不可能知道对手手里的牌是什么,下一张牌又是什么。你所拥有的信息已超出所能处理的极限,而且这远非全部。
编程亦如是。领域专家无所不知,但把一切都向你倾囊相授却毫无意义。何况,你也不一定需要所有的领域知识。你需要熟悉团队,但同事总有些事情是你永远无法知晓,或者不能完全理解的。不过,编程高手能够把必要的领域知识融会贯通,掌握团队的动态,并始终提供技术上的真知灼见。他们知道他们永远无法成为百晓生,他们知道什么事情值得思考,哪些应该置之不理。纵使面前汹涌澎湃的信息仍是残缺不全,他们也总能做出正确的决定。
即时反馈
普通牌手在反馈信息少的游戏中表现最好。因为牌手是根据信息而赢钱的。在5张牌梭哈中只有一轮押注的机会。各位玩家只有一次机会来分析你给出的信息,而你也只有一次机会犯错。专家级牌手更喜欢多轮的游戏。游戏中的回合数越多,他们就有越多机会从低水平的对手身上捞到好处。他们喜欢即时反馈,并根据反馈做出调整。在有多个回合的游戏中,每一个回合都可以得到反馈,专业玩家就会根据当前局势调整打法。
编程高手同样喜欢即时反馈。从业务人员即时反馈回来的信息,可以避免你在构建业务应用时走上弯路。从另一个程序员即时反馈回来的信息,可以在软件产品化之前发现bug。持续集成服务器可以提供即时的集成反馈,从而避免集成之痛。喜欢敏捷的人能马上说出迭代是一个有着显著成效的实践,因为它可以让程序员和业务人员得到即时反馈。不过,作为一个编程高手,纵使他不喜欢敏捷,他也能够意识到即时反馈的价值;即使在非敏捷的环境中,他也会争取得到更多的反馈,从而避免浪费时间精力。即时反馈可以让你了解前行的方向正确与否,每一个专家都会珍视这些信息。
上下文为王
无论扑克还是编程,没有绝对正确或是绝对错误的选择。如果你有一对K,那么在翻牌之前你该不跟么?也许吧。这要看你是在打比赛还是赌钱、有上限还是没上限、你坐在哪个位置上、你是否已经不跟过一次还是已经封顶了等等。我在扑克中学到了一点,那就是在给出答案之前,一定要综合考虑所有的因素。
在编程中沉浸的时间愈久,同样的体会在我心里就愈加深刻。Java在有些时候是不错的选择,但它并非万能。所有的编程语言均如此。工具亦然。Hibernate很不错,但它不适用的地方还有IBatis,当IBatis也不适用的地方还会出现或自己创造新的解决方案。几乎没有一款解决方案能够绝对有效,它只有在恰当的形势下才会发挥应有的作用。在错误的环境中,它也许会成为毒药。
所以,面对一门新的语言或者工具,无论是你是打算弃若敝履,或是爱不释手推而广之,不妨先想想它的适用环境,尽量做到对症下药,量体裁衣。