软件维护怎么做才不越修越乱?我用删除预算+维护日历,把故障率从23%降到9%

🔑 关键词:软件维护,技术债,删除预算,依赖升级,故障复盘

📖 摘要:从救火式维护转向熵减式维护:用维护日历、删除预算、依赖分级和恢复演练,把软件维护从修bug变成控制变更。含具体步骤、参数和踩坑记录。

软件维护怎么做才不越修越乱?

图片

先说个丢人的事。2023 年 4 月,我负责的一个内部工单系统在周三凌晨升级 PostgreSQL 12 到 15,原计划 2 小时,结果从 1:10 干到 6:40。 不是数据库起不来,是一个导出 PDF 的旁路服务挂了:容器基于 alpine:3.18,升级 OpenSSL 后 wkhtmltopdf 找不到 Noto CJK 字体,中文全变方框。 更离谱的是,这个服务没人记得是谁加的,代码仓库最后提交停在 2021 年 9 月,compose 文件里还写死了一个测试库地址。 那天早上我一边回滚,一边在群里发“先恢复,再复盘”,心里清楚:这不是维护,这是还债,而且利息已经滚到工资级别。

后来我把维护这事拆成两个指标看,才想明白一点。 很多团队把软件维护等同于“修 bug”,所以永远在救火;但 bug 只是症状,真正咬人的是变更熵:配置漂移、依赖腐化、没人敢删的开关、没人知道的接口。 我们做过一次粗算,2023 年上半年生产变更 147 次,其中 34 次失败,变更失败率 23%;平均恢复时间 MTTR 4 小时 20 分。 到了 2024 年下半年,同样规模,变更 162 次,失败 15 次,失败率 9.2%;MTTR 1 小时 10 分,月均告警从 210 条降到 60 条左右。 不是我们招了高手,而是把“维护”从项目制改成了日历制,并且给删除留了预算。

图片

对比:救火式维护 vs 熵减式维护

我见过最常见的维护节奏是这样:一年一次大版本升级,窗口留 6 小时,负责人临时拉群,回滚靠“把旧包再传一次”。 这种模式不是完全没用,但它有个致命问题:所有风险都堆在同一天,所有人都在赌。 熵减式维护反过来,每周 30 分钟看依赖,每月一次小升级,每季度一次删除冲刺,每半年一次恢复演练。 成本上,救火式每月大约 3 人天,熵减式 1.5 人天;但前者经常半夜炸,后者基本能在工作时间处理。

具体维度我列个表,不吹牛,都是我们踩出来的:

图片

维度 救火式维护 熵减式维护
触发点 出故障、CVE 爆了 日历到点、EOL 临近
升级频率 1 年 1 次大版本 每月小版本,季度评估大版本
回滚 手动找旧包 每次变更带回滚脚本
删除 不敢删,先注释 季度删除冲刺,删开关/死代码
指标 看有没有报错 看变更失败率、MTTR、告警数
责任人 谁有空谁上 每个服务有 owner 和备份 owner

这张表里最反直觉的是“删除”。 大多数维护文档都在教你怎么升级、怎么备份,但很少教你怎么删。 我的独立观点是:软件维护的核心不是修,而是删和锁。删掉不再需要的复杂度,锁住必须稳定的接口和配置。

图片

我现在的维护日历,具体到几点

我们现在的节奏很土,但能跑。 每月第一个周三 10:00-11:00,依赖盘点:跑 pip list --outdatednpm outdatedmvn versions:display-dependency-updates,把结果丢进表格。 分级规则:红色 30 天内处理,包括已 EOL、有高危 CVE、阻塞升级;黄色 90 天;绿色 180 天。 比如 Python 3.8 在 2024 年 10 月停止安全更新,PostgreSQL 12 在 2024 年 11 月 EOL,Django 3.2 在 2024 年 4 月 EOL,这些就不能再放绿色。 每季度最后一个周五 14:00-17:00,删除冲刺:删 feature flag、删死代码、删三个月无调用的接口、删临时脚本。 每半年一次恢复演练:从备份恢复一个真实环境,记录 RTO。我们目标 RPO 15 分钟,RTO 2 小时;第一次演练 RTO 是 5 小时 12 分,不合格,后来写了 7 个故障单才压到 1 小时 48 分。

图片

删除冲刺不是瞎删。 前端我们用 knip 和 ts-prune 扫未使用导出,后端 Python 用 vulture,Java 用 spotbugs 加自定义规则。 但工具只能给候选,最后一定要看调用链和日志。 我们有个开关叫 enable_new_export_flow,从 2022 年 11 月上线后一直是 true,代码里 43 处判断,删掉后构建时间从 8 分 32 秒降到 2 分 18 秒。 另一个接口 /api/v1/legacy/orders/sync,日志显示 180 天只有 3 次调用,全是内部测试账号,确认后下线,省了一台 4C8G 的容器。

给普通团队的落地步骤,别照抄

如果你现在系统不大,别一上来搞平台。 第一步,建服务清单:服务名、owner、语言版本、依赖数、EOL 日期、备份方式、RTO、RPO。 第二步,依赖分级:红黄绿,红色 30 天,黄色 90 天,绿色 180 天,谁不处理谁在周会上说原因。 第三步,设变更预算:每周最多 3 个生产变更,每个变更必须有回滚脚本、验证清单、观察窗口。 第四步,开删除预算:每季度新增代码如果超过 1000 行,至少删掉 300 行无效代码或 10% 的旧开关。 第五步,做恢复演练:不是看备份文件在不在,而是真的恢复一次,记录分钟数。

图片

我踩过两个坑,第二个更蠢。 第一个坑是把维护窗口放凌晨,觉得影响小,结果没人愿意半夜回消息,出事响应更慢。 第二个坑是只看 CVE 分数,不管实际暴露面。我们曾经为一个内网后台的高分 CVE 折腾两天,最后发现那个服务端口只对跳板机开放。 现在我更看重“可达性”:这个漏洞能不能从公网、办公网、容器网络摸到?摸不到就降级,摸得到就升级。 维护不是追求零风险,那是幻觉;维护是让风险可见、可排期、可回滚。

最后说句可能挨骂的:软件维护做得好不好,不看你们修了多少 bug,看你们敢删多少东西。 一个系统如果三年没删过代码、没关过开关、没下线过接口,它的维护成本一定在暗处涨。 你可以从下个月开始,先做一件事:把最老的 10 个 feature flag 找出来,确认状态,能删的删,不能删的写 owner 和过期时间。 这事儿不酷,但比凌晨三点在群里发“谁动了配置”有用得多。

🏷️ 标签: