我喜欢的事物

记我喜欢的东西,和为什么喜欢。

语义化命名

我喜欢让名字直接表达含义。读出一个好名字,所指的东西就自己浮现。

IUPAC 系统命名(化学)

「2-甲基丙烷」这个名字在描述一个结构:主链是丙烷,三个碳原子排成一列,2 号碳上接着一个甲基。不用翻图册,照着名字就能画出这个分子。俗名「异丁烷」叫起来顺口,但名字里没有结构——知道它叫什么,不等于知道它长什么样。

这件事有个明确的起点:1787 年的《Méthode de nomenclature chimique》推动的命名改革,让化学名字从逐个记忆的符号,变成按规则可以推导的东西。

反过来想

我喜欢把问题翻过来看的做法。正面走不通时,答案常常藏在背面。

反过来想,总是反过来想

芒格 1986 年在哈佛学校毕业典礼上的演讲,题目就是反过来讲「如何保证过上悲惨的人生」。他在演讲中引用了数学家雅可比的格言「反过来想,总是反过来想」。

这篇演讲收录在《穷查理宝典》中。

只做好一件事

我喜欢只做一件事的小工具。程序各司其职,把它做好,剩下的交给组合。

每个程序只做好一件事

1978 年,《Bell System Technical Journal》出了 Unix 专刊,刊首前言把这种风格总结成四条准则,第一条是「Make each program do one thing well」——每个程序只做好一件事。新工作宁可另写一个程序,也不往旧程序里添「功能」。前言由 McIlroy、Pinson、Tague 三人署名,领衔的 McIlroy 是管道的发明人,这条准则后来一直记在他名下。

它还有一句流传更广的版本。1994 年 Peter Salus 的《A Quarter Century of UNIX》记下了 McIlroy 的三句话:写只做一件事并把它做好的程序;写能彼此协作的程序;写处理文本流的程序,因为那是通用的接口。cat、grep、wc,每个都只做一件小事,管道一接,就能做成谁也单独做不成的事。

不为特例破例

规则少,才守得住。每开一个特例,规则就松一分。

特例还不够特殊,破不了规则

Python 的设计哲学写成了一首 19 行的小诗,The Zen of Python,收录在 PEP 20,终端里敲 import this 就能读到。第八行是:Special cases aren't special enough to break the rules——特例还不够特殊,不足以破坏规则。紧接着的下一行却让了一步:尽管实用胜过纯粹。规则的墙上留了一扇气窗。

我喜欢它的理由很实际:规则一多,特例就多;特例一多,规则就成了摆设。少写几条规则,每条都当真,反而守得住。这和「永远选最简单」是同一类取舍——不是容不下复杂,是不肯为特例预付代价,那笔账最后总要由之后的每一次判断来还。

简单到修得好

我喜欢简单到能被修好的设计。手边只有最普通的工具,也能让它重新转起来。

普通机械师修得好的飞机

KISS 原则,Keep it simple, stupid。据称出自洛克希德 Skunk Works 的首席工程师 Kelly Johnson——造出 U-2 和 SR-71 黑鸟侦察机的地方。流传最广的是那则轶事:他把一把工具递给设计团队,要求正在设计的喷气式飞机,必须能在战场上由普通机械师仅用这些工具修好。stupid 指的不是人笨,而是东西怎么坏,和手边能拿来修它的东西,两者要相称。

维基百科在这个出处前留了「据说」:1960 年美国海军有过代号 Project KISS 的项目,1938 年的报纸上还能找到更早的变体。谁先说的并不太要紧,要紧的是它给「简单」定了一个可检验的标准——不是造的人觉得简单,而是修的人手上看得出高低。把东西设计得简单,是替之后每一个接手的人着想。

信心够了就停

测试是手段,能工作的代码才是交付物。买信心,按需出价。

报酬只为能工作的代码

Stack Overflow 上有个问题“How deep are your unit tests?”,排最上面的回答来自 Kent Beck——测试驱动开发的创立者、极限编程的创始人。他说得很直白:我拿报酬是因为代码能工作,不是因为写了测试,所以我的哲学是,用尽可能少的测试,达到所需的信心水平。括号里还补了一句自嘲:这个信心水平恐怕比行业标准还高,不过也可能只是自负。

