技术债务
核心问题:什么是技术债务?如何识别、管理和偿还技术债务?
1. 技术债务的类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 故意债务 | 明知不好但为了赶工期 | 跳过单元测试、硬编码配置 |
| 无意债务 | 不知道更好的方案 | 不合理的架构设计、过时的依赖 |
| 环境债务 | 外部环境变化导致 | 框架版本过旧、依赖库停止维护 |
| 代码债务 | 代码层面的问题 | 重复代码、过长方法、缺少文档 |
这四类并非同一维度的划分:「故意 / 无意」描述债务产生的意图,「环境」描述债务产生的背景,「代码」描述债务涉及的对象。一项债务可以同时归属多类,分类服务于识别和治理,而不是要求每项债务只能归入一类。
2. 技术债务识别
识别信号见下方注释,其中的数字是示例阈值而非通用标准。代码层面的坏味道可对照 代码坏味道 排查,架构层面的债务评估与治理决策可沉淀为 ADR,持续治理配合 Code Review。
java
// 识别信号(示例阈值,只能触发调查,不能独立证明技术债务存在)
// 1. 代码层面
// - SonarQube 报告的 Bug 和坏味道
// - 测试覆盖率低于 60%
// - 构建时间超过 10 分钟
//
// 2. 架构层面
// - 添加新功能需要修改多处代码
// - 模块间耦合严重
// - 缺少自动化测试
//
// 3. 团队层面
// - 新人上手需要超过 2 周
// - 频繁出现"改了这里那里坏了"
// - 团队不敢重构某些模块
// 技术债务登记模板
/**
* 债务编号:TD-001
* 债务名称:订单模块缺少单元测试
* 债务类型:代码债务
* 严重程度:高
* 影响范围:订单模块所有功能
* 产生原因:赶工期跳过了测试
* 偿还成本:5 人天
* 风险:缺少测试,发布存在回归风险
* 优先级:P1
* 计划偿还时间:下个 Sprint
*/3. 技术债务管理策略
一项债务该随功能顺手处理、单独立项还是暂时保留,取决于改动范围、影响面和风险:
- 随功能开发处理:改动范围小、与当前代码路径直接相关,不会明显扩大回归风险(对应策略 2、4)。
- 单独规划治理:债务影响多个模块、需要迁移或协调,风险已超出可接受范围(对应策略 3)。
- 暂时保留:清偿成本高于继续承担的成本且风险可控,记录责任人、复查条件或触发事件。
java
// 策略 1:每次 Sprint 预留约 20% 的容量处理技术债务(示例比例,按团队历史数据和债务风险调整)
// Sprint 容量:20 故事点
// 功能开发:16 点(80%)
// 技术债务:4 点(20%)
// 策略 2:Boy Scout Rule(离开时比来时更干净)
// 每次修改代码时,顺手改善周围的代码
// 适合改动范围小、与当前修改直接相关的改善,不能替代系统性债务的专门规划
// 策略 3:技术债务 Sprint
// 每 4-5 个功能 Sprint 后,安排一个专门的技术 Sprint(示例节奏,按团队交付节奏调整)
// 集中处理跨模块重构、依赖升级等需要专门协调的债务
// 策略 4:重构与功能开发结合
// 在开发新功能时,顺便重构相关模块
// 避免"等有时间再重构"(永远不会有时间)4. 技术债务的 ROI 分析
下面的计算是简化模型,假设回归 Bug 由缺少测试导致,且修复成本能通过补测试全额省回,实际收益会打折扣。
java
// 偿还技术债务的收益计算(简化模型)
// 假设:订单模块缺少测试,每次发布平均有 2 个回归 Bug
// 每个 Bug 修复成本:2 小时(定位 + 修复 + 测试)
// 每月发布 4 次,每月损失:2 × 2 × 4 = 16 小时
// 偿还成本:5 人天 = 40 小时
// 回本周期:40 / 16 = 2.5 个月
// 决策:回本周期短只是支持偿还的一个因素,不能单独决定是否立即偿还
// 还需结合生产风险、团队约束和替代方案综合判断技术债务的核心:技术债务不是坏事,就像金融债务一样,适度的债务可以帮助你快速发展。关键是"有意识地借债,有计划地偿还"。判断一项债务是否该治理,看的是风险、清偿成本和持续承担成本,而不是它当初是否有意识地产生;有意识引入却没有偿还计划的债务,和无意识积累的债务,一样需要治理。