财新传媒 财新传媒

阅读:0
听报道

 

有一种说法:互联网时代追求软件开发的质量已经过时了。等你把一款软件精心打磨好,黄花菜都凉了。天下武功,唯快不破。快糙猛,迅速抢占市场才是王道。

 

这种说法不能说完全没有道理。有时候时机确实很重要,错过了机会窗口你就是图灵再世也无力回天。

 

但在大多数情况下我坚决反对这种观点。

 

要想领先于竞争对手,光有点子和速度是远远不够的。

必须要建立让别人难以跨越的壁垒。

 

杰出的软件只有杰出的头脑才能创造。快糙猛是无法建立起壁垒的。

 

点子是最容易复制的。世界上很多聪明人,每天都产生大量想法。你以为独一无二的主意,大概率其他某个人也想到了。

 

如果你的产品没有足够的壁垒,即便竞争对手一时还没有你的点子,看到你做出的东西之后他们也很容易依葫芦画瓢。

 

他们有的资源比你多,有的动作比你快。你如何应对他们的挑战?

 

做企业也好,做系统也好,都是一场马拉松。一开始领跑的,不一定笑到最后。如何才能比别人先跑完全程?

 

下面我就结合自己的经验,讨论一下用什么方法才能开发出领先于对手,保鲜期超过十年的软件。所谓“保鲜期”,是指到时候不但能用,用起来还不觉得过时。

~~~~

 

我有什么软件开发经验?

 

自我介绍一下:在一个正在被 cancel 的大学拿了计算机编程语言方向的博士,在谷歌开发软件超过十五年,以系统软件(测试框架、编译器、编程工具、大数据处理基础软件)为主,被垠神喷过。现在在谷歌广告部门带领一个小团队开发介于系统和应用之间的大数据处理软件。热爱写代码,从未离开一线,经常琢磨开发软件的正确姿势。

 

所以,我的观点可能会更适用于系统类软件的开发,但保证不是赵括论战。

~~~~

 

我认为只有走得稳才能走得快。强行求快的结果就是项目中后期的时间都用来补洞了。

 

我在自己开发软件和带团队的时候,都坚持高标准严要求,尽管很多时候这让人抓狂,觉得我多事,影响了别人的开发进度。

 

我承认自己确实有时候要求太高,有拔苗助长的倾向。但是,我希望能影响更多的程序员以开发传世之作为目标,而不是一味求快。

 

突破自己总是需要一个痛苦的过程。停留在自己的舒适区,重复低级劳动是无法提升能力的。

 

相比于团队一时的进度,我更注重于引导程序员发展长期的能力,以卓越为目标,通过日常学习和积累,从普通程序员走向高手。

 

当然,有些人的目的不在于学习。他们只是想把工作对付过去,完成项目,升职套现。

 

这种想法可以理解,毕竟不是每个人的梦想都是要成为伟大的程序员。

 

但我要说的是,在可能的情况下,我不会选择和这样的人共事。如果我自己组建团队,必然是要找热爱编程,有工匠精神的人。大家在一起取长补短,共同进步,开发出让人交口称赞的软件。

 

有人说,你这样太偏执了。软件业的迭代迅速。很多软件,过两三年就没法用了,需要重新开发。这种情况下去追求质量,未免舍本求末。

 

可是,软件行业有一个特点,那就是赢家通吃:在一个领域基本上老大占据 80% 的市场,老二占 10%,其他全部玩家去瓜分最后剩下的 10%。一定要在一个领域内深耕细作才有可能打败对手。

 

所以,开发一款能用十多年的软件,回报远远高于开发五款能用三年的软件。

 

不疯魔不成角。只有偏执狂才能开发出伟大的软件。

 

姐夫丁(Jeff Dean,谷歌资深院士)能设计出 MapReduce 这样改变行业格局的系统,并高质量地实现。他的战斗力抵得上一百个普通程序员。

对软件质量严格把关,是一本万利的事。

~~~~

 

成功的软件必然是很好地解决了用户痛点。

 

是不是应该把用户当上帝,用户要什么我们就做什么?

 

不。这种完全市场导向的行为缺乏系统观大局观,必然导致短视,头疼医头,脚疼医脚。

 

在这种思路下设计出来的系统,好像某国某奇葩总统的策略。东一榔头西一锤子。每加一项功能都好像是改进,但回头一看,100多个功能没有朝一个方向形成合力,产生不了synergy(协同效应),反而增加了系统的复杂性,让用户面对众多选择无所适从。

 

