The mythical 10x programmer

Salvatore Sanfilippo

传说中的 10 倍程序员

在编程神话中,10 倍程序员是指工作效率能达到另一个普通程序员十倍的程序员。这里的普通程序员,可以理解为能够做好本职工作、但不具备 10 倍程序员那种神奇能力的人。不过,为了更准确地刻画“普通程序员”,更好的说法是:他们代表的是这门专业中职业程序员的平均编程产出水平。

对于这种“怪物”是否存在,编程社区的看法极其两极分化:有人说根本不存在所谓的 10 倍程序员;也有人说这种程序员不仅确实存在,如果你知道去哪里寻找,甚至还能找到 100 倍程序员。

如果把编程看成一门“线性”的学科,那么 10 倍程序员显然像是一种不合常理的可能性。一个跑步者怎么可能比另一个跑得快 10 倍?或者,一个建筑工人在相同时间内怎么可能建造出另一个工人十倍的东西?然而,编程是一门设计学科,而且是以一种非常特殊的方式成为设计学科的。即使程序员不参与程序实际的架构设计,实现程序的过程仍然需要对实现策略进行次级设计。

因此,如果程序的设计和实现并不是线性的能力,那么在我看来,经验、编码能力、知识以及识别无用部分的能力等因素,就不只是线性优势;在创建程序的过程中,它们会以乘法的方式共同发挥作用。当然,当程序员既能处理程序的设计,又能处理程序的实现时,这种现象会更加明显。任务越是“目标导向”,潜在的 10 倍程序员就越能利用自己的能力,以少得多的努力实现目标。而当手头的任务更加僵化,对该使用哪些工具、应如何实现都有具体规定时,10 倍程序员在更短时间内完成大量工作的能力就会受到削弱:他们仍然可以利用“局部”的设计可能性来做出好得多的工作,但无法以更深层的方式改变达成目标的路径;而这条路径甚至可能包括将规格中的一部分彻底从项目中删除,从而让最终目标看起来几乎一样,但实现目标所需的努力却大幅减少。

在作为程序员工作的二十年里,我观察过许多与我共事的程序员,也观察过那些在我的指导下努力实现既定目标、为 Redis 和其他项目提供补丁的人。同时,很多人告诉我,他们认为我是一个编程速度很快的人。考虑到我远非工作狂,我也会以自己作为快速编写代码的参照。

