软件工程里常见但没人写进规范的 7 条经验
软件团队通常会写下编码规范、发布流程、值班手册和故障预案,但很多真正决定事故处理质量的经验,并不在文档里,而是靠线上故障反复教育出来的。
这些经验有一个共同点:平时看起来像常识,出事时最容易被忽略。下面这 7 条,覆盖了发布、数据库、日志、第三方依赖和风险操作,几乎每个工程团队都会反复遇到。
1. 线上刚出问题,默认先假设和刚发布的变更有关
线上故障发生后,最常见的第一反应是:问题不可能和自己刚提交的改动有关,因为影响面看起来完全不同。
现实往往相反。只要故障出现在发布之后,最新变更就应当被视为最高优先级嫌疑对象。此时最差的做法,是先花一小时证明“这不是我改坏的”。
更稳妥的处理顺序是:
- 先回滚
- 先恢复服务稳定
- 再开始排查根因
这样做的原因很直接:
- 故障现场每多持续一分钟,业务损失和排障复杂度都在上升
- 系统处于异常状态时,继续调查往往会受到噪声干扰
- 先恢复稳定基线,才能更准确地判断问题是否由新变更触发
回滚优先于辩解
只要最近部署过任何内容,无论改动看起来多小,都应优先考虑撤回。很多故障并不是直接修改了报错模块,而是通过配置、依赖版本、时序变化、资源占用或数据副作用间接触发。
因此,事故响应中的默认原则应当是:
发布后出故障,先回滚,再调试。
2. 备份只有成功恢复过,才算真的可用
很多系统显示“已开启备份”,但这不等于具备可恢复能力。没有实际执行过恢复演练的备份,最多只能算“看上去存在”。
数据库、对象存储、卷快照、托管服务备份,真正出故障时要回答的不是“有没有备份”,而是“多久能恢复、恢复到什么程度、由谁来执行”。
需要明确的恢复问题
恢复能力至少要覆盖这些问题:
- 使用的是增量备份,还是固定时间点的全量快照
- 两次备份之间的时间间隔是多少
- 最多可能丢失多少数据
- 谁具备触发恢复的权限
- 是否只有单个人有权限
- 恢复入口在哪里,具体操作步骤是什么
- 恢复耗时大约多久,是 10 分钟还是 2 小时
- 恢复期间应用是否完全不可用
- 恢复完成后,数据是否符合预期
恢复演练不是一次性工作
恢复流程会变化,基础设施会变化,数据量也会持续增长。以前 10 分钟能完成的恢复,数据规模扩大后可能需要 2 小时。
因此,恢复演练必须周期性执行,而不是在系统上线时点一次按钮就结束。只要工程师拥有数据库写权限,就应当理解恢复路径,不能把这件事完全交给基础设施团队。
3. 日志总会在事故发生时暴露设计缺陷
大多数项目在刚开始时都会希望把日志写得“足够完美”,但等到故障真正发生,通常会发现日志要么缺关键信息,要么信息过载到无法搜索。
常见问题包括:
- 缺少定位问题所需的上下文
- JSON dump 被错误解析,字段混乱,不可检索
- 多服务之间没有共享请求 ID 或任务 ID
- 日志过于啰嗦,真正重要的信息淹没在噪声里
在使用代码生成工具或智能体辅助开发后,另一个问题更加明显:日志数量很多,但结构和语义并不稳定,最终导致阅读成本更高。
日志设计的平衡点
可用日志系统至少要做到三件事:
- 保留排障所需的关键信息
- 以可搜索、可聚合的结构化方式输出
- 不制造过量噪声
一个实用的最小检查表如下:
| 检查项 | 目标 |
|---|---|
| 请求 ID / Trace ID | 跨服务串联调用链 |
| 关键业务字段 | 能快速定位对象、租户、任务、用户 |
| 错误上下文 | 包含错误码、异常类型、输入摘要 |
| 日志级别 | 区分 info、warn、error,避免全都打 error |
| 结构化输出 | 便于检索、聚合和告警 |
| 敏感信息控制 | 不输出密钥、密码、完整隐私数据 |
日志设计不存在一次到位。比较可靠的做法,是每次事故后回头补齐“当时最想看到但没看到”的字段,并删除长期无用的噪声。
4. 任何涉及数据的变更,都必须准备回滚方案
很多团队会为代码发布准备回滚,但对数据库迁移、数据修复、批量写入、约束变更缺少同等严格的预案。实际上,凡是碰数据的操作,都应在执行前明确逆向路径。
典型场景包括:
- 新增字段
- 插入数据
- 删除数据
- 修改字段类型
- 调整约束条件
- 数据回填
- 批量修复脚本
回滚方案必须具体到操作级别
如果要新增一个字段,应提前准备:
- 删除该字段的迁移脚本
- 回退相关代码改动的 PR 或提交
如果要插入数据,应提前准备:
- 精确删除这批插入数据的脚本或条件
如果要删除数据,应提前准备:
- 一份安全副本或导出结果
如果要修改字段类型或约束,应提前明确:
- 如何无损恢复原始状态
- 回退过程中是否会触发数据截断、索引重建、锁表或兼容性问题
没有实测过的回滚方案,不算回滚方案
纸面上的回滚流程价值有限。没有实际执行过的回滚脚本,线上一旦出问题,很可能在最需要它的时候再次失败。
可以把这条规则写成最简形式:
只要要改数据,就先写回滚;只要写了回滚,就先跑一遍。
5. 每一个外部依赖都会失败
接入第三方 API 通常很快:注册、拿 key、调用成功、功能上线。困难发生在它开始不稳定的时候。
外部依赖的常见失败形式包括:
- 接口超时
- 返回 429,触发限流
- 返回格式变化
- 局部区域不可用
- 鉴权异常
- 供应商内部故障
- 支持响应过慢
有些团队在设计阶段会问一句“挂了怎么办”,但最终落地通常只有“重试 5 次 + 指数退避”。这远远不够。
可用性会被依赖链放大消耗
假设系统自身目标是 99.9% SLA,如果又引入一个关键外部系统,同样也是 99.9% SLA,那么整体停机时间会显著增加。按年计算,停机时长可能从 8 小时左右增加到 17 小时左右。
只要依赖是关键路径的一部分,系统可用性就不是单系统指标,而是整条链路的乘积。
接入外部依赖前应回答的问题
- 对方的限流规则是什么
- 超过限额后会发生什么
- 是否有提前告警机制
- 外部 API 全部不可用时,影响的是某个功能还是整个应用
- 是否已经在生产环境验证过依赖不可用时的真实行为
- 是否可以缓存响应
- 是否可以把请求写入队列延后处理
- 是否可以回退到陈旧但可接受的数据
- 用户侧如何提示降级状态
- 故障发生时,对方的联系人是谁
- 对方支持 SLA 是多少
必须做故障注入或真实验证
很多团队以为第三方依赖故障只会影响一个边缘功能,结果真实故障发生时,整个应用都被拖垮。原因通常是调用位置比预想更核心,或者错误传播路径没有被隔离。
因此,依赖容灾不能只靠推测,必须验证:
- DNS 解析失败时会怎样
- 超时时线程池或连接池会不会被耗尽
- 熔断后是否能快速失败
- 重试是否放大流量雪崩
- 降级逻辑是否真的生效
6. 只要存在任何风险,就执行“四眼原则”
高风险操作不应该由单人完成,尤其是在拥有高权限的场景下,例如:
- 生产数据库操作
- root 或管理员权限操作
- 生产控制台修改
- 批量删除
- 影响权限、计费、账务的数据修复
- 紧急热修复
四眼原则很简单:只要不能百分之百确认安全,就找另一位工程师一起看。
第二双眼睛的价值
双人复核带来的收益通常不只是“帮忙检查”:
- 解释操作计划的过程中,操作者经常会自己发现漏洞
- 旁观者更容易注意到命令参数、环境、目标实例等细节错误
- 高压状态下,第二个人能显著降低误操作概率
什么时候最应该停下来找人
有一种情况尤其危险:夜里、周末、线上告急,又不想打扰别人,于是打算自己一个人直接做高风险操作。
这通常不是“动作快”,而是风险信号最强的时候。越不方便找人,越说明不应单独执行危险操作。
可以把执行门槛设成明确规则:
- 涉及生产数据变更,必须双人确认
- 涉及不可逆删除,必须双人确认
- 涉及 root 权限操作,必须双人确认
- 涉及临时脚本直接跑线上,必须双人确认
7. 临时修复往往会变成永久实现
软件开发里最持久的东西,经常是“先这样,后面再补”的临时方案。
产品优先级不断变化,新需求总比修旧账更容易获得注意力。一个带着明显妥协的 V1 或 MVP,很可能会在生产环境里存活数年。
问题不在于做最小可行版本,而在于把“简单”做成了“脆弱”,把“范围受限”做成了“靠胶带维持”。
简化实现和凑合实现不是一回事
可以接受的最小方案应该具备这些特征:
- 设计简单,但边界清楚
- 功能有限,但行为可预测
- 性能普通,但不会随机失效
- 不支持未来规模,但不会靠隐式假设勉强运行
不可接受的临时方案通常表现为:
- 依赖手工步骤却没有文档
- 错误路径没有处理
- 数据结构为短期方便硬编码
- 关键逻辑分散在脚本、控制台、数据库手改中
- 没有监控,也没有回滚能力
如果已经知道“以后大概率不会重写”,那就不应交付一个明显脆弱的版本。更现实的策略,是推动一个范围小但工程质量过关的实现。
AI 代码生成场景下,这些经验只会更重要
到了 2026 年,新的工程常识已经很明确:
AI 生成代码的质量,上限取决于上下文质量。
“垃圾输入,垃圾输出”仍然成立,但问题已经不只是提示词本身。智能体会通过规则、技能和 MCP 获得上下文,团队真正浪费时间的地方,是它很快产出了一份“看起来能用”的代码,但随后需要大量返工。
一个典型问题是搜索满足即止:模型在找到第一个看似合理的答案后就停下,没有能力确认那是否真的是正确答案。结果是日志字段不一致、异常处理缺失、回滚路径没写、对第三方依赖的失败模式没有建模。
这使得上面 7 条经验在 AI 辅助开发中更加关键:
- 发布后故障更需要快速回滚,而不是和生成结果争辩
- 备份与恢复流程不能交给“应该没问题”的假设
- 日志必须结构化,否则 AI 生成的冗长输出会加重噪声
- 数据迁移必须带回滚,因为自动生成脚本很容易漏掉逆操作
- 外部依赖的失败模式必须显式设计,不能只靠默认重试模板
- 高风险变更必须双人复核,尤其是 AI 生成的命令或脚本
- 临时方案更容易被自动化批量制造,因此更需要设定最低质量线
如果团队在代码、PR、文档、对话和运行时信号之间缺少统一上下文,AI 工具只会更快地产生不稳定实现,而不会自动带来工程质量。
一份可执行的团队检查清单
下面这份清单可以直接加入发布流程、变更模板或值班手册。
发布与事故响应
- 最近一次发布是否可以一键回滚
- 线上故障时是否先执行回滚而不是先争论归因
- 回滚后是否保留现场信息用于后续排查
备份与恢复
- 最近一次恢复演练是什么时候
- 恢复耗时是否有最新记录
- 谁拥有恢复权限
- 允许的数据丢失窗口是多少
日志与可观测性
- 是否存在统一的请求 ID / Trace ID
- 关键业务字段是否可检索
- 日志是否包含必要上下文而不过载
- 是否排除了敏感信息
数据变更
- 每个迁移是否都有对应回滚脚本
- 回滚是否实际执行验证过
- 删除或修改前是否保留安全副本
外部依赖
- 是否明确限流、超时、熔断和降级策略
- 是否测试过依赖不可用时的行为
- 用户通知和支持联系人是否明确
高风险操作
- 是否要求双人复核
- 是否限制了过高权限的长期持有
- 夜间或周末操作是否有升级和协助机制
临时方案治理
- 临时实现是否满足最低工程质量
- 是否记录了明确边界和已知限制
- 是否能在无人记忆背景的情况下继续维护
关于
关注我获取更多资讯