作为设计者,要了解用户明确提出的需求,更要了解用户不知道的需求。

 

什么叫用户不知道的需求?

 

比如说,在汽车发明之前,你去调查市场对交通工具的需求,用户会告诉你他们想要跑得更快的马,想要更舒适的马车。如果顺着用户的要求去搞,永远不会发明汽车,更不会发明飞机,我们今天还会骑着马去上班买菜。

 

要取得革命性的进步,必须要有前所未有的想法。这些想法不能指望用户带给我们。作为系统设计师,必须沉下去,把用户潜在的需求融化进血液里(他们要的是更安全舒适快速地从A地到达B地,而不是更快的马),然后从第一性原理(first principles)出发,真正从根本上解决问题,才不会陷入局部优化的误区。

~~~~

 

老码字工人托尔斯泰说过,成功的软件都差不多,失败的软件各有各的奇葩。

 

确实,绝大多数成功软件都有一个共性:用户越多价值越大。

 

如果你买了大郎烧饼,买烧饼的人多了,并不会增加你手里这一块烧饼的价值 - 它既不会因为销量增长而变大,也不会因为用户交口称赞而更香。所以,要靠卖烧饼发财致富并不容易。大郎再勤快,也无法改变自己被喝药的命运。

 

可是,如果你是微信用户,用微信的亲戚朋友越多,像老万故事会这样的微信公众号越多,微信对你就越有用。这就是网络效应

 

还有一种情况:用户或第三方开发者给系统开发了很多扩展或插件。用户越多,这样的插件就越多,系统就越有用。

 

一个软件的原始作者再强大,精力和时间也是有限的。如果不能吸引更多的人主动参与开发,永远无法做大。

 

所以一个系统软件在开始设计的时候,就必须考虑到如何才能不让设计者成为系统壮大的瓶颈。

 

好的系统架构师,应该潜心于制定系统规则和合约。规则制定好了之后,独立的贡献者可以加入各种新的功能,不会影响系统本身的稳定性。而这一切,不必等待系统管理者的许可。

 

这样,贡献者可以形成一个社区,各种插件、扩展在市场上竞争,优胜劣汰,最后剩下的都是金子。原始开发者、用户、第三方开发者,皆大欢喜。

 

我举几个例子。

 

苹果公司的 Logic Pro 音乐制作软件自带了庞大的虚拟乐器库。可是,它毕竟不能涵盖世界上所有的乐器,总会有人的需求是它不能满足的。

 

但 Logic Pro 有强大的声音采样功能,可以让用户方便地开发出各种不同音色的虚拟乐器。有了这个虚拟乐器制作功能,用户和第三方开发者纷纷贡献新的乐器,形成了一个社区。这个社区的扩大,反过来使 Logic Pro 本身更有吸引力。

 

所以说,现在软件的竞争很多时候是平台的竞争,生态系统的竞争。

 

为什么取代苹果的 iOS 和谷歌的安卓这么困难?因为它们形成了稳定的生态,每个阵营都有很多开发者。这种情况下,系统就有很强的粘性。要开发一个新系统把用户吸引过去,难于登天。即便微软这种要体量有体量要技术有技术的竞争者,也只能望洋兴叹。

 

虽然 iOS 和安卓不会永远是市场的领跑者,但是这种生态优势可以保持很多年,直到下一次模式突变,才会被黑马打破。

 

再来个例子。

 

我在开发 C++ 测试框架 Google Test/Mock 的时候,没有想把用户想要的所以功能都做进去,虽然那样看起来很有成绩。我的想法是把系统本身打造得短小精干,同时提供几种扩充系统功能的机制,比如匹配器(matcher)API 和测试事件监听 API。

 

把这些机制打造完善之后,用户不需要我的审批就可以扩充系统的功能。而且他们开发的这些新功能可以和系统自带的核心功能一样好用,不是什么二等公民。这样用户就有了改进 Google Test 的动力,互相帮助,让系统越来越好,带动了用户群的不断扩大。

 

Google Test/Mock 已经有快 15 年的历史了,现在还是谷歌内部和其他一些大厂开发 C++ 测试的首选。所以标题里说的保鲜十年是完全可行的。

~~~~

 

软件开发是技术和艺术的结合。软件要写得好,必须要会审美。

 

去年(2019)夏天,我和很多同学回到母校中国科大参加校庆。和计算机系的老师们和其他校友座谈的时候,我提出了一个想法,就是人文教育,尤其是关于美的教育,应该是计算机教育的一个重点。

 

一个软件开发者,如果不知道什么是美的东西,对美没有一种敏锐的直觉,是开发不出伟大的软件的。

 

我觉得码工要读一些跟技术无关的闲书,体会好文章的节奏、语感、结构和逻辑,然后在写软件的时候触类旁通。

 

乔布斯早年花了不少时间去学书法。这看似无用的经历和对美的疯狂追求让他主导创造了一系列伟大产品。

 

精妙和优雅的代码,可以让人拍案惊奇,叹为观止。春风十里,不如读一段优美的程序。

 

比如,用 dongbei 语言写的快速排序程序是这样的:

  •  

【排得贼快】(村子)咋整:

寻思:村子有几个坑 跟 零 一样一样的?

要行咧就 滚犊子吧 村子。

 

杆子 装 村子的老大。

几个杆 装 零。

矮墩庄 都是活雷锋。

高个庄 都是活雷锋。

哥们儿 在 村子 磨叽:

寻思:哥们儿 跟 杆子 一样一样的?

要行咧就 几个杆 走走。

要不行咧就 寻思:哥们儿 比 杆子 还小?

要行咧就 矮墩庄 来了个 哥们儿。

要不行咧就 高个庄 来了个 哥们儿。

磨叽完了。

 

 

顺溜庄 都是活雷锋。

矮墩 在 整【排得贼快】(矮墩庄)磨叽:

顺溜庄 来了个 矮墩。

磨叽完了。

还行 从 一 到 几个杆 磨叽:

顺溜庄 来了个 杆子。

磨叽完了。

高个 在 整【排得贼快】(高个庄)磨叽:

顺溜庄 来了个 高个。

磨叽完了。

滚犊子吧 顺溜庄。

整完了。

 

看完,是不是整个人都顺溜了?

~~~~

 

我为什么要写公众号?原因之一是写文章的能力会影响到写软件的水平。养成把话说清楚的习惯,系统才会设计得有条不紊,代码才会正确好看。

 

伟大的软件源码看起来赏心悦目,不光逻辑清晰不拖泥带水,而且举重若轻,正确性一目了然。

 

反之,坑爹的软件源码三棍子打不出一个屁,不知道它到底想要做啥,更搞不明白它到底对不对。

 

我喜欢讲软件开发有三个层次:第一层是正确性,保证软件做它应该做的事情;第二层是高效率,让软件用尽量少的代价做它的事情;第三层是可读性,让读者一看就明白代码是正确而且高效的。

 

我所了解的成功的程序员,有一个共同的特点就是表达能力特强,能够用简洁的语言抓住重点,在很短的时间内讲清问题的本质。

 

这不是偶然。表达清楚,说明问题想得清楚,也更容易影响别人,达成自己的目标。

 

包括我在内的很多人在这方面的是有欠缺的,常常词不达意,花了很多时间,自己都觉得自己没有讲清楚,更何况听众。

 

而这方面的能力不是能够突飞猛进的,因为它不是一两个单独的知识点的问题,而是涉及到人的整个思维习惯和知识体系。

 

我希望通过写作来锻炼自己的表述能力,帮助自己写出更好的代码。

 

建议码工朋友们在提升自己表达力上面下下苦工。这是个辛苦的过程,但是回报也是巨大的。

~~~~

 

如果一个人没有代码洁癖,没有文字洁癖,那么他可能根本意识不到自己写的软件的各种问题。在他看来,自己的代码跟伟大的代码比也没有差到哪里去。

 

不明白自己差距在哪里,又谈何改进呢。

代码要写得好,必须要会吹毛求疵。

 

在做代码审查的时候,我经常会提出一大把意见,从设计到实现,从正确性到可维护性,从效率到风格,甚至注释的语法、标点符号的使用、大小写的规范性,事无巨细我都会提出来。

 

当然,有的时候我也需要做一个权衡:如果挑刺太多,可能会造成被挑者的心理创伤。尤其是在他还没有适应我辣手摧花风格的时候。

 

这时候我会先抓主要矛盾,提出百分之八九十的问题。剩下的问题,在今后的工作中再慢慢指出。

 