下面列出的是我认为对程序员生产力影响最大的几项素质。

  • 基本编程能力:完成子任务

    程序员最明显的局限或优势之一,就是处理实际实现程序某个部分这一子任务的能力:一个函数、一种算法,或者其他任何东西。令人惊讶的是,据我的经验,能够非常高效地使用基本的命令式编程结构来实现某种功能,并不像人们想象的那么普遍。在团队中,我有时会看到非常不称职的程序员,他们甚至不知道一个简单的排序算法,却比那些理论上能力极强、但实践中非常不擅长实现解决方案的科班出身程序员完成了更多工作。

  • 经验:模式匹配

    我所说的经验,是指针对一系列反复出现的任务,已经探索过的解决方案集合。一个有经验的程序员最终会知道如何处理各种子任务。这既能避免大量设计工作,尤其还能成为对抗设计错误的极其有力的武器,而设计错误恰恰是简单性的最大敌人之一。

  • 专注:实际时间 VS 假想时间

    如果不考虑时间的质量,花在写代码上的小时数就没有意义。缺乏专注可能由内部和外部因素造成。内部因素包括拖延、对手头项目缺乏兴趣(你不可能擅长做自己不喜欢的事)、缺乏锻炼或身心状态不佳、睡眠不足。外部因素包括频繁的会议、没有真正独立办公室的工作环境、同事经常打断你,等等。显然,努力提高专注度、减少打断,会对编程生产力产生不可忽视的影响。有时,为了获得专注,需要采取极端措施。例如,我只会偶尔查看邮件,而且大多数邮件都不会回复。

  • 设计取舍:牺牲 5% 换取 90%

    复杂性往往产生于这样的情况:人们不愿意承认,项目中一个非核心目标占用了大量设计复杂度,或者由于核心功能与非核心功能之间存在设计张力,使得另一个更重要的目标变得非常难以实现。设计者必须能够识别设计中所有并非轻松可得的部分,也就是说,付出的努力与获得的优势之间不存在成比例的关系。一个为了最大化产出而执行的项目,会恰恰专注于那些真正重要、并且能够在合理时间内实现的方面。例如,在设计消息代理 Disque 时,我曾意识到,只要为消息提供尽力而为的排序,项目的其他所有方面都可以得到大幅改善:可用性、查询语言和客户端交互、简单性以及性能。

  • 简单性

    这是一个显而易见、却又几乎什么都能包含的要点。要理解什么是简单性,不妨看看复杂性通常是如何产生的。我认为,复杂性的两个主要驱动因素,是不愿意进行设计取舍,以及设计活动中错误的不断累积。

    如果思考设计过程,就会发现,每当我们沿着错误的方向前进,就会离最优解越来越远。最初的设计错误落到错误的人手里,不会促成对同一系统的重新设计,反而会导致为了弥补最初的错误而设计出另一个复杂的解决方案。因此,项目会在每一步错误之后变得更加复杂、效率更低。

    实现简单性的方法,是用头脑中的小型“概念验证”来进行推理,从而在程序员心中探索大量简单设计,并从看起来最可行、最直接的解决方案开始着手。之后,经验和个人设计能力会帮助改进设计,并为那些需要解决的次级设计找到合理方案。

    不过,每当必须采用复杂的解决方案时,都应当花很长时间思考如何避免复杂性;只有在考虑过完全不同的替代方案后仍找不到更好的可能性,才应继续沿着这个方向前进。

  • 完美主义,或者如何扼杀生产力并让设计产生偏差

    完美主义有两种形式:一种是工程文化,要求让程序达到可度量的最佳性能;另一种则是一种人格特质。在这两种情况下,我都认为它是程序员快速交付成果的最大障碍之一。完美主义以及对外界评判的恐惧,会引入一种设计偏差,导致人们只根据心理因素或那些可以轻易度量的参数来改进设计,从而做出糟糕的选择;而健壮性、简单性、按时交付的能力等因素,往往从未被纳入考量。

  • 知识:掌握一些理论会有所帮助

    处理复杂任务时,关于数据结构、计算的基本限制以及非常适合对某些任务建模的非平凡算法的知识,会影响找到合适设计的能力。并不要求你成为无所不知的超级专家,但至少了解针对一个问题的多种潜在解决方案,当然会有所帮助。例如,将设计取舍(接受一定比例的误差)与对概率集合基数估计器的了解结合起来,就可以避免为了统计数据流中的唯一项目而采用复杂、缓慢且内存效率低下的解决方案。

  • 底层:理解机器

    程序中的许多问题,即使使用高级语言,也源于不理解计算机将如何执行某项任务。这甚至可能导致由于所使用的工具或算法存在根本性问题,而不得不从头重新设计并实现整个项目。扎实的 C 能力、对 CPU 工作方式的理解,以及对内核如何运行、系统调用如何实现的清晰认识,都可以避免在项目后期遭遇糟糕的意外。

  • 调试能力

    为了查找 bug,很容易投入海量工作。逐步获取 bug 状态信息、以一组理性的步骤修复 bug 的能力,再加上编写不太可能包含过多 bug 的简单代码的态度,两者结合起来,会极大地影响程序员的效率。

对我来说,看到上述程序员素质能够让产出产生 10 倍的差异,并不令人惊讶。它们结合起来,可以让人很好地实现那些从可行模型出发、并且可能比替代方案简单好几倍的设计。有一种强调简单性的方式,我喜欢称之为“机会式编程”。基本上,在每个开发步骤中,都要选择对程序用户群影响最大、同时所需努力最少的功能集合来实现。