把这套程序从头翻了一遍之后,我得出一个当时完全没有意识到的结论。
我写的每一行都在把代价分配给某个人,而我分配的时候并不知道自己在分配。
几个具体的例子
为了让第一次访问不慢,我把代价给了机器。 程序建的网站,我设成一直开着、从不休息、也不因为没人访问就歇下来。这样早上第一个人来点,页面立刻就出来。代价是内存一直占着,应用本身的毛病也不再被定期清理掉,会一直累积。
我当时只算了「快」这一头。
为了让程序不崩,我把代价给了现场的人。 出错的地方我大多包起来,出错了记一行然后继续。程序不会崩,进度条会走完。代价是出问题时人看到的可能不是真正的原因,得从很远的地方往回猜。
我当时只算了「不崩」这一头。
为了少装一个依赖包,我把代价给了后来接手的人。 装配零件这件事我是手写的,每个零件自己保管一份自己,一共三十几处。好处是客户机器上不用多装东西。代价是谁想知道「这个东西从哪来」,得翻进方法体里去看,签名上看不出来。
我当时只算了「少一个包」这一头。
为了让人不会操作错,我把代价给了想改设置的人。 有些地方我做成只能看不能改,改了要点保存才生效。好处是乱点的概率小了。代价是真想调整的人没有一个顺畅的入口。
这些选择有没有错
我不打算把它们判成错的。
这四个选择都是在当时的约束下做的,而且四年运行下来,没有哪一个造成过事故。机器内存够用,现场的人没被卡住太久,依赖包确实一个都没多装,误操作也确实少。
让我现在重新想一遍的不是「选得对不对」,是我当时不知道自己在选。
我当时的判断标准很单一,这一步能不能跑通、会不会出错、客户会不会点不动。这三问都是短期的,问的是「今天下午能不能部署完」。它们没有一个问到「半年后谁为这个决定多花时间」。
现在我看这类事的方式
我现在遇到一个设计选择,会多问一句,如果它出问题,是谁在什么时候发现?
这一问很便宜,但能把很多选择照出另一面。
- 关掉定期回收 → 出问题是内存慢慢吃满,发现的人是一年后加内存的那个人
- 出错不声张 → 发现的人是出故障那天在现场排查的人
- 依赖手写 → 发现的人是下一个读这段代码的人
- 改不动的拼写 → 发现的人是每一个第一次看到它、停下来想「这是不是笔误」的人
第四类最轻微,但它有个特点,每来一个人,代价就付一次。
这套程序现在还在用,我改它的原则是能小改就不重写,改完客户那边感觉不到变化。
这个原则本身也是一次代价分配,我把「代码不够干净」的代价留给了自己,把「不用重新学一遍」的好处给了用的人。
这一次我是知道的。
这个工具本身后来开源出来写成了《部署器开源记 · 第 1 篇 · 发版那五步,我为什么没做成命令行》:发版那五步,我为什么没做成命令行。