即便是这样,我通常提的意见也会是其他审查者的四五倍。但我相信,在代码质量上一开始多发一点力,我们今后就会省下十倍的时间,甚至不止,因为软件质量问题造成的后果可能相当严重。

 

去年 5 月份,我的团队依赖的一个组出了事故,因为一行错误的代码,造成了全球性的系统故障,都上新闻了。然后几十个工程师每天加班,周末无休,几乎一个月才把问题基本解决。

 

事后复盘,如果开发的时候严格把关,是有好几次机会可以避免这个事故的。遗憾的是它还是发生了。但是如果我们从中吸取教训,坏事也能变成好事。

 

~~~~

 

软件系统的 API (应用编程接口)非常重要。API 决定了用户会怎样使用系统。如果 API 定义得不好,系统用起来就费劲,还容易出错。

 

初学者在开发的时候往往陷入一个误区,认为系统的功能越强大越好,而不会去做减法。

 

其实很多时候,系统不能做什么比系统能做什么还重要。

 

为什么这么说?

 

我们不希望系统被误用。可用户多了什么样的人都有,指望用户不犯错误是不可能的。基本上,所以可能犯的错误都会有人犯一遍。

 

一个好的系统,应该设计得很健壮,从根本上杜绝用户犯某一类错误的可能性。

 

比如,在 C++ 中合理使用 const 变量和 unique_ptr(唯一指针),可以防止一大类不小心改变参数值的逻辑错误和内存管理错误。

 

我们在设计 API 的时候,要让正确的事情容易做,错误的事情很难做甚至根本就不可能做。这样的软件才可能长治久安。

~~~~

 

软件开发不是一个一蹴而就的过程,很多时候一开始的想法跟最后的实际会差了十万八千里,所以要不停修正。

 

但是大多数人都喜欢开疆拓土而不是深耕细作。守业哪有创业有意思有成就感呢。

 

可一个千疮百孔的系统是不能支撑它的开发者开疆拓土的。这时候,洁癖就会派上用场了。

 

有代码洁癖的人,看见系统里面的问题,如芒在背如梗在喉,必须拔之吐之而后快。比如他们看见某个 API 有问题,就会想办法把它改得更好。

 

这个时候,光定义一个更好的 API 去取代旧的 API 还不够,因为用户很少会主动把他们的相关代码用新的 API 重写一遍。

 

如果一个 API 有公司外部的用户,只能给他们留足够多的时间(两三年)重写代码。这段时间内必须支持旧的 API 。

 

如果这个 API 只有公司内部用户,我们可以主动修改用户代码,帮助他们更快更轻松地完成迁徙。

 

听起来这事很费劲,但是在好的工具支持下是完全可以办到的。

 

比如在谷歌内部,有代码搜素工具找到一个 API 的全部使用点,有代码改写工具按预定的规则把这些使用点自动替换为新的代码,有代码格式化工具把新的代码自动重新格式化,保证格式规范,还有代码提交工具把大规模的改动化整为零拆成小包分发给各自的负责人审查。

要舍得投入时间把旧的 API 删掉,系统才能不停的进步。如果一个公司内部没有我上面说的这些工具,也许应该考虑投入一些人力开发出它们。

 

代码洁癖还包括不能忍受系统中有冗余的部分。这部分代码会增加系统的复杂性,提高系统的维护成本和出错机会,所以要及时剔除,让每一行代码都有它存在的价值。一个优秀的程序员,能够主动发现系统中的这一类问题,而不是仅仅忙着完成老板交给的任务。

 

我在评价一个程序员工作的时候,不光看他完成了多少项目,还会看他主动为系统做了哪些清理优化工作。

 

我知道,在一个公司提升职员的时候,完成新的项目是最容易量化的标准,所以我会刻意纠偏,保证真正为系统优化作出贡献的程序员得到他们的回报。同时,如果一个人自己动作很快,却给别人留下一堆擦屁股的工作,这样的人我会毫不犹豫减分,确保公平。

~~~~

 

拉拉杂杂扯了这么多,肯定有不准确、不全面、不正确的地方。欢迎大家留言讨论、指出,共同进步。

 

 

 

 

 

 

 

话题:



0

推荐

老万故事会

老万故事会

156篇文章 18天前更新

老万,基层程序员。智商配置一般,主频较低,小内存患者。文化程度介于《知音》和《故事会》之间。偶尔写几个字,发在财新和微信公众号“老万故事会”(laowangushihui)。

文章