程序员最大的本事,问他本人多半答不到点子上:不是哪门语言,不是哪个框架,是调试练出来的那套肌肉记忆。这套动作有个隐藏前提——世界是可以 debug 的。
第一道工序就决定大半胜负:把问题定义出来。“网站太慢"不是问题,是情绪。“移动端首屏超过 3 秒,转化率掉了两成"才是问题。定义模糊,后面每一步都打在空气里。程序员不信"我觉得"“差不多"“大概是这个意思”。输入是什么、输出是什么、什么状态算解决——三件事问清,问题解决了一半。
第二道,拆。面对大麻烦,本能反应不该是"怎么解决它”,而是"它能拆成哪几个互不干扰的小问题”,拆到每一个都简单到不用想就能做为止。写一本书,拆到章节,拆到段落;找一份工作,拆到简历、投递、笔试、面试、谈薪。大问题吓人,多半只是没拆。
第三道,假设加验证。东西不工作了,外行的做法是乱改,改到能跑为止;专业的做法是:收集现象,提出最可能的原因,设计一个最小实验去推翻它,推翻就换下一个假设。一次能证伪的小实验,顶十次"再试一次”。之前另一篇拆 Docker 连锁故障的文章里就是这个路数:报错在 A,根因在 B,B 修好,A 自己消失。
第四道,先找现成答案。遇到的麻烦,九成九别人遇到过,且有成熟解法。第一反应应该是"谁解决过这个问题",而不是"我从头做一个"。拿设计模式和开源库解题不是偷懒,是对成本负责——重复发明轮子,发明出来的往往还是方的。
第五道,先跑通最小版本。完美是个移动靶,先出一版让现实打分,再改。纸面上改十轮,不如落地跑一轮。
第六道,假设最坏会发生。设计的时候就要问:输入是脏的怎么办,网络断了怎么办,机器挂了怎么办。失败要早,失败要响——问题暴露得越早越便宜,默默吞掉错误是最贵的那一种。
最后一道才轮到自动化:流程没跑对之前,别急着上脚本。手工重复不是勤劳,是不肯投资自己——但前提是流程本身已经对了。给错误的流程做自动化,只是让错误提速复制。
反方也得接得住。这套流程这么死板,做事反而更慢?——慢是账面:花一小时做计划,买回的是十小时收拾烂摊子,这笔账谁都会算,谁都不肯先付。现实世界没有日志和单元测试,这套有什么用?——现实世界有验收标准。不敢写下"什么算解决了",那不是生活,是无头苍蝇换了一种说法。
不适用区也划清楚:这套是给事用的,不是给人用的。情绪优先的问题,先听,别拆;把人当系统里的根因去 debug,两头都输。世界可以 debug 的前提,是你敢先把问题说准。很多人的毛病从来不是不会修 bug,是不肯承认 bug 在哪。