复杂度治理:本质复杂度与偶然复杂度 — editorial cover photo

复杂度治理:有的复杂该砍,有的该拥抱

软件复杂度分两种,问题固有的本质复杂度,和我们自己加的偶然复杂度。前者要拥抱,后者要砍掉。

2026年3月10日·返回文章列表
文章大纲

接手过一个情报中心模块,逻辑嵌套七八层,一个 if 里套 if,改一个分支要理半天。前任不是写不出好代码,是没人停下来分辨,哪些复杂度是业务真的要的,哪些是我们自己加上去的。重构完迭代周期缩短了一半,没砍任何功能。

这就是复杂度治理最关键的那个区分。Fred Brooks 在《没有银弹》里把软件复杂度分成两类,本质复杂度和偶然复杂度。前者来自问题本身,你绕不开。后者来自我们的实现,可以也应该砍掉。混淆这两类,是绝大多数「系统越来越难维护」的根源。

本质复杂度,你只能正确实现它

PlantUML
正在加载图表…
PlantUML diagram

图:本质复杂度来自问题本身要拥抱、正确建模;偶然复杂度是我们亲手加的要砍掉。混淆这两类是系统越来越难维护的根源。

业务规则、领域逻辑、规模要求,这些是问题固有的复杂度。

折扣怎么算、审批流怎么走、风控规则怎么匹配,这些规则是业务定义的,再好的技术也消除不了,你只能理解它、正确实现它。金融监管规则、电商库存逻辑、支付清结算流程,每个领域都有自己绕不开的复杂度,这是你要花力气去啃的部分。

一个常见的误区是抱怨「业务太复杂」。但业务就是这么复杂,抱怨没用。工程师该做的是想办法正确建模这些复杂度,DDD(领域驱动设计)就是帮干这个的,把业务语言映射成代码结构,让复杂度有地方安放、能被理解。

判断本质复杂度的标准很简单,问一句,这个复杂度是业务要求的吗。是,那它就是本质的,拥抱它,把它实现对。

偶然复杂度,我们亲手种下的负担

剩下那些不是业务要求的复杂度,全是我们自己加的。它有几个典型来源。

过度抽象。为了「将来可能要复用」提前加了三四层接口和工厂,结果未来从未来,抽象层成了理解负担。我见过一个只有一个实现的服务,套了接口层、工厂层、策略层、配置层四层壳。问为什么,答曰「方便扩展」。方便了三年,扩展从没发生过,新人接手要先花两天读懂这套壳。

冗余代码。复制粘贴的重复逻辑,同一个计算散落在五六个地方,改一处漏四处。这种复杂度是懒出来的,不是设计出来的。

不当耦合。模块之间不必要的依赖,改一个功能牵连一片。判断信号很直接,改一个功能要动很多文件,基本就是耦合过高。

遗留设计。业务已经变了,老架构没跟上,新需求硬塞进旧结构,拧巴出新的复杂度。

技术选型不当。用复杂的工具解决简单的问题,引一个重量级框架就为了用一个函数,带来的维护成本远超收益。

一个反直觉的事实,过度抽象比适度重复更糟

PlantUML
正在加载图表…
PlantUML diagram

图:Rule of Three——第一次直接写,第二次容忍重复,第三次才抽象。凭想象预先抽象几乎都是错的。

这点值得单独拎出来讲,因为它违反很多人的直觉。

很多人把「消除重复」当成铁律,看到两处相似代码就想抽公共方法、抽基类、抽工具层。但适度的重复其实是可接受的,它容易识别、容易处理。为了消除重复而建的复杂抽象层,反而更糟,它增加理解成本、阻碍修改,而且一旦抽象错了,所有用到的地方都得跟着遭殃。

业界有个 rule of three,重复出现三次再抽象,别预先抽象。第一次直接写,第二次容忍重复,第三次才动手抽。这个原则的内核是,抽象要有足够的真实样本支撑,凭想象抽出来的抽象几乎都是错的。

抽象值得做的判据是,复用收益明显大于理解成本。如果抽完之后调用方还得理解一堆配置和约定才能用,这个抽象大概率是负资产。

怎么识别偶然复杂度

落到日常,有几个信号能帮你揪出偶然复杂度。

改一个功能要动很多文件,耦合过高。理解一个类要花很久,职责过多。一个简单需求要写很多代码,抽象过度。这三条命中任何一条,基本就有可以砍的偶然复杂度。

更深一层的信号是,问「为什么这么做」,答不上来或者答「一直都是这样」,往往是偶然复杂度。真正必要的复杂度,背后一定有清晰的业务或技术理由,说得清为什么。

治理的几个原则

PlantUML
正在加载图表…
PlantUML diagram

图:YAGNI 不做用不到的、KISS 选最简的、童子军原则顺手清理、定期重构对抗腐化——四者都是渐进式治理。

YAGNI,不实现用不到的功能。别为想象中的需求设计,需求真来了再加,加的时候你会比现在更清楚它到底要什么。

KISS,能用简单方案就别用复杂的。最简方案往往是对的,复杂只有在简单真的不够时才上。

童子军原则,改代码时顺手把它改得比来的时候干净一点。复杂度是渐进积累的,也只能渐进治理,别指望一次大重构解决所有问题,那种重构往往会引入新的偶然复杂度。

定期重构。软件会自然腐化,业务变、人流动、紧急修复随手写,偶然复杂度会慢慢堆起来。承认这件事,定期专门留时间清理。

简单不是简陋

最后说一个认知层面的点。

简单系统的力量在于它好理解、好维护、好扩展。复杂系统哪怕功能强大,长期来看是负担,改一处崩多处。

但简单不等于简陋。简陋是没想清楚就乱写,简单是想清楚了之后用最少的代码正确解决问题。两者的区别在脑子里,不在代码行数上。一个看起来简单的方案,背后往往是把问题想透了,把偶然复杂度都剔除了。

一个判断标准

交付一个方案前问自己,如果新人接手,他多久能看懂。要很久,大概率是偶然复杂度太多。能很快,说明复杂度都用在刀刃上。这个标准比任何代码度量都准,因为它直接反映了系统的可维护性,而可维护性才是复杂度治理的终极目标。

复杂度治理是一场持续的仗,没有终点。软件不会自己保持简单,它只会越来越复杂,除非有人主动把它拉回来。这件事的价值不会立刻显现,但半年一年后,你的系统还能被人轻松接手和演进,这就是治理的全部回报。

Continue Reading

关联文档推荐

查看全部
架构演进:从单体到微服务的渐进之路 — editorial cover photo权衡思维:软件设计没有银弹 — editorial cover photo技术成长路径:从写代码到做决策 — editorial cover photo
作者:archy.shawn
声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。