1947年9月9日,美国海军准将格蕾丝·霍珀在哈佛大学计算机实验室使用MarkII和MarkIII计算机进行研究工作。她的团队追踪了MarkII上的一个错误,操作员发现该错误是由飞蛾进入MarkII的继电器引起的。团队移除了飞蛾,一切恢复正常。当时的工作人员记录了这样一条日志:“发现bug的第一个实际案例”。这个著名的事件就像打开魔盒的潘多拉。从此以后,bug在程序员的世界里到处乱飞。▲全球首创bug谈:如何为bug找借口?在我担任过的角色中,有一个职位叫做DevelopmentManager,通常简称为DM。记得在一次基于平台的二次开发项目中,因为bug太多,我们几乎用了一个milestonecycle来debug,于是我的DM有了新的解释:DebugMan。没有人喜欢虫子。Bug意味着失误、不确定性、超时、交付风险……负面词再多堆起来也不会多余。随便找个有过一两次项目经验的开发者,问问他的调试记忆,气氛就跟上坟一样。对于bug,开发者的神经往往是非常敏感的。有一个笑话很有意思——“我应该如何向程序员报告错误?”你不能直接告诉他:“这是错误的,你的程序有错误吗?”你要这样说,会直接被他瞪回去:“你自己不会用!”。你可以换个说法:“咦,这里好像不对,是不是我搞错了?”这时,程序员的心猛地一跳:“靠……难道我的代码有bug?”这个现象挺有意思的:有时候,当一个bug被发现时,错误的始作俑者会开玩笑地自拔:“谁没写过bug,Windows还是有bug的。”我也用过,感觉很好用,比如:梅西可以罚点球,我罚空门,可以理解。但实际上……这个逻辑经不起推敲。Windows操作系统,一个历时30多年的庞大工程,装机量估计超过地球人口,复杂程度只能猜测。据微软公布的信息:Windows95代码量约1500万行;WindowsXP代码量约4500万行;WindowsVista代码量约5000万行;Windows7代码量5000万+万行。以Windows7为例,有超过5000万条代码,23个团队,1000多人的开发团队。产生如此规模的bug,和一个人在办公室工作一天,写了200行代码,然后搞出一堆bug,把项目搞得一团糟,这能比吗?最后,淡淡的说一句“微软也有bug”,你不觉得丢人吗?所以后面就没有用这句话了,就这么借口,水平太低了。我将在稍后讨论它的替代方案。思考:我们可以消除错误吗?为了对抗bug,人们发明了各种工具和手段,小到方法论,大到生产工具。越来越先进的IDE,复杂的codereview系统,从单元测试到集成联调,再加上beta版、试用版、公测等等。所有这些都有消除错误的相同目标。但这些令人眼花缭乱的解决方案的存在证明了一个悲剧:人类太容易犯错误了。如果说任何事情都有正反两面的意义,那么虫子的正能量就是创造了大量的就业机会,从而维护了社会的稳定。那么,为什么我们总是无法避免错误呢?我们可以杜绝错误吗?答案当然是不可能的。因为那样的话,程序员的日子岂不是太舒服了?不适合吃苦耐劳的位置。而且,在我们生活的这个世界上,越是喊着要淘汰的东西,就越是普遍存在。就像苍蝇、蚊蚋、污染、犯罪、战争,这样的例子不胜枚举。按照常理来说,经验越多的老手写的代码,一次通过的几率就越大。比如他们会思考的更全面,对异常的判断和处理更精良,对边界条件的把握更准确等等。所以我们可能会幻想:只要我们足够细心,努力磨练自己的技能,让一部分码农先有经验,然后共同成熟,最终就能实现全世界开发者联合起来的大解放。消除错误?不幸的是,这只是治标不治本,不是长久之计,因为bug是有等级的。老手bug比较少,但是低级错误很少,也会遇到bug,而且他们的bug往往是难度系数为N的难题。在设计层面,或者直接一些不可抗拒的因素(你有没有遇到过完成了政府项目?)。总而言之,新人有新人的挑逗,大叔有大叔的短路,老手也会有自己的滑铁卢。错误或功能请求?bug概念的由来表明了它的必然性。世界上第一只虫子是飞蛾,谁能料到这一幕?从某种意义上说,bugs是可以提前预测和准备的不可预见的错误。它们被称为异常,trycatch是它们的朋友。至于为什么会出现bug,荷兰著名计算机科学家EdsgerW.Dijkstra有一句经典的话:如果说调试是去除软件bug的过程,那么编程一定是把min的过程。这就是上面提到的“Windows也有bug”的另一种选择。:)想象一下,当你从头开始写一段代码,中间的任何时刻,你的程序都跑不起来,至少达不到目标效果。在实用性上,它完全等同于一堆满是bug的代码。你可能会争辩说,程序还没写好,功能还没实现,没有bug。事实上,换句话说,缺少功能和包含有缺陷的功能对用户来说都是无用的。一段在开发阶段还没有写的代码和已经写完了但是有缺陷的代码是一回事。由此可以推导出一个著名的命题:那不是bug,这是一个特性请求。 有时,我们很难区分问题是错误还是功能请求。在文中,作者抛出了一个案例:用VisualStudio构建WindowsGUI程序时,没有使用系统默认字体。这是一个错误吗?很难说。毕竟,随着软件应用越来越普及,所谓人性化趋势越来越被追求,那种认为只要程序能运行就不是bug的传统观点正在慢慢改变。对于一个有强迫症的癌症用户来说,UI存在缺陷,基本上整个软件都不能用。事实上,在如今各种APP竞争激烈、同质化的时代,用户体验上的问题往往是致命的。以前大家都不用选,所以也没那么挑剔,只要程序能搞定就行。今天的电脑用户已经被宠坏了,在这样的时代,bug已经悄然泛滥。那么,如何编写没有错误的代码呢?答:没有编码。一个悲观和绝望但唯一正确的解决方案。尽量少写代码,试着在这种绝望中寻找一点希望。这个答案暗示了一种方法:编写尽可能少的代码。因为Dijkstra大师已经说得很清楚了,编程就是创造bug的过程。好吧,你写的代码越少,你犯错的机会就越少,这是显而易见的。我们很容易有信心维护一个300行的代码;当我们接手3000行代码时,反应取决于每个人的素质。现代开发方式也包含了这种思想,从IDE的智能提示、代码补全功能,到各种语言都有的各种“从入门到精通”的开发框架,还有很多实用的约定,都在帮助开发者减少不必要的编码。框架和规范的思维可以减少出错的可能性。事实上,即使是编程语言本身的历史发展,也是按照这种思想进行的。从底层的汇编语言,到C/C++,再到Java/C#/Python……等高级语言,语言进化的目标之一就是将程序员从脏活累活中解放出来。“不重新发明轮子”的精神,一方面在指导我们提高效率,不重复劳动成本,另一方面也在减少重蹈覆辙的机会。当代web开发中的各种包管理理念都在深刻践行这种精神,以至于2016年3月爆发了著名的npm&left-pad事件:一串11行填满功能模块,被全世界所使用,结果,那天当作者Azer去掉模块包时,全局前端崩溃了。受影响的产品和团队甚至包括著名的React!这件事让人反思:我们是不是忘了怎么编程了?一个简单到人人都会写的功能,但都选择导入,而不是自己实现。最终,太多了。写代码真的很难。没有错误,没有代码。为什么要追求无错误?但是,如果真得不写代码了,那已经没有女朋友,现在连代码都没有的程序员,这还叫人活吗?不能这样杀程序员,还得谈人权。有时候,当答案实在让人无法接受时,我们就应该想想这个问题是不是错了。那么,换个角度,为什么要追求无bug呢?也许我们根本不需要害怕bug。有BUG的地方就有麻烦,有麻烦的时候就需要解决这个麻烦。能解决麻烦的,客户付钱。只处理简单的问题是没有价值的。市场只认可那些能为困难提供解决方案的人。简单的说,要想赚钱,就不要怕麻烦。对于客户来说,无论是bug还是featurerequest,都是需要解决的问题。一个优秀的PM可以将客户报告的bug打包成featurerequest,返回一套解决方案。然后,优秀的业务代表出来签署补充协议。恭喜你,你的项目资金又增加了一点。英国有句谚语:哪里有泥巴,哪里就有黄铜。从这个角度来看,“如何写出没有BUG的代码?”这个问题可能确实是错误的。
