第十七章 越简单的模型越有效

接着上一章的话头。上一章墨叔把你逼到”你手里永远只有模型,不是世界”这个墙角;这一章他要问一个更要命的问题:既然模型可以随便建,那你凭什么挑?


墨叔:上一章咱们的结论是,你手里拿的从来不是世界,是模型。今天我接着往前逼一步。同一个现象,你可以建出无数个模型——那你凭什么选一个?

你:凭它对不对啊。

墨叔:好,那我给你一个真实场景,你来判”对不对”。凌晨三点,你的服务报警:CPU 打满,接口大面积超时。现在有四种解释——第一,上游在批量推送,触发了我们的同步校验,线程池被打满;第二,交易量上涨后 GC 进了恶性循环,老年代回收不过来;第三,昨天值班的同事改了一行配置,把限流阈值调错了两个零;第四,墨菲定律发作,宇宙在针对我们。你说哪个”对”?

你:第四个明显是废话。前三个……都有可能,得排查。

墨叔:注意你刚才的用词——“都有可能,得排查”。这句话是这一章要盯的第一个毛病。你先回答我:第四个”宇宙针对我们”,它错在哪儿?它可解释了所有现象,一点都不遗漏。

你:它没法指导我干活。我按它没法动手。

墨叔:漂亮,你自己摸到了判据的边。它不是”错”,它是不做任何可被检验的断言,所以它连上擂台的资格都没有。而前三个才是真正的候选模型,因为它们都指向了一个具体的、可干预的因果节点:改上游推送策略、调 GC 参数、回滚配置。真要修系统你会发现,真正戳中病根的那一个,往往描述起来最短——不是因为它信息少,是因为它直接指向了那个你一动就有反应的节点。

你:那你刚才说我”都有可能,得排查”这句话有毛病,毛病在哪?

墨叔:程序员最容易踩的坑就在这句话里:把”我能列出很多可能”误当成”我思考得深”。一个需求或者一次故障,有人能一口气报出七八种可能原因,然后说”都排查一下吧”,看着极其严谨,其实是用枚举假设替代了建立模型。

你:这个我不服。多想几种可能,怎么就成毛病了?

墨叔:因为它们性质不同。真正的建模是:你先想清系统里”真正会出问题的那几个因果节点”,然后用最少的假设去指认病根,并且这个指认必须能预测”我改哪里、现象会怎么变”。而枚举是把一打互不相关的猜测摊在桌上,让全组人平均分配精力去逐一排除。前者是下锚,后者是撒网。撒网耗光团队一整晚,下锚二十分钟收工。

你:可有时候我确实不知道啊,只能撒网。

墨叔:那就诚实说”我现在没有模型,我在做无先验搜索”,这是正当的第一步。但别把它包装成严谨。你下次准备说”可能是 A、可能是 B、也可能是 C”的时候,先自己停一秒:我是在枚举,还是在建模?——这一秒就值钱。

你:行,我认这一刀。那”选最简单的”这条规矩,是谁定的?

墨叔:西方叫奥卡姆剃刀,十四世纪那位奥卡姆的威廉说的:“如无必要,勿增实体。“王东岳把它往上提了一级,叫科学模型的选择律:在所有能同等解释观测事实的模型中,优先选那个最简单的。注意他提的这一级很关键——它不是科学家的审美偏好,是模型竞争中的生存法则。

你:生存法则?模型还会死?

墨叔:当然会死,而且天天在死。你想想模型是怎么死的:不是被哲学家投票投死的,是在”能不能解释新事实、能不能做预测、能不能被证伪”的竞争里被淘汰的。现在假设一个复杂模型和一个简单模型都能解释同一批已知数据,那复杂模型凭空多出来的那些假设、参数、齿轮,全是什么?

你:……负债。

墨叔:对,纯负债。它们不增加一分解释力,却增加了出错概率、降低了可验证性、抬高了传播和使用的成本。这跟生物演化是同构的:一个器官如果对生存没有净贡献,光维持它就得烧能量,那它在自然选择里就是负资产,迟早被裁。模型也一样——多一个未被证实的假设,就多一个将来会被事实打脸的靶子。

你:所以”简单者胜”不是因为好看。

墨叔:一点关系都没有。是因为简单者扛风险的能力更强、被证伪的代价更低、被替换的摩擦更小。王东岳还有更狠的一层:文明越往后,代偿越复杂,存在度越低。这话放到模型世界里同样成立——一个被层层包装、塞满假设的模型,就像一台祖传系统:它能跑,但没人敢动它,它的存在度,也就是可理解、可维护、可替换的程度,已经被复杂代偿吃干了。

你:等一下,我想给你的”简单”要个更硬的定义。“简单”这个词太主观了,我觉得简单的东西,别人可能觉得绕。

墨叔:这个质疑非常好,而且二十世纪的数学真给出了硬定义。柯尔莫哥洛夫说:一段信息的复杂度,等于能生成它的最短程序的长度。你要描述一串一亿位的数字,如果得把这一亿位全存下来,它就是复杂的;如果一句”π 的前一亿位”就够,它就是极简的——内容一模一样,复杂度差了一亿倍。

你:所以复杂度不是”看起来长不长”,是”生成它所需的最短描述”。

墨叔:对,而顺着这条线还有一个更实用的原则,叫最小描述长度:在描述数据这件事上,最好的模型,是让”模型本身的长度 + 用这个模型去编码数据后残差的长度”两项之和最小的那个。 你品一下这个式子的美:它一口气把过拟合和欠拟合都堵住了。模型太简单,残差项爆炸;模型太复杂,模型项爆炸。奥卡姆剃刀不是一句劝人从简的格言,它是这个和式的最优解。

你:这个式子我能直接搬到工程上。我们那套异常处理,模型项——就是规则本身——只有五十行,但残差项巨大:每个月都有一批”规则没覆盖到”的工单要人肉处理。反过来我见过另一套,规则三千行覆盖一切,可没人看得懂,模型项爆了。

墨叔:你这两个例子摆一起,就是同一个和式的两端。所以下次有人问你”到底多简单才算简单”,你别答”感觉”,你答这个和式:把你要维护的规则量,加上你每月为漏网情形付的人肉成本,谁的和小谁赢。这是一个能算的账,不是审美。

你:所以第一步的认知扭转是——

墨叔:简单不等于信息量少,简单是”解释力除以假设数”这个比值高。一个好模型像一段写得极简、却把所有边界都覆盖住的核心函数;不像一篇堆满注释、却跑不通的文档。我见过太多工程师把”我加了好多判断,所以我很严谨”当成美德——判断多不等于严谨,判断多等于你还没找到那个能让这些判断同时消失的更深结构。严谨的标志是”少”:少到不能再少,却一个边界都没漏。


你先答:

墨叔:那我立刻拿你最熟的东西反驳我自己。现实里很多成功系统极其复杂——一个承载亿级用户的分布式架构,几十个组件、几百个配置项。这是不是直接证伪了”越简单越有效”?先自己想三十秒。

你:我猜是”问题本身复杂”和”模型多余的复杂”不是一回事?

我的参考答案(供你对照): 对,你说的正是关键切分,我把它说全。一个亿级系统必须复杂,因为用户量、一致性要求、容灾要求这些结构条件本身就是复杂的。王东岳讲”存在度递弱、代偿必递繁”,说的正是这种情形:生存环境变复杂,系统必须长出更复杂的代偿才能维持存在,这是被迫的,不是可选的。但”被迫的复杂”和”多余的复杂”是两回事。同一个亿级系统,A 团队用十个边界清晰、契约可验证的服务承载这份复杂度;B 团队用十个互相耦合、职责重叠、没人说得清依赖关系的服务承载同一份复杂度。两者都”复杂”,但 B 的复杂里裹着大量未被证明必要的实体。所以奥卡姆剃刀否定的从来不是必要的复杂,而是同等解释力下多余的那部分复杂。给你一个可操作的判据:去掉它身上任何一块,系统还能不能活?能活却没去掉,就是违反选择律。


墨叔:好,现在正面回答那个最刺耳的质疑:世界这么复杂,简单模型凭什么有效?我给你四层理由,一层比一层深,你别急着跳。

你:来。

墨叔:第一层,可证伪性。波普尔说过,一个理论之所以算科学,不在于它”对”,而在于它”能被证明错”。简单模型假设少,所以它的预测特别硬:它明确说”如果这样,那么一定那样”,你找到一个反例它就崩。听着像缺点,其实是它最大的优点——它给了你一个清晰的真假判定点。

你:复杂模型呢?

墨叔:复杂模型假设多、参数多、自由度多,你随便调几个内部设定就能把反例圆回来。于是它永远”没错”,也永远”说不准”。翻译到你工位上:一份前置条件写得清清楚楚的接口契约,你能立刻写出验证它的单测;一份”怎么调都能跑、跑出来啥都行”的代码,才是真正的噩梦——因为它不可证伪,所以它不可信。

你:这个我马上想到一个更刺骨的例子:过拟合。

墨叔:说,你来讲。

你:一个参数爆炸的模型可以把训练集里每个样本都拟合到误差趋零,但换新数据就一塌糊涂。因为它用过量参数”记住”了噪声,而不是”学到”结构。

墨叔:这就是奥卡姆剃刀在数据科学里的精确重述:当模型复杂度超过数据所承载的结构信息,多余的参数不是更聪明,是在拟合噪声。而且你注意这句话可以原封不动搬到系统设计上——当你的异常处理分支多到覆盖了历史上每一次故障的具体形态,却说不清它们背后的统一失效模式,你就是在过拟合于过去,而不是在建模未来。

你:那第二层?

墨叔:第二层,可迁移性。简单模型抓的是结构,不是表象。牛顿那个 F=ma 不只管苹果,还管炮弹、行星、潮汐。你写一个只有三条规则的限流算法,可能同时解决了下单、支付、查询三个场景的过载;而你为三个场景各写一套”专属防崩逻辑”,看着更贴合实际,实际是把同一个问题的三种表现割裂成了三笔债。

你:这个坑我踩过,而且是血的。同一份”校验用户身份”的逻辑,登录接口一份、支付接口一份、发券接口一份,三份拷贝各改各的。某天安全规则升级,改了两处,第三处漏了,结果出了个越权,谁都想不通。

墨叔:注意你这个案例的根因不是”没写校验”,是”校验这件事从来没被提炼成一个稳定的简单模型”——它被场景噪声稀释成了三份会各自漂移的实现。单一来源的简单模型天生就能跨场景复用;碎成三份的模型天生就在制造熵。模型越简单,它剥离掉的场景噪声越多,也就越能迁移。这跟好抽象是一回事:把会变的和不变的分开,把不变的提炼成极少量稳定接口,让场景噪声留给上层去应付。

你:第三层?

墨叔:第三层,认知带宽——上一章我们刚讲完人是带宽极窄的接收器,这一章直接兑现。一个模型要被人理解、被团队共用、被新人继承,就必须能压进人的工作记忆。塞了四十个参数的模型,只有作者本人转得动;作者一离职,它就变成团队里的黑盒神谕,谁都得去拜,谁都不敢改。

你:这条我完全同意。我们 on-call 手册上最救命的一句话就是”先看线程池,再看 GC,最后看限流配置”。

墨叔:这句话比一份三百页的架构白皮书更值钱,原因特别朴素:凌晨三点,人只有读懂简单东西的带宽。所以简单不是给聪明人看的,简单是给”疲惫的、被叫醒的、心跳一百二的那个你”看的。你设计任何东西的时候,默认读者是那个状态的人,你的设计水准立刻上一个台阶。

