当系统功能还不多时,改一处就生效,测一轮就放心,一切都很直观
等版本一版版往上迭代,你会慢慢发现:
- 改一个小需求要翻好几个模块
- 新功能一上线总会莫名碰到旧逻辑
- 故障排查第一步不是看日志,而是先问“最近谁动过配置和开关”
真正在压垮你的,不是那一次大改动,而是一层一层叠上去的隐性复杂度
==============================
一、需求越赶越“临时”,分支就越堆越厚
1、先顶上去再说的 if else
典型模式是:
- 赶上线时先沿着原有流程插入一段判断
- 老用户一条逻辑,新用户一条逻辑,大客户再一条逻辑
- 心里想着“先这样,将来有时间再重构”
短期只是多了一层 if,长期变成:
- 同一个请求要经过多层分支才能走到终点
- 任何新需求都得先问“这是哪一类用户,该走哪条支路”
- 新人根本不敢随便改,一动就怕撞到某个历史特例
每一次“先顶上去”,都在给未来的你加一层迷宫墙
2、“兼容一下”的特例越塞越多
还有一种隐性加法是:
- 某个渠道需要特殊折扣,就在原逻辑里多加一条判断
- 某个国家要用另一套计费,就在原接口里塞一套计算方式
- 某个大客户要自定义规则,就在原服务里专门留出一块逻辑
看上去都只是“多照顾一个场景”,但几年下来你会发现:
- 代码里到处是“如果渠道等于某值就走另一套逻辑”
- 任何一个新需求都要问“会不会影响当年那个大客户”
- 重构几乎动不了,因为谁也不知道删掉哪一段会炸谁
隐性复杂度就是这样被一条条特例塞出来的
==============================
二、配置越灵活,系统越难预测
1、配置项爆炸成另一套“软代码”
为了把逻辑从代码里挪出去,大家会不断加配置:
- 一个功能拆成多个开关,逐一控制细节
- 灰度、白名单、黑名单都通过配置驱动
- 各环境、各租户、各渠道都有自己的配置组合
表面上是灵活可控,实际效果是:
- 配置本身变成一套没有类型检查的“影子代码”
- 多个功能叠在同一配置文件里,组合出无数未被验证的路径
- 真出问题时,谁也说不清到底是哪几组配置一起搞坏了行为
最终要排查问题,需要做三件事:
- 看代码
- 看配置
- 追溯谁在什么背景下改过配置
这背后就是隐性复杂度从代码层蔓延到了配置层和运营层
2、开关没有生命周期,只会越加越多
很多开关一开始只是“方便灰度一下”:
- 临时开一个选项给少量用户试用
- 降级时临时绕过某个逻辑
- 某次事故后临时加了个“保险开关”
但上线之后没人负责收尾:
- 灰度结束也不关开关,只是默认设成某个值
- 降级结束也不拆逻辑,只是把开关再切回来
- 时间久了,所有人都只敢“再加一个新开关”,不敢删旧的
久而久之:
- 开关列表成了团队记忆的坟场
- 很多已有逻辑和开关已经没人完全搞得懂
- 每次需求评审都要浪费大量时间确认“旧开关要不要一起动”

==============================
三、依赖和耦合不断加厚,全局行为越来越难预判
1、服务之间关系清单缺失
功能多起来后,自然会有:
- 更多公共组件
- 更多跨服务调用
- 更多链路依赖
但如果没有一份随时间维护的“依赖全景图”:
- 哪些接口是关键路径
- 哪些模块被哪些业务共享
- 哪些调用一旦变慢会拖垮整条链路
就只能靠印象和口头说明,这意味着任何一个服务改动,都有机会在你没意识到的地方引发联锁反应
2、不区分“局部故障”和“系统性风险”
同样是一个小 bug:
- 落在边缘模块,只影响少数功能
- 落在公共组件或共享配置上,就会波及全局
如果设计时没有刻意区分:
- 哪些功能的失败可以被局部容忍
- 哪些组件必须加隔离和熔断
- 哪些路径出问题时要优先降级非关键功能
那系统在功能不多时看着一切正常,一旦迭代几轮叠上去,就变成“到处都是关键路径”,任何错误都有机会引爆全场
==============================
四、从“堆功能”转到“控复杂度”,要动两层东西
1、给每个模块设一个简单的复杂度边界
不需要上很重的制度,只要做到几条:
- 配置项数量超过一定值时,必须先删旧的再加新的
- 某段代码分支层级过深时,新需求优先考虑拆模块,而不是继续加 if
- 特例多到数不过来时,要么单独抽出服务,要么明确哪些要被砍掉
让每次设计讨论里都出现一句话:
- “再这么堆,会不会让这个模块变成以后谁都不敢动的黑盒”
有了这个意识,很多“看起来方便”的方案会自动被筛掉
2、把临时方案和踩坑模式显式写出来
两件事可以尽快做:
- 建一个技术债清单
- 每个“先这么凑合”的方案写上原因、风险、触发重构的条件
- 定期在迭代规划时看一眼,选几条真正还掉
- 为复发过的事故类型起名字
- 总结它的触发条件和典型征兆
- 加进代码评审、架构评审的检查项里
久而久之,团队会慢慢形成一些“共有记忆”:
- 一看到某种 if 组合或配置模式,就有人会说:“这看着像我们当年那次崩盘的前奏”
- 一看到某个改动,就会有人问:“是不是又在加一层复杂度,而不是替换掉旧的那层”
当隐性复杂度被这样一点点摊开、标记、设边界,系统就不会再在某个版本突然“长歪”,而是始终在一个可预期的复杂度范围内慢慢发展
==============================
五、顺带说说用易路做“统一出口层”,怎么少踩一点坑
如果你的系统里还掺着一大堆“跨区访问、代理出口、采集脚本”的需求,出口层本身也会变成复杂度来源:
- 同一批 IP 既跑核心业务又跑采集脚本
- 不同团队随手加出口、改配置,最后谁也说不清现状
- 一出问题先怀疑代理,结果查半天是自己结构堆炸了
这时候,用一个可视化、可分组的代理平台,至少能把“出口这一层”的复杂度托住。以易路代理为例,你可以:
- 按业务维度建不同线路组
比如核心后台一组、运营一组、采集一组,代码只认组名不认具体 IP - 把“高价值账号”和“高频采集”硬拆到不同类型线路上
核心账号用住宅线,采集用机房线,风险和成本各走各的 - 用面板的成功率和延迟指标,反向校验自己的结构设计
哪组线路经常打爆、哪组几乎闲着,一眼就能看出来,方便你调整队列和优先级
你不需要指望易路帮你消灭复杂度,但可以让“代理出口”这一块,从一堆散装脚本配置,变成一层可视化、可管控的基础设施。剩下的,就是在这层之上,把自己的功能复杂度慢慢削平。