技术债清单列了 80 条,一年下来一条没还。原因几乎从不是"团队不重视",而是这 80 条没有价格:没有人知道还第 37 条要花几天、能换回什么,于是它在需求排期里必然输给任何一个有明确交付物的需求。想让债被还,先让债能报价。
1. 清单不可执行,是因为缺三样东西
| 缺什么 | 表现 | 补法 |
|---|---|---|
| 影响面 | 条目写着"代码耦合严重",不知道影响谁 | 写成"改动 A 模块会连带改 B、C 两处" |
| 触发条件 | 不知道什么时候会咬人 | 写成"新增字段时必须同步改 3 个 DTO" |
| 代价 | 不知道还它要几天 | 写成"拆出接口约 2 人日,含回归 1 人日" |
id: billing-rule-coupling
condition: 新增或修改价格规则时 # 何时咬人
impact: 需同步改 DTO、SQL、缓存键三处 # 影响谁、改哪里
cost: 同类改动近三次平均 9 天
target: 拆出规则引擎接口后预计 3 天
payback: 第 4 次同类需求收回(8 人日)
expires: 该模块下线即失效
一条债只有写成"在什么条件下、由谁承担什么额外成本",才进入可排期的状态。其余都只是情绪记录。expires 那一行尤其重要:没有失效条件的条目,会永久占据清单。
2. 三种债,处置方式完全不同
| 类型 | 来源 | 特征 | 处置 |
|---|---|---|---|
| 有意的权衡 | 当时为赶交付做的选择 | 有文档、有触发条件、当事人还在 | 定期复核条件是否仍成立 |
| 无意的腐化 | 长期叠加的补丁、复制粘贴 | 没人说得清为什么这么写 | 需要先止损,再谈还 |
| 环境变化的过时 | 依赖停止维护、平台版本演进 | 代码没变,外部世界变了 | 属于升级项,按风险窗口处理 |
最常见的误判是把第二类当第一类处理——"当初是有意这么设计的",于是它永远留在清单上。判据很硬:能不能指出当初的取舍条件。指不出来,就是腐化。
3. "债"这个隐喻的误导
| 隐喻暗示 | 现实 | 后果 |
|---|---|---|
| 有本金有固定利息 | 利息随使用方式剧烈变化 | 用"总利息"排序会排错 |
| 必须还清 | 很多是永久性设计选择 | 追求"清零"会浪费预算 |
| 越早还越省 | 系统可能半年后下线 | 在即将废弃的模块上还债是净损失 |
| 债主是外部 | 债主是未来的自己和同事 | 谈判对象被想错 |
"技术债"更适合当作沟通词而不是会计模型。它的价值在于让非技术方理解"现在省的时间将来要还",一旦拿它做精确排序,就会算出错误答案。
4. 定价的四个维度
| 维度 | 怎么估 | 谁受影响 | 权重信号 |
|---|---|---|---|
| 改动成本增量 | 对比同类需求的近几次工时 | 开发 | 近半年工时是否单调上升 |
| 故障概率增量 | 该模块近一年事故与回滚次数 | 值班、用户 | 是否集中在同一处 |
| 人员流动影响 | 能独立改动该模块的人数 | 新人、交接 | 是否只剩一人 |
| 机会成本 | 还它挤掉了哪个需求 | 业务 | 被挤掉的需求价值 |
四个维度里只有第一个能相对客观地估。其余三个靠观察,不靠打分。任何把四维加权成一个总分的方法,最后都会得到一堆看起来精确却没有共识的数字。
5. 利息的观测方式
| 信号 | 怎么读 | 方向 |
|---|---|---|
| 同类需求工时 | 取近 5 次的估时与实际工时差 | 差值扩大即利息在涨 |
| 回归缺陷率 | 该模块改动后的线上缺陷占比 | 高于全局均值说明耦合在扩散 |
| 上线频率 | 该模块的发布间隔 | 间隔变长通常因改动风险高 |
| 变更前置时间 | 提交到上线的时长 | 卡在评审与手工验证上 |
| 回滚率 | 回滚次数 / 发布次数 | 上升说明改动不可预测 |
# 从版本库直接取利息信号:某目录近半年的提交数与改动文件数
git log --since='6 months ago' --pretty=format: --name-only -- src/billing \
| sort | uniq -c | sort -rn | head -20
改动最频繁的文件,通常就是利息最高的地方。这个排序比任何"代码坏味"扫描都更贴近真实成本。
6. 什么时候必须还
| 判据 | 具体表现 | 不还的后果 |
|---|---|---|
| 阻塞新需求 | 三个需求都因为同一处结构无法落地 | 需求方开始绕过系统 |
| 故障频发 | 同一模块季度内多次事故 | 值班成本超过还债成本 |
| 无人能改 | 仅一人能改,且其在休假/离职 | 单点风险变成现实 |
| 安全或合规硬约束 | 审计项无法满足 | 业务本身不可持续 |
7. 什么时候不该还
| 判据 | 具体表现 | 理由 |
|---|---|---|
| 用户不可见且稳定 | 三年没改过的报表模块 | 利息已停止累计 |
| 即将下线 | 有明确下线时间的系统 | 投入无法回收 |
| 改动风险大于收益 | 重构会触碰核心交易路径 | 引入的故障风险高于省下的工时 |
| 团队即将解散 | 交接期只剩两个月 | 无人承接改动的长期维护 |
8. 三种还债规模
| 规模 | 典型投入 | 成本可预期性 | 适合 |
|---|---|---|---|
| 顺手改 | 半天以内,混在需求里 | 高 | 命名、日志、明显的重复 |
| 专项 | 数天到两周,独立排期 | 中,常有意外 | 抽出接口、替换依赖 |
| 重构 | 数周到数月 | 低,容易扩散 | 边界错位、分层失效 |
规模越大,成本可预期性越差,这就决定了排期方式:顺手改不需要排期,专项要有独立条目,重构必须先做探针(先改一小块验证假设)。
9. 重构与重写的界限
判断顺序(自上而下,命中即停):
1. 现有系统能否在不改变外部契约的前提下拆分?
能 → 重构,不做重写
2. 是否有覆盖核心路径的自动化回归?
没有 → 先补回归,再谈重构;重写更不该提
3. 需求是否要求现有模型无法表达的能力?
否 → 重写的唯一动机就只剩"看不懂",这不是理由
4. 重写期间旧系统能否继续服务?
不能 → 双写双跑的成本会把收益吃光
5. 团队是否做过同等规模的迁移?
没有 → 先用 strangler 模式切一条支路验证
重写通常不该选,原因不在技术,在并行期成本:新旧两套同时维护、数据双写、行为对齐,这段期间的产出接近于零,而这段时间往往正好是业务最需要交付的时候。
10. 排期的两种方式
| 方式 | 做法 | 优点 | 风险 |
|---|---|---|---|
| 跟需求绑定 | 每个需求预留固定比例用于改善 | 一定有进展,不排期也能动 | 比例被压缩时最先牺牲 |
| 独立排期 | 单独立项,明确交付物 | 可做结构性改动 | 与需求争夺同一批人 |
| 触发式 | 达到阈值(工时翻倍、二次事故)即启动 | 有客观依据,好解释 | 阈值需要事先约定 |
实践里最稳的是"触发式 + 跟需求绑定":日常靠绑定小步推进,触发条件命中时升级为独立排期。纯独立排期在需求压力下几乎必然被砍。
11. 度量:不要打分,问"下一次改动要多久"
可用的问题(可回答、可比较):
- 给 X 加一个字段,从提交到上线要几天?上个月是几天?
- 新人第一次改 X,需要几个人陪着看代码?
- X 的一次发布需要几步手工操作?
不可用的问题:
- X 的技术债评分是多少?(口径不可复现)
- X 的坏味数量是多少?(与交付无关)
打分工具的问题不在不准,在不可比:两个季度用不同口径算出的分数无法比较,于是没人相信它。工时与人数是所有人都认得的单位。
12. 沟通:用业务语言报价
差的说法:这块代码耦合太重,要重构一下,大概两周。
好的说法:现在改一个价格规则要动 4 个文件、经过 3 次回归,
最近三次平均 9 天。拆开之后同类改动预计 3 天。
投入 8 人日,第 4 次同类需求就能收回。
报价必须包含三件事:当前成本、目标成本、回收点。缺回收点,讨论就会变成"要不要讲代码质量";有了回收点,讨论变成"这个投入划不划算",这才是可决策的问题。
13. 排错:还债项目本身的失败迹象
| 现象 | 原因 | 处理 |
|---|---|---|
| 专项做了两个月还没合并 | 范围在过程中扩大 | 冻结范围,先合并可用的部分 |
| 重构后缺陷率上升 | 缺少对照的回归覆盖 | 补特征测试,缩小单次改动面 |
| 还完一轮清单没变短 | 只还了容易的,难的原地不动 | 按利息排序,不按难度排序 |
| 业务方不认账 | 报价里没有回收点 | 用最近一次真实需求做例子 |
| 同一处反复列入清单 | 只改了症状没改约束 | 找到产生该结构的流程并改流程 |
最后一条最值得注意:同一处债被还了两次,说明产生它的流程还在——评审缺位、模板没更新、复制粘贴的源头没删。
14. 边界
| 场景 | 更合适的做法 | 理由 |
|---|---|---|
| 一次性项目 | 不还,记录即可 | 没有下一次改动 |
| 有明确下线时间的系统 | 只做止损,不做结构改造 | 收益无法回收 |
| 团队交接期 | 只做可交接的文档与测试 | 结构性改动无人承接 |
| 核心交易路径且无回归 | 先补测试,不动结构 | 风险高于收益 |
| 债务集中在第三方依赖 | 按升级窗口处理,不列技术债 | 性质是版本演进 |
15. 自测
- 一条债要写成什么样才进入可排期状态?
- 怎么判断一条债是"有意的权衡"还是"无意的腐化"?
- "技术债"作为会计模型会在哪三处算错?
- 四个定价维度里,哪一个能相对客观地估?
- 重构与重写的分界线,第一条判据是什么?
- 向业务方报价时必须包含哪三件事?