你可能常有这种感受:
版本一版接一版发,功能一块块加上去,线上没出大事故,但日常开发越来越折腾:
改个小需求要翻半天代码;
排查一个小问题得拉好几个人上语音;
谁提“重构一下”都知道难度很大。
这不是你矫情,往往说明一件事:
系统的隐性复杂度,已经在多年迭代中悄悄堆到了边缘。
这篇文章只讲三件事:
一是复杂度到底是怎么被一点点堆出来的;
二是有哪些明显信号在提醒“快失控了”;
三是还能通过哪些简单动作,把复杂度拉回相对可控的范围。
==============================
一、舒适期里,复杂度在悄悄攒
早期系统通常比较“听话”:
- 新功能加得快;
- 问题多半能直接定位到某个模块;
- 相关的人都比较熟代码,脑子里有一张差不多的架构图。
正因为一切还撑得住,
大家容易形成一种错觉:“先干活要紧,复杂度以后再管”。
但每一次“先凑合一下”都在往里塞东西:
- 这里多一个 if 特判;
- 那里多几个状态字段;
- 新需求先挂在老链路上。
短时间看不出问题,几年之后就变成谁也不敢动的大泥球。
==============================
二、隐性复杂度是怎么被一点点堆出来的
1、特例越来越多,主流程被淹没
典型演进路径:
- 先实现一条“标准流程”;
- 业务说这里有个例外,加一个判断;
- 又来一个例外,再叠一个条件;
- 久而久之,“例外”比“主流程”还长。
后果是:
- 文档里的规则和代码里的实现逐渐对不上;
- 谁都不敢删旧分支;
- 改一处行为,很难保证别的路径不跟着变。
复杂度不在功能数量上,而在这些看似无害的特判组合里。
2、状态乱长,没人再敢碰数据结构
随着需求增加,状态会这样膨胀:
- 表和对象上不断加字段、枚举、标记;
- 不同模块对同一字段的理解并不一致;
- 很多状态组合从来没认真设计过。
于是:
- 一个行为是否生效,要看 3–5 个字段组合;
- 某些诡异 bug 只在极少数状态下出现;
- 谁也不敢删字段,只能继续往上加。
看上去只是“多了几个字段”,
实际是状态空间指数级膨胀。
3、依赖链条被一点点拉长
出于“复用”和“解耦”,系统之间的调用会越来越密:
- A 服务调 B 服务,B 又调 C;
- 前面加网关,后面挂规则引擎,中间插埋点与缓存;
- 旁边还绕了一圈代理、代理出口和各种中间件。
每一步都能给出合理解释,
叠在一起就是:
- 任何一次请求的真实路径很难说清;
- 任意一环抖一下,整个链路都跟着不稳;
- 故障定位从“找一段代码”变成“先猜哪一层出了问题”。

==============================
三、哪些信号说明复杂度已经接近失控
出现下面这些情况,就该警惕了:
1、小需求改动越来越“重”
- 改一个小按钮,评审要半天;
- 字段扩展牵出一堆“历史兼容”讨论;
- 真正写代码的时间很少,大部分时间花在“怕出事”。
这说明不是需求难,而是系统对改动极其敏感。
2、排查严重依赖“老人记忆”
- 新人看代码看不出意图,只能问“当时为什么这么写”;
- 很多逻辑只能用“那次事故之后加的兜底”来解释;
- 一两个老同事一离职,模块就没人敢动。
系统真实设计不在文档和图里,只在少数人口述里。
3、修 bug 主要靠“再加个条件”
遇到问题时团队的默认做法是:
- 再加一个 if;
- 再多一段兜底;
- 先把现象挡掉再说。
很少有人提出:
- 把这段流程抽出来重画;
- 把状态重新分组建模;
- 删除几条过期逻辑再实现新规则。
补丁越打越多,复杂度只会持续上升。
==============================
四、怎么把复杂度往回拉一点
1、先画出“真实现在”的关键链路
不要一上来就谈“目标架构”,先搞清楚现在关键链路到底长什么样。
从一两条核心场景开始:
- 下单全链路
- 登录与权限
- 数据采集配合代理出口那条链路
对每条链路至少要弄明白:
- 经过哪些服务和关键模块
- 受哪些配置和开关影响
- 中间依赖了哪些外部系统
画出来之后,很多“怎么这么绕”的地方会暴露出来,后面重构才有具体落点。
2、给复杂度设“红线”,不是无限透支
可以定一些简单的团队规则,比如:
- 单个模块的条件分支数超过某个值,就必须考虑拆分
- 核心路径上的依赖层数超过某个上限,就要合并或收口
- 对核心数据结构,新增状态前必须讨论“能不能改模型而不是加标记”
不是要追求完美设计,而是让每次“再凑合一把”之前,大家至少意识到在透支复杂度。
3、每个迭代顺手做一点“减法”
一次性重写全系统很不现实,更可行的做法是每个迭代顺手还一点债:
- 清掉确认不用的配置与开关
- 删除明显死分支和重复逻辑
- 把重复出现的校验和转换抽成统一模块
- 对最常出问题的那条链路,花一个迭代单独梳理
这些“小减法”不会立刻把系统变干净,但会慢慢阻止复杂度继续失控地堆上去。
4、用易路把“出口层复杂度”先收一收
很多系统的隐性复杂度,其实是被出口层放大的:
不同脚本各写一套代理接入方式、不同环境各自维护 IP 列表、有人手动切线有人写死在配置里,最后谁也说不清“这条业务现在到底走哪”。
这块反而是最容易做减法的地方,你可以把“代理出口”统一收敛到易路代理上来做一层:
- 把按环境、按业务、按敏感级别分散的出口,全部整理成几类清晰的线路组和标签
例如:核心业务组、运营组、采集组、测试组 - 让脚本和服务只认标签不认 IP,后续换线、扩容、降级都在易路面板里改,不再动一堆零散配置
- 通过易路后台的延迟、成功率、错误率统计,替代掉自家系统里一堆临时埋点和脚本式排查
对你来说,这相当于给“出口层复杂度”来了一次集中收口:
接入方式统一、线路分组统一、观测入口统一,后面每次加功能、加脚本,只需要接在既有标签和线路组上,不再额外堆一层“出口玩法”。
==============================
功能越迭代越多,复杂度上升是自然结果
真正危险的是团队习惯了“先加再说、不敢删、不敢改”,
久而久之谁都知道系统很复杂,却没人说得清“复杂到哪了”。
当你发现:
- 压测都能过,真业务一上量就各种抖;
- 排查越来越依赖几个人的口头记忆;
- 改动任何地方都像在拆炸弹,
基本就可以确认,隐性复杂度已经到了该认真管理的时候。
从今天起,可以先做三件简单的事:
- 给任务按业务价值分级,别再一视同仁;
- 画出一两条关键链路的真实结构图;
- 在评审时固定问一句“这次改动会不会再推高复杂度”。
只要这三件事能持续做下去,
系统就会从“越来越难碰”,
慢慢变回“虽然复杂,但至少看得见、说得清、改得动”的状态