你:第四层是什么?你说最深的一层。

墨叔:第四层:简单模型更接近结构本身。为什么?因为世界的底层规则往往是稀疏的。物理定律能用极少几个方程描述极大尺度的现象,是因为自然在基本层面确实是”低维结构 + 局部复杂性”的组合。真正有效的理论,是用最少的自由度去捕捉那个最稳定的结构。

你:所以反过来说,我的模型又简单又有效,恰恰证明我戳中的不是表象?

墨叔:这句话你要背下来。你写的那条只用一个规则就压住全局抖动的核心逻辑,之所以短,是因为它压住了真正会抖的那个变量;而不是在每一个表象抖动点上分别去安抚。这四层合起来,就回答了你最初的质疑:简单模型有效,不是因为它偷懒,是因为它把可证伪性、可迁移性、认知带宽和底层结构,压缩进了同一个极简形式。它省掉的是装饰,留下的是因果。


又停一下:

墨叔:我现在给你下一个套。既然”简单+可证伪”这么好,那我给你一个终极简单的模型:“一切皆因缘。“它极其简单,而且永远不会被证伪。按你刚学的标准,它是不是最有效的模型?

你:……直觉上不对,但按你给的两条标准,它好像全占了。

我的参考答案(供你对照): 这是奥卡姆剃刀最常见的误用陷阱,必须点破:可证伪性和”简单”是两个独立维度,而且”简单”这条规则有一个前置条件——同等解释力。一个模型要进选择律的赛场,首先得是”能解释并预测事实的候选者”。“一切皆因缘""道可道非常道”这类玄言,确实简单、确实永不被证伪,但它们压根没上擂台:它们不做任何可被观测检验的断言,所以连候选模型都算不上,自然谈不上被选为有效模型。打个你一定懂的比方:你写一个函数,对任何输入都返回 null,而且永不抛错——它”绝不会出错”,但它是个废函数。可证伪性的前提是”它先做出了明确断言”,然后我们才能谈这断言被验证没有。奥卡姆剃刀只在”都能解释同一批事实”的候选之间裁决;对压根不解释任何事实的东西,剃刀没意见,因为它没资格站上台。所以完整表述是:用最少的假设,做出最硬的预测——少了后半句,简单就成了虚无主义的遮羞布。


墨叔:光讲道理你可能还半信半疑,我们把镜头拉到科学史,看几个真被这把剃刀改写过的转折。王东岳老提醒一句:别抽象地谈哲学,要看它怎么在文明进程里落地成活。

你:第一个案例?

墨叔:托勒密对哥白尼。托勒密用”地心加本轮”——小圆套大圆——解释行星逆行,这模型在数学上能跑、能预测,而且跑了一千四百年。问题是它太能跑了:为了贴合越来越精确的观测,后人不断往里加本轮,加到八九十层,臃肿得像一床打满补丁的被子。

你:哥白尼赢是因为算得更准吧?

墨叔:这是最大的误解,也是这个案例最值钱的地方:哥白尼一开始的预测精度并不比托勒密高。他赢的原因恰恰是——他用”太阳居中”这一个结构翻转,一次性干掉了几十层本轮。同等解释力,假设数少一个数量级。这就是选择律最标准的裁决姿势。后来开普勒再往前削一刀,用椭圆取代正圆,砍掉了”轨道必须是完美圆”这个从来没被证明必要的美学假设——模型更简了,同时也终于更准了。

你:第二个?

墨叔:牛顿。在他之前,“天体运动”和”地面落体”是两套语言、两个世界。牛顿用运动定律加万有引力把两者收成一个模型。请注意,这不是”加功能”,这是减实体:两套叙事收敛成一套。当时科学界震撼的不只是算得准,而是发现——原来我们一直以为必须分开处理的两类现象,共享同一个底层结构。这就是第四层效力的史诗级示范。

你:第三个我猜是麦克斯韦。

