执行:想法是最容易的那一环
第二根支柱——把一个判过的想法,变成一个真正能跑的东西
这是三篇支柱文章的第二篇。第一篇讲品味,说的是好想法何其难得——稀有,分布的尾巴,经年判断力慢慢结出的果。于是这一篇的标题听着像自相矛盾:想法是最容易的那一环。不矛盾,是换了参照系。想出想法难;可一旦品味递给你一个值得一年的想法,把它做成真的才是整件事的生死之地——而这恰恰是人们挥挥手打发掉的部分:「不过是工程罢了。」这声轻慢,就是本篇要治的病。
因为好想法配上坏执行,不是打了折扣的好想法。是零。想法只是个乘数;在有东西执行它之前,它一文不值。拿你的绝妙想法乘以零执行,得数你自己知道。
乘数最清楚的例子,也是我们行里最疼的一个。整个七十年代,Xerox PARC 造出了图形界面、以太网、激光打印机,还有把它们串在一起的 Alto——现代个人电脑里相当大的一块。(鼠标出自 SRI 的 Engelbart 实验室;PARC 把它做到了能用。)Xerox 兑现了其中一部分——激光打印确实成了生意——却把电脑本身砸在了手里:Alto 从未作为产品发售,1981 年的 Star 一败涂地。Apple 和 Microsoft 在界面上执行到位,拿走了整个行业。那些难的想法早已被发明出来,摆在那里好几年。再看 Friendster:它比 Facebook 更早做出社交网络,还领先几百万用户起跑,输掉的原因里很大一块是不体面的工程——页面出了名地慢,据称一开要好几秒,而扩容问题始终没有修好。同一个想法。不同的执行。不同的历史。执行不是重要工作之后的那道工序;它常常就是重要工作。
三件不是一回事的事:coding、programming、hacking
我希望学生能做到的第一件事,是分清自己此刻在做哪一件——这几个词常被混用,可它们不是同一种活动。
Coding 是写代码——把已知的解法变成语法的局部动作。三者中最小的一件,也是被高估最多的一件,大概因为它最显眼。
Programming 是从一张白纸解决问题——设计解法,选定结构,在东西成形之前决定它是什么。真正的思考多半住在这里。
Hacking——我取它的老意思,当褒义讲——是在亿万行不是你写的代码里解决问题。在一个内核里找到需要动手的那三处。把一个庞大的既有系统,掰去做它从未打算做的事。老实说,真实的系统研究要的多半是这个,而它恰恰是学校教得最少的本事。
把三者在脑中分开的价值在于:研究几乎从不发生在 coding 这一层。卡住的时候,问一句「我卡在 coding、programming 还是 hacking 上?」,问题常常自己就散了。卡在 coding,是语法或 API 的事——琐屑,查就是了。卡在 programming,说明你还没想清楚自己要造什么——回去做设计。卡在 hacking,说明你还不懂你身处的这个系统——去读它。诊断不同,药方不同。
这个区分还有个更深的版本,值得早点长进骨头里:软件工程,其实就是 programming 对时间积分。一段用完即扔的脚本,和一个十个人要维护十年的系统,哪怕今天做的是同一件事,也不是同一种物体。Fred Brooks 几十年前就说透了这一点,还给出了数字:把一段能跑的程序变成别人能用的产品,约莫三倍工作量;再把它变成更大系统里的一个部件,又是三倍——所以完整的「程序系统产品」,大约是车库版的九倍。学生第一个大项目里的惊愕与疼痛,多半就是撞见这个九倍。知道它在路上等着。
你需要能做到的事
这是我会递给人的一张具体清单。一个博士读下来,你至少该有一次:
- 彻底吃透一门编程语言——不是「会写」,而是熟到骨子里,像你做梦时说的那门语言。
- 黑进一个大型系统——真正钻进一个庞大而陌生的东西,让它听你号令。
- 从零造一个系统,从此不再怕系统。这一条最能改变人。你亲手从无到有造过一个数据库、一个内核、一个编译器之后,任何代码库都吓不住你了,因为你知道里面没有魔法——只有决定,有的高明,有的你会换个做法。
- 养成一个好的可视化或工具习惯。当你能看见系统在干什么,调试就不再是考古。
在你自己的方向上,还有两件:你得能快速做原型,你得真正吃透脚下的每一层——操作系统、硬件、网络——透到它们骗不了你。一般原理是:没有程序员帮忙,硬件跑不到最优;而下面那层若是黑箱,你就帮不上这个忙。最微妙的系统 bug、最出人意料的性能数字,几乎都住在同一条缝里:你对下层的心智模型,与它实际行为分道扬镳之处。往下走一层。几乎总是值的。
关于「不怕系统」,再给一条读代码的实用心得,因为扎进巨大代码库这件事吓退过太多人。代码其实不是拿来读的,是拿来解剖的:一段代码不是文学,是标本。没人把一个百万行的系统当小说从头读到尾。你找一根线头——一个请求、一次系统调用、一个函数——拽住,顺着往下捋,其余九十九万行,不到用时不看。读代码是一种主动的、外科式的动作,不是被动的。信了这一条,大系统就不再是墙,而是迷宫——迷宫至少有路可走。
刻意练习,而非一万小时
人们爱谈「一万小时定律」。它是错的,至少被严重歪曲了——一万小时没有任何魔力,被这条定律引用其研究的那位学者,花了多年时间澄清这一点。小时数不是机制。造就技能的是刻意练习:在能力的边缘干活,做刚好超出你舒适范围的事,并得到明确指出错处的反馈。一万小时舒舒服服的重复,只会让你和第一小时一样好;不舒服的那种练一小时,配上真反馈,就能挪动你。
对我们来说,不舒服的那种长这样:去写你一直绕开、因为没把握写好的那部分;去测你自以为了如指掌的系统的性能,然后发现自己错了;把论文投出去,把每一条毒辣的审稿意见读完,而不是别过脸去。反馈是全部要义——不巧,这又成了学徒制的一条论据,因为好导师就是一台按你的弱点校准过的反馈机器。
给自己造一个系统
有个怪异的盲区:做系统的人,几乎从不为自己的工作造一个系统。看看你的研究生活实际是什么——输入流的到达速度超过你的处理速度(论文、想法、死线、讨论串、半截的念头),延迟要求硬邦邦,缓存只有一小块还漏,名字叫记忆。你绝不会这样运营一个服务。可多数学生偏要全装在脑子里,丢三落四,更糟的是——把本该用来思考的注意力,烧在了「记住」上。原则一句话:脑子是用来产生想法的,不是用来存放想法的。把悬而未决的事从头脑里搬进一个你信得过的地方;把自己的工作流当成一个待工程化的系统,而不是性格里改不掉的一部分。
这个系统里有两件东西不容商量。第一是真正算思考的笔记,而非誊写。笔记不是工作的记录;做对了,笔记就是工作本身——思考实际发生的地方。好的笔记,就是在纸上生成与评判,是上一篇里那个品味循环的外化与固化。
第二是真把系统跑起来的纪律,这比挑一个系统难得多。有句话值得刺在每个一年级学生身上:你不会升到目标的高度,只会跌到系统的水平。人人都想做出伟大的研究;「想」是均匀分布的,也是无用的。人与人的差别在那套无聊的日常机器上——什么时候读,怎么记,死线守不守。所以你若采用一个系统,给它一场公平的审判:严格跑满两周再下结论。多数人三天就放弃一个好系统,因为它别扭——可别扭是任何新技能的成本,什么也说明不了。
交付:死线、估时与取舍
最后,是把「存在的研究」和「差点存在的研究」分开的那部分:收尾。三件事要知道。
你不擅长估时,而且错得不随机——可预测地偏向同一边。这叫规划谬误:我们系统性地低估自己任务的耗时,哪怕手握堆成山的「每次都超时」的证据。解法不是乐观,也不是意志力,而是采取外部视角——问问和这次类似的事情,以前实际花了你多久,然后信那个数字,别信脑子里的故事。
既然一切都比你想的久,你注定做不完所有事,于是知道砍什么,是一种执行技能,而不是执行的失败。Herbert Simon 给这种正确行为起的名字是 satisficing(满意即止):别把每个部件都优化到底,每个做到够好就前进。帕累托分割在系统工作里真实得残酷——约两成的工夫,换来八成的结果。给每一块找到那两成,交付,剩下的时间只花在「卓越确实能改变贡献」的部位。
还有,快点迭代。先让它跑,再让它对,再让它快——只此一序,绝不倒置。把整个工作环境设计成「改动与结果之间即时相连」:改一处与看见变化之间的回路越短,你能跑的轮次就越多;而执行和想法生成一样,奖励的是回路的圈数。回路紧,迭代多,系统成。
这一切之下——三种造法,层层机制,工具,死线——执行说到底是一件事:给机器建一个足够准的模型,让它做你打算让它做的事,而不是你以为它会做的事。bug 就住在你的模型与机器分歧的地方。这是你要学会建模的第二个系统。第三个的反击最凶,因为它自己长着脑子:其他人。
下一篇:沟通——把做完的东西,从你的脑中搬进所有人的脑中。