系统性地识别、记录、评估和偿还技术债务,避免债务积累到"无法维护只能重写"的地步,在开发速度和代码质量之间保持平衡。
- 开发中做了妥协/临时方案时(记录债务)
- 每个迭代规划时(安排偿还)
- 代码审查发现历史遗留问题时
- 新功能开发受阻于现有代码结构时
- 定期技术债务盘点(每月/每季度)
| 类型 | 说明 | 示例 |
|---|---|---|
| 有意债务 | 明知不完美,为赶进度刻意为之 | "先硬编码,下期做配置化" |
| 无意债务 | 当时不知道更好方案 | 用了不合适的库/模式 |
| 环境变化 | 原来合理,现在不适用了 | 用户量增长后单体架构成瓶颈 |
| 腐化债务 | 缺乏维护逐渐恶化 | 无测试覆盖、文档过时 |
识别信号:
- "这段代码没人敢动"
- "加个功能要改 5 个文件"
- "这个 Bug 修了又出现"
- "部署/测试太慢太痛苦"
- "这个依赖已经停止维护了"
记录格式:
## TD-XXX: [债务标题]
- 类型:有意 / 无意 / 环境变化 / 腐化
- 位置:[文件/模块路径]
- 描述:[当前问题是什么]
- 影响:[对开发效率/性能/稳定性的影响]
- 修复方案:[建议如何解决]
- 估算:[修复需要多少时间]
- 优先级:P0 / P1 / P2 / P3
- 记录日期:评估矩阵:影响面 × 修复成本
| 修复成本低(< 1天) | 修复成本高(> 1周) | |
|---|---|---|
| 影响面大(阻碍开发/影响用户) | 立即修(P0) | 排入近期迭代(P1) |
| 影响面小(局部/潜在风险) | 顺手修(P2) | 记录观察(P3) |
量化影响:
- 每次遇到这个问题浪费多少时间?
- 是否阻碍新功能开发?
- 是否有稳定性/安全风险?
- 影响范围(一个模块 vs 全局)?
原则:
- 每个迭代预留 20% 时间给技术债务
- 优先偿还 P0/P1(阻碍性债务)
- 小债务随手修(Boy Scout Rule:离开时比来时更干净)
- 大债务单独排期,当作"功能"来规划
偿还策略:
- 随修随还:改到相关代码时顺手改善
- 专项偿还:安排专门迭代处理大债务
- 重构偿还:结合新功能开发一起重构
- 替换偿还:用更好的方案替换旧实现
记录位置:docs/archive/<最新归档日>/tech-debt.md —— 每日滚动的唯一权威清单,随文档归档建立。
字段扩展:在第二部分记录格式基础上增加两个字段——来源归档(债务首次登记的归档日期)、状态(open 今日新增 / carried 继承昨日未偿 / closed 今日已偿)。
滚动规则:
- 归档日先读昨日清单:已偿(提交/代码验证)→
closed并注偿还提交,后续归档不再出现;未偿 →carried,仅保留 ID + 一行摘要,全文以首次登记日归档为准 - 当日新识别(TODO/FIXME/HACK、妥协方案)→
open - 归档日无新增债务也须继承昨日清单,保证"最新归档必有权威清单"
- 旧归档中的债务条目仅历史追溯,不作为工作依据
与偿还计划的衔接:滚动清单是债务的唯一来源,偿还仍按第三部分影响面×成本排期、每迭代预留 20% 时间。
- 代码审查时关注"新增债务"
- 临时方案必须写 TODO + 创建债务记录
- 保持测试覆盖率(防止腐化)
- 定期重构(不等债务堆积)
- 技术选型时考虑长期维护成本
- 文档与代码同步更新
- 技术债务有统一记录位置
- 每条债务有影响评估和修复方案
- 优先级已评估(影响面×成本)
- 每迭代预留 20% 偿还时间
- P0 债务不跨迭代
- 新增临时方案有 TODO 标记
- 每月盘点一次债务清单
- 已清偿的债务标记关闭
| 输出物 | 格式 | 存放位置 |
|---|---|---|
| 技术债务清单(滚动权威) | Markdown | docs/archive/<最新归档日>/tech-debt.md |
| 债务登记入口 | Markdown/Issue | docs/archive/<最新归档日>/tech-debt.md 或 GitHub Issues |
| 偿还计划 | 迭代 backlog | 项目管理工具 |
| 债务盘点报告 | 文档 | docs/archive/<归档日>/review-YYYY-MM-DD.md |
| 误区 | 正确做法 |
|---|---|
| 假装债务不存在 | 记录它,可视化它 |
| 想一次还清所有 | 分优先级,逐步偿还 |
| 永远不还(一直做新功能) | 预留固定比例时间 |
| 还债 = 完全重写 | 渐进式改善,小步重构 |
| 不记录"先凑合"的代码 | 妥协可以,但必须记录 |