墨叔:对,而且它最赏心悦目。法拉第用”力线”这种极形象但非数学的语言描述电磁,麦克斯韦把它翻译成四个方程,还顺手预言了电磁波,后来赫兹真测到了。你注意这个模型的姿态:它没往里堆假设,它是把本来分散的电学、磁学规律,用一个统一的数学结构收编,然后免费多解释了一样东西——光。一个模型在变简单的同时还多覆盖了事实,这说明简单和解释力不是零和,它们经常同向。

你:第四个?

墨叔:爱因斯坦,以及他对”多余假设”的过敏。相对论有句著名的方法论信条:一切都应尽可能简单,但不能过于简单。他极度厌恶那种”只是为了拟合数据”而塞进理论的特设性假设,宁可花十年去找一条更深的原理——等效原理——也不肯在旧框架上打补丁。

你:把这四个摆一起,我看出一条线了:科学的每次大跃迁,都不是加法。

墨叔:说得准。几乎每一次重大跃迁,都是一次奥卡姆修剪:找到那个能让旧模型里一堆假设同时变得多余的更深结构。 这对你写代码意味深长——当你发现系统里有一堆”各自都能解释一点、加起来乱成一团”的特设逻辑,最该做的不是继续加 if,是退一步去找那个能让它们同时消失的上层结构。这在工程里有个名字,叫范式升级;在这一章的语言里,它叫修剪。

你:那我现在有点担心走向另一个极端。

墨叔:你该担心,我正要拦你。很多人拿剃刀当”我懒得想复杂情况”的借口,砍出一堆掩盖问题的伪简洁。所以我们必须划清”极简”和”简陋”的界。三条分水岭。

你:第一条。

墨叔:极简是对结构深刻理解之后的收敛,简陋是对复杂性的逃避。你砍掉一个参数,得是因为你证明了它冗余,不是因为你懒得理解它。就像重构:好的重构是”我读懂了它,发现这两个分支其实等价,于是合并”;坏的重构是”我看不懂,先删了再说,挂了再救”。

你:第二条。

墨叔:极简承担得起追问,简陋经不起追问。你问一个极简模型”这里为什么不加一个条件”,它能给你严密论证;你问一个简陋模型同一句,它立刻露出”因为这是拍脑袋定的”那层底色。所以判别方法特别便宜:多问三个”为什么这里不”。

你:第三条。

墨叔:极简留有余地容纳复杂性,简陋只是把复杂性推出门外。好的极简框架,主干稳定、扩展点清晰,新的复杂需求挂上来不破坏主干;简陋方案,需求一变就推倒重来。我举个你天天遇到的:你要按会员等级和库存决定发货优先级。简陋写法是一坨判断堆——是 VIP 且有库存且没被封且不是偏远地区且金额小于一万,就高优;是 VIP 且有库存且没被封且是偏远地区,就中优;然后再堆七八条类似的。

你:这种代码我维护过,任何一条新规则——比如”预售订单降级”——都得在十几个分支里手动找位置插。

墨叔:而且插错一处没人看得出来。极简写法是先把结构提炼成稳定维度:把 VIP、库存、地区、封禁各写成一条只回答一个布尔维度的规则,然后用一个统一的编排函数去应用这组规则。注意这里的要点——它并没有减少业务规则的数量,那些规则该有还是有;它减少的是”规则编排”这件事的实体数。新增规则只往规则集里加一项,主干一行不动。

你:我还想问一个反方向的问题:有没有那种”剃刀害了人”的案例?简单到误事的。

墨叔:太多了,而且比过度设计更隐蔽,因为它披着美德的外衣。最经典的一类是用一个数代替一个分布。你们汇报接口性能,报的是平均响应时间——五十毫秒,多漂亮。可平均值这个模型太简单了,它把长尾整个抹掉。真实情况可能是九成请求五毫秒,一成请求五百毫秒,而那一成里装着你最大的客户。

你:所以我们后来只看分位数。

