Skip to content
Published on

好工具是隐形的 — gingerBill 的主张与“过于隐形”的工具陷阱

分享
Authors

引言 — 好工具是隐形的这一主张

Odin 语言的作者、也就是不折不扣站在造工具一方的 gingerBill,最近写的《Good Tools Are Invisible》登上了 Hacker News 榜首。主张正如标题所言:好工具应当是隐形的,而造出这样的工具正是工具制作者的目标。他反复告诫要提防的一种习惯,是把工具的缺陷当作“解开来很有趣的谜题”来兜售的态度。他不希望工具有趣,他希望它隐形。

这篇文章最锋利的地方是一个心理层面的观察。觉得自己在高产,和实际上高产,是两回事。 用难用的工具解开难题会让人心情愉悦,而人们常把这种愉悦错当成成果。他给出的诚实指标不是解谜的快感,而是挂钟时间以及过程中犯错的次数。一旦熟悉了编辑器,工具就会融入背景消失不见。但当某件事无法轻松处理的那一刻,工具又会重新显现。

其余论点也都落在同一根轴上。多数情况下多光标比 vim 宏更快的例子、强默认值是对用户时间的尊重这一主张,以及用他的话说“学习曲线是成本而非美德”这句话。他还指出,当工具变成身份认同时,承认它的缺陷就仿佛在否定自己,诚实的评估因此受阻——这一点也很到位。他把程序员偏爱过度可配置性的原因,解释为偶然复杂性(accidental complexity)。不过他明确表示,自己并不谴责那些真正高效地使用 vim 或 emacs 的人。批评的靶子是把摩擦包装成美德的思维方式,而不是工具的选择本身。

我认同的地方 — 把摩擦当作功能兜售的话术

我认为这篇文章最有力的一段,是把“感觉到的生产力”和“实际的生产力”区分开来。因为这是我每天都会遇到的事。保存时自动格式化、LSP 自动补全、顺手的键位映射——当这些都正常运作时,我根本不会意识到编辑器的存在。反过来,一旦语言服务器卡住,或者补全给出牛头不对马嘴的候选项,工具就又重新映入眼帘。“熟悉之后它会不会消失”是一条真正好用的判断标准。

他还说,GUI 的局限并非本质使然,而是工具作者投入不足的结果;认为终端 UI 天生更优越的想法是错的。我大体上同意。做得好的 GUI——好的性能剖析器或 diff 界面——和终端一样隐形,有时甚至更隐形。因为它把在终端里需要在脑中重建的结构,直接铺在你眼前。“是 TUI 还是 GUI”是一根错误的轴,“它给我抛来几道谜题”才是正确的轴。

我也同意他对“把摩擦当作功能重新兜售”这种话术的批评。“这东西熟了就快了”这句话,往往是在为沉没成本辩护。因为舍不得学它所花的时间,就把那份难度本身抬高成了价值。用挂钟时间和犯错次数来衡量、而不是用解谜的满足感来衡量,这个提议不是感性抒情,而是工程。

最深层的信号在别处。当工具真正隐形时,我对它的看法本身也随之消失。无所谓好坏——正是 gingerBill 所说的“忘了自己正在用它”的状态。反过来,如果我此刻正在为编辑器争论不休,那么至少在那一刻,这件工具对我而言就并非处于隐形状态。

有一处我想稍作反驳。说宏比多光标“劣 99.999%”是一种修辞上的夸张。宏可以录制、可以回放、可以保存成文件甚至纳入版本管理——这些都是多光标不擅长的事。尽管如此,他更深层的论点,也就是“别把工具浪漫化,去衡量产出”,依然是对的。反驳只是让例子有了瑕疵,却撼动不了主张。

“过于隐形”的工具陷阱

这里要加上一条来自一线的注脚。gingerBill 说的“隐形”,意思是界面上没有摩擦。但工具隐形的方式还有另一根轴:因为内部运作不透明而看不见。这两者截然不同。前者是好事,后者是危险。

构建系统就是典型例子。当缓存命中、增量构建很快时,构建工具会完美地隐形。麻烦的是它不告诉你缓存为什么没命中、CI 为什么突然要跑 40 分钟的时候。一件工具正常运作时看不见,出故障时依然看不见,那就不是优雅,而是不可观测。

这个问题在 Kubernetes Operator 上表现得最鲜明。你声明期望状态,Operator 就会自动去调和(reconcile)。因为不用操心,所以它隐形。而正是因为这份隐形,即便调和悄悄停了——finalizer 卡住、admission webhook 挂掉、或是默默忽略了 CRD 里的某个字段——也没人察觉。声明式工具的舒适,往往与“不知道到底发生了什么”这一代价成对出现。我自己也曾在很久之后才发现,有个节点几周里一直处于无法调度的状态却无人过问(事后复盘)。就是因为工具太安静了。

这里还有一份更深的代价。隐形的抽象替你扛起了你的能力——可一旦它渗漏,就只剩下那些从没往底下看过一眼的人。这正是 Joel Spolsky 所说“任何非平凡的抽象,都会在某种程度上渗漏”的那个点。如果 kubectl apply 永远“就那么通过了”,那么在它不通过的那天,值班工程师从来没学过它底下究竟有什么。工具越隐形越好,但隐形并不意味着它底下的东西就消失了。

使用时隐形,出故障时大声鸣响

于是我在 gingerBill 的标准上再加一根轴。好工具在使用时应当隐形,同时在出故障的那一刻,又要以醒目、可读的方式重新现身。摩擦的隐形(好)与失败时的沉默(坏)是两根不同的轴。把这两根轴叠起来,工具就分成了四个格子。

                    失败时吵闹            失败时安静
──────────────────  ──────────────────  ──────────────────
平时看不见            值得信赖的工具(目标)   最危险的工具
平时碍事              诚实但未完成           最糟 — 又慢又黑

我最信赖的工具在左上格——平时看不见,出故障就吵闹。最危险的工具在右上格——正常时和异常时一样安静。这不是在反驳 gingerBill,而是在补充他。在他正确强调的强默认值之上,我再配上可观测性。

好的 Operator 会输出状态(condition)、事件和指标。平时不用操心所以看不见,但你一探究竟,它就会告诉你自己正在做什么。我用 Rust 写了个小 Operator、进而发现自己整支机群其实早已宕掉的那件事(那个故事),归根结底也是把工具变吵的例子。“像变魔术一样自动搞定”的框架之所以危险,道理也一样。猜对的时候它隐形,而猜错的那一刻,连供你调试的落脚面都消失了。

可见性也不必等到失败才有。好工具会在你需要时把自己显示出来——--dry-runkubectl describe、查询的 EXPLAIN、构建的 --verbose 图。默认看不见,一旦你发问,就能一探究竟。让你不必每天盯着也能维持心智模型的,正是这种“按需可见性”。

结语

gingerBill 的文章值得一读。“把摩擦当作乐趣重新兜售不过是合理化”这一诊断很准确,而“熟悉之后它会不会消失”是挑选工具的一张好试纸。对他的结论,我没有太大异议。

若要从一线补上一条注脚——还应当一并追问“它出故障时,会不会重新、并且醒目地现身”。使用时看不见,失败时读得懂。同时满足这两点的工具,才是我长久以来信赖的工具。

他的论点和我的论点底下,是同一种价值:对用户的尊重。他要求工具尊重你的时间,而我在此之上再加一条——也要尊重你的理解。一件今天替我省下时间、却在出故障时把我丢在半路的工具,并不是替我省下了那些时间,而只是暂时借走罢了。

参考资料