有些系统从架构图上看并不复杂。
几个服务,一套数据库,一个消息队列,外加缓存和定时任务。每个组件都有成熟文档,单独拿出来也容易理解。但维护它的人仍然觉得累。
累的来源往往不在组件数量,而在那些没有被系统表达、只能由人记住的关系。
“这个任务显示运行中不一定真的运行中,要再看日志。”
“修改设备配置以后,需要手动重启另一个 APP。”
“这个 GUID 在旧模块里叫 deviceId,在新模块里才叫 guid。”
“这类告警不能按通用逻辑查,因为超级管理员的范围不一样。”
每句话都不长,合在一起却构成了系统真正的操作手册。它没有版本,没有测试,通常只存在于几个人的记忆和聊天记录里。
复杂度会转移,但不会消失
为了赶进度省略状态机,复杂度会转移给排障的人;不统一设备身份,复杂度会转移给每一个上下游;不设计失败恢复,复杂度会转移给值班人员;不写迁移脚本,复杂度会转移给部署现场。
代码少了,不代表系统简单了。可能只是把逻辑从机器能执行的地方,搬到了人必须记忆的地方。
我现在会留意一种危险信号:解释系统时是否频繁使用“记得”“注意”“特殊情况下”和“到时候手动”。这些词不是绝对错误,但每出现一次,都说明某个约束尚未被建模。
把记忆还给系统
并非所有知识都值得写成代码。可反复造成事故的隐性规则,应该逐步找到归宿:
| 人脑中的提醒 | 更合适的归宿 |
|---|---|
| 参数不能同时开启 | 配置校验 |
| 操作必须遵循顺序 | 状态机或工作流 |
| 某字段全局唯一 | 数据库约束 |
| 服务异常要重启 | 进程守护与健康检查 |
| 发布前必须执行某命令 | CI/CD |
| 两套标识正在迁移 | 兼容层和迁移指标 |
这样做的目的不是消灭人的参与,而是把人的注意力留给真正需要判断的地方。
文档不是最后的容器
文档能承载背景、取舍和操作说明,却不能代替所有约束。写着“端口不能重复”的文档,仍然比不上启动时直接拒绝重复端口;写着“任务需要先停止再删除”,仍然比不上接口内部保证状态合法。
最可靠的知识通常有多层表达:代码负责强制,测试负责举例,日志负责解释,文档负责讲清为什么。
系统复杂度无法被完全消除。业务本身有复杂性,分布式环境也有真实的不确定性。工程能做的,是不要继续发明那些只能依靠记忆维持的复杂度。
一个系统是否容易维护,可以看新人多久能开始工作;也可以看老成员休假时,团队是否仍然从容。
如果某个人不在线,系统就突然失去一部分能力,那么那部分能力从来没有真正属于系统。