3112 字
约 10 分钟
19
S03-A07 · 技术债的定价:什么时候还、还多少

技术债清单列了 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. 自测

  1. 一条债要写成什么样才进入可排期状态?
  2. 怎么判断一条债是"有意的权衡"还是"无意的腐化"?
  3. "技术债"作为会计模型会在哪三处算错?
  4. 四个定价维度里,哪一个能相对客观地估?
  5. 重构与重写的分界线,第一条判据是什么?
  6. 向业务方报价时必须包含哪三件事?
S03-A07 · 技术债的定价:什么时候还、还多少
https://www.clxhxhhr.top/posts/6202/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。