墨叔:这就是欠拟合:你的模型比数据承载的结构更简单,于是它把真结构当噪声扔了。第二类是把有状态的东西建模成无状态的。你为了”简单”,假设每次重试都是幂等的、每个请求都是独立的,结果一次网络抖动引发重复扣款——那不是模型简单,那是模型缺项。第三类是把多目标压成单目标:只用一个指标衡量团队产出,看着极简,实则逼所有人去优化那个指标本身,把系统真正的目标弄丢。

你:那我怎么区分”这是极简”和”这是缺项”?

墨叔:回到剃刀那个前置条件——同等解释力。你砍掉长尾之后,你的模型还能解释”为什么大客户在投诉”吗?不能,那你就不是在修剪,你是在删数据。判据一句话:如果砍掉一个实体之后,某一类你原本能解释的现象变得无法解释了,那这一刀砍的不是赘肉,是骨头。 剃刀是修剪已经被证明多余的部分,它从来不是”我先砍了再说,出事再补”的许可证。

你:所以极简不是”少规则”,是”少结构”。

墨叔:这句话可以当本章的一句结论。所以真正的结论不是”简单万岁”,是:在所有同等解释力的候选里选最简单的那个——但前提是它真的同等解释力,而不是靠藏匿复杂性来冒充简单。

你:那”存在度递弱、代偿必递繁”和”越简单越有效”,我还是觉得有点顶。

墨叔:因为你把两句话的主语混了。“越简单越有效”的主语是模型:描述同一个现象时,更简单的模型竞争力更强。“存在度递弱、代偿必递繁”的主语是系统——文明、生命、组织:系统演化到后段,环境压力和熵增更大,必须靠更复杂的代偿结构才能维持存在。

你:等等,你反复说”存在度”,我一直没敢问:这词到底怎么定义?

墨叔:王东岳的原意是指一个存在物”自身稳定、能独立存续”的能力。基本粒子存在度极高——它几乎什么都不需要就能存续几十亿年;一块石头次之;一棵树要水、要光、要土;一个人离开文明连三天都难;而现代人,脱掉电力、供水、物流、货币,基本就是个待宰的裸猿。越往后演化,存在度越低,所以必须靠越来越厚的代偿结构撑着才能”看起来很强”。

你:那映射到系统上呢?

墨叔:你把它读成”这东西离开外部支撑还能不能活、还能不能被人接手”。一个五百行、零依赖的命令行工具,存在度极高:十年后编译一下还能跑,谁都看得懂。一个跑在八个中间件、三套配置中心、两个内部平台上的服务,存在度极低:任何一个依赖下线,它就死;原作者一走,它就成黑盒。它看起来更强大,因为它功能更多;但它更脆弱,因为它活着的前提条件多了几十条。

你:所以我们平时说的”这个系统很重”,其实说的是存在度低。

墨叔:说的就是这件事,只是没有词。有了这个词你就能做一件很有用的事:在方案评审时明确问一句”这个方案会让系统的存在度升还是降”。如果降,那它必须换回来足够的收益,而不能只换回来”看起来更先进”。

你:所以两句话是不同层的。

墨叔:合起来看真相是这样:系统被迫越来越复杂,因为它要活;但在每一个局部,我们仍然应当用最简单的模型去理解和管理它,因为要省认知带宽、要可证伪、要可迁移。麻烦在于,很多人把”系统很复杂”误当成”理解它也必须很复杂”,于是用越来越绕的概念、越来越厚的文档、越来越花的架构去”应对”,结果系统本身的可理解度被这些代偿又吃掉一层。

你:那给我一句能记住的。

墨叔:接受系统会变复杂这个事实,但绝不用更多的复杂去对抗复杂。 用最简洁的模型承载最必要的复杂,这才叫成熟。


最后一道,这道最扎心:

墨叔:假设你今天在评审会上端出一个极简方案:一个用三条规则、二十行代码解决的事,你砍掉了原方案里的消息队列、状态机和一套配置中心。你论证充分、边界清楚。结果它被否了。你说,为什么?