他接着给出了自己的取舍细则:不常犯的错,不为它写测试;条件复杂、容易看走眼的逻辑,就多测一分;在团队里写代码,就照大家集体容易出错的地方加码。测试在他手里是一笔笔出账的预算,每一条都得换来实打实的信心。这话出自测试驱动开发的创立者之口,「尽量少」反而更有分量——多写的每一条,都得自己给出理由。

不要模式

好界面不该有看不见的状态。指哪打哪,不必先声明我要开始做什么了。

把 NO MODES 挂上车牌的人

剪切、复制、粘贴,是 Larry Tesler 和同事 Tim Mott 在 Xerox PARC 的编辑器 Gypsy 里想出来并实现的。他一生反对「模式」式界面——那种要先进入某个状态才能做事的设计:按一下进入插入模式,字符才肯落地,再按一下退出来。他要的无模式界面,任何时候所有操作都摆在手边。他的汽车车牌就写着 NOMODES,个人网站干脆叫 nomodes.com。

模式的毛病在于,界面替你记着一个看不见的状态:同一套操作,效果取决于此刻藏在哪个模式里。无模式把这个账本撕掉——每一次操作即刻生效,没有进出的仪式。这个主张 Tesler 从 PARC 带出来之后一直没放手。对做界面的人来说,它是一句提醒:别让用户替你的状态记账。

失败要显眼

我喜欢会当场出声的失败。出错的那一刻就把问题亮出来,好过它潜伏下去,某天以另一副面孔回来。

失败要立即且显眼

Jim Shore 在 2004 年 9—10 月号的《IEEE Software》上发表《Fail Fast》。文章开头说,软件开发里最让他头疼的是调试——不是几分钟就能看穿的 bug,而是运行几小时后、在特殊条件下才现身、堆栈跟踪指向死胡同的那些。他的建议是把软件做成 fail fast:问题发生时,立即且显眼地失败。反面的做法是让软件自动绕过问题,看起来健壮,实际是在「慢慢失败」(failing slowly)——错误被吞下,之后以更奇怪的方式现身。

他举了读配置文件的例子:属性缺失时,返回一个默认值,还是抛出异常?返回默认值的系统,在配置打错字时毫无动静,直到用户碰上莫名其妙的故障,排查要好几天;fail fast 的系统在错字引入的瞬间就停下,报出缺的是哪个属性、在哪个文件,三十秒修好。论文里的关键工具是断言——一小段检查条件、不满足就失败的代码。哪里该加?他建议去找注释:注释常在不经意间记下对代码的假设,看到它们、或想写下一条时,想想怎么把它变成断言。断言长在系统的接缝处,负责拆穿「别人会怎么用错这个接口」;方法自身的正确性,交给测试。

「立即且显眼」听起来让软件更脆弱。他说正相反:bug 越早暴露,越容易修,流到生产的就越少。他也不主张一崩了之——部署到客户那里,就用一个全局错误处理器收容意外异常、报告出来;但明确反对在生产环境关掉断言,因为客户现场的错误是穿过测试网漏出来的,最难复现。静默不会让错误消失,只会把它推迟到最难查的时候。我在自己维护的系统里也尽量这么做:读不到该读的东西,就当场说清楚。

老技术的横向思维

我喜欢给熟透了的老技术找新用法。技术是旧的,用法是新的。

黑白液晶赢下彩色对手的游戏机

「散々使いこなれて枯れてきた技術を水平思考し、まるっきり違う目的に使うことによって、ヒット商品は生まれ出る」——说这话的是横井军平,任天堂的工程师,Game & Watch 和 Game Boy 都出自他手,句子收在 1997 年的访谈集『横井軍平ゲーム館』里。用大白话讲:不追最新最贵的技术,专挑用到烂熟、量产后便宜下来、人人摸得清脾性的老技术,挪去一个谁也没想到的用途,新东西就从这里长出来。

1989 年的 Game Boy 是这套想法的实证:同代掌机纷纷换上彩色液晶,六节电池只撑三五个小时;它用一块黑白屏,四节电池跑上一整天,便宜、皮实,最后拿下八成以上的市场。我喜欢它的理由,和我信条里那句「越简单,越长久」是一回事——老技术是被时间和市场筛过一遍的东西,简单、便宜、处处有人懂,所以活得久。盖尔定律说的也是这件事:能运行的复杂系统,必定从能运行的简单系统演化而来。