你:技术上说不通吧?还是我没讲明白?

我的参考答案(供你对照): 大概率跟技术无关,答案在王东岳讲的”生存结构”里。第一,简单方案显得”不够专业”——在一个崇尚架构感的团队里,用三条规则而不是上消息队列,容易被读成”太简陋、没技术含量”;人们习惯把复杂误读为严谨,把简单误读为随意。第二,简单方案会让某些工作岗位显得多余——有些复杂架构之所以活着,是因为有人专门维护它,极简方案一落地,这部分工作就消失了,这触及局部利益,必然引来软抵抗。第三,简单方案的”可证伪、可追责”让人不安:一个边界清晰、谁改崩了谁负责的模型,比一个谁也说不清责任在哪的复杂系统更让人不敢碰——因为简单意味着赖不掉。第四,认知惯性和沉没成本:团队已经为复杂方案付了学习费,切换意味着重新理解、重新培训。所以推动简单从来不只是技术说服,还得处理”复杂代偿”背后的利益与惯性。王东岳讲文化是自洽封闭的,团队的技术文化同样自洽封闭——要撬动它,光讲道理不够,还得有结构上的松动,比如换一个能为结果负责的决策口径,或者先在一个低风险场景里把简单方案跑出数据来。


墨叔:最后把这套东西压到你明天要干的事上。我给你几条,都能直接用。

你:说。

墨叔:第一,KISS 不是口号,是模型的生存律。你一定见过那种”看起来很专业”的代码:本来三个 if 能写完的函数,被封成策略模式加工厂加注解驱动的配置中心,还配三页说明。理由通常是”为了扩展性”。如果这个扩展三年没发生,那多出来的全部结构就是王东岳说的过度代偿——降低存在度,让系统更难懂、更难改、更脆。成熟工程师不写”未来可能需要”的代码,他写”现在确实需要、且已经删不掉”的代码。

你:第二条我猜是 YAGNI。

墨叔:对,YAGNI 就是选择律在工程管理上的投影。在缺乏事实——也就是真实需求——支撑时,任何”提前设计”都只是未经证实的假设,塞得越多,将来被证伪、被废弃的概率越大。与其赌一个复杂的通用框架,不如先用最直白的代码把问题解掉,让真实流量和数据来告诉你哪里真需要抽象。

你:第三条呢?

墨叔:技术债的本质,是复杂代偿压垮了存在度。每一次”先这样上线吧,后面再改”,都是给系统加一层代偿;代偿累到一定程度,可维护性归零,于是出现那个经典场景:没人敢动它,任何改动都要全链路回归,新人读两周还找不到边界在哪。所以定期还技术债,本质就是定期做一次奥卡姆修剪:把那些曾为应急而生、现在已无必要的复杂,砍回简单。

你:那选型呢?我天天纠结这个。

墨叔:选型上剃刀给你一个硬指标。方案 A 简洁、假设少、易验证;方案 B 功能全、覆盖广、但参数和分支爆表。在缺乏决定性反证时,选 A——不是因为 A 一定对,是因为 A 的被证伪成本和替换成本都更低:万一错了,你亏得少、换得快。我给你走一遍数据库选型:候选是 MySQL、PostgreSQL、MongoDB、TiDB、自研 KV。业务是标准结构化交易数据,事务一致性是硬要求,那 MongoDB 的解释力不足,先出局;当前数据量和并发离分库分表临界点还很远,那 TiDB 的”分布式”这层代偿是多余的,出局;PostgreSQL 的多模型能力现在用不上,但它不构成额外负担,留作备选;最后在 MySQL 和 PostgreSQL 之间,如果团队百分百熟 MySQL、生态工具齐全,就选 MySQL——理由不是它更先进,是同等解释力下它的假设最少:团队不需要为一个新引擎额外付学习代偿。

你:这么一走,决策过程本身就是一次修剪。

墨叔:每一步都在问同一句:“这个额外实体,真必要吗?“养成这个习惯,你的技术选型会从”追新”变成”算账”。再给你两条收尾的。第五,警惕伪简洁:用魔法宏、深链式调用、过度抽象的 DSL 把逻辑藏起来,表面行数少了,实则把认知成本转嫁给每一个读代码的人。判断标准只有一条——一个新人,或者凌晨三点的你,能不能在五分钟内真正搞懂它并安全地改一行?不能,就还不够简单。

你:第六条?

墨叔:微服务,我专门单列,因为它是违反选择律的重灾区。很多团队一上手就拆二三十个服务,理由是”大厂都这么干""为了高可用”。你拿剃刀量一下:你们现在的团队规模、发布频率、一致性要求,真需要二十个独立进程、独立数据库、独立运维管道吗?对绝大多数中小业务,一个模块边界清晰的单体,假设更少、链路更短、排查更快、部署更简单。盲目微服务引入的是一整套分布式固有的复杂性——网络、一致性、链路追踪、服务治理——这些代偿在没有对应结构需求时是纯负债。系统拆得越碎,任何一次故障的定位越靠运气,存在度被吃得越低。

你:那怎么把这套东西变成团队的习惯,而不是只有我一个人念经?

墨叔:把它焊进评审。代码评审除了问”对不对""全不全”,最该多问一句”这里能不能更简单”。具体盯三类冗余:一,重复的相似代码能不能收成一个稳定函数;二,为”未来可能”预留的抽象,现在有没有真实调用方;三,配置项、开关、分支是不是多到了没人说得清每个取值的后果——参数爆炸就是假设爆炸。一个团队只要把”能不能再删一点”变成评审的默认动作,技术债增速会肉眼可见地掉下来。这不是抠门,这是把模型选择律制度化。

你:还有一个我一直很别扭的地方:写方案。我写三十页,领导说太长没时间看;我写一页,又被说不够严谨。

墨叔:这恰好是本章的另一面。方案的长度不是严谨度的度量,它是”你有没有找到结构”的度量。三十页那份,通常是把所有你想到的情况平铺成清单;一页那份,如果它写的是”核心判断三条、关键取舍两处、风险与回滚一段”,那它反而是更强的模型——因为它做了硬断言,能被人当场反驳。

你:可细节呢?细节总得有地方放。

墨叔:放附录。主干必须能被人在五分钟内攻击。一份不能被五分钟攻击的方案,不是严谨,是不可证伪。 那三十页里最危险的地方在于:读者的注意力被均摊到每一页,于是最关键的那个假设——比如”我们假设上游会保证幂等”——被埋在第十九页第三段,谁也没看见,直到它半年后炸掉。

你:所以简单的表达也不是省事,是把靶子明确亮出来。

墨叔:亮靶子是勇气,不是偷懒。这也是为什么我说简单的人往往更敢担责——他把可以被反驳的地方摊在了明面上。

你:最后你总得说点软的吧。

墨叔:软的这条其实最硬:简单是一种对后来者的善意。你写的代码大概率不是你一个人维护到底。简洁的模型是你留给继任者的认知遗产;繁复的代偿是你留给他的认知牢笼。而且复杂会遮蔽——它会让接手的人误以为”这系统本来就该这么绕”,于是不敢动、不愿懂,最后整个团队被这套复杂锁死,谁都在维护,谁都不理解。你今天多删一层,是替三年后那个不认识的人省下三个通宵。

你:我明白了。今晚我就打开最近一次提交,挑一个我自己都觉得有点绕的函数,问它有没有没被证明必要的实体。

墨叔:砍完跑一遍测试,你会有手感——“简单者胜”不是哲学,是能被指头摸到的东西。最后留你一个问题,不用现在答:下一回有人端出一个”看起来很专业”的方案,你别急着佩服,先问一句——它多出来的那部分,到底是必要的代偿,还是只是没人敢删的累赘?而如果那个方案是你自己端出来的,你还问得出这句话吗?