7AA7 Blog

技术债的量化与管理:从感知到偿还

码同客 UID:88
2026-09-21 20:03
34 2 0

“这块代码该重构了”很难争取到资源,它描述的是主观感受,而排期表上每一行都是可交付的功能。技术债管理的第一步不是动手重构,而是把感受翻译成能用同一套标准比较的东西——否则偿还永远排在交付之后,直到某天以故障的形式强制偿还。

债务先要分类

不同类型的债,利息形式和偿还方式完全不同,混在一起算总账必然失真。

类型 典型表现 主要利息形式
代码债 重复逻辑、超长函数、命名混乱 每次改动耗时增加、改动诱发新问题
架构债 边界模糊、循环依赖、模块职责重叠 新需求影响面扩大、无法局部发布
测试债 覆盖率低、测试依赖真实环境、用例不稳定 回归靠人工、发布恐惧、缺陷率上升
文档债 接口无说明、部署步骤口传 新人上手慢、故障处理依赖特定个人
依赖债 库版本过期、中间件不再维护 安全风险、升级成本随时间递增

分类的意义在于选出可偿还的部分。依赖债和架构债往往需要专项投入,代码债适合在日常开发中顺手处理。把”重构整个模块”当成一般任务排进迭代成功率极低——它太大、太难验收,通常会被压缩或延期。

还有一个常被忽略的类型:无知债——当时的最优解在今天看是错的,但那是约束条件下的合理选择。它不构成过失,也不宜用”欠债”追责。区分它与草率决策是债务管理能否被团队接受的前提:当时是否有更优的已知方案、是否处在明确时间约束下。

利息怎么算

不必追求精确的货币化,但要把”不好维护”变成可比较的量。

思路一是改动耗时比。同一个模块,一个熟悉它的人完成一次典型改动要多久,与结构清晰的模块对比,差距就是利息。估算可来自团队直觉——让三个人分别估再取中位数,比单人判断可靠。

思路二是故障概率。统计过去半年到一年的缺陷并按模块归类。高频问题若集中在一两个模块,那里就是债务最重的区域,数据从缺陷管理系统提取,成本低且难以被质疑。

思路三是新人上手时间:从入职到独立完成第一个生产改动需要多久。它综合了代码可理解性、本地环境搭建难度和文档质量,是很诚实的度量。

把这些记进统一的表格,每行对应一个具体的债务项:

| 债务项              | 类型     | 利息表现                    | 量化方式              | 优先级 |
|--------------------|---------|---------------------------|---------------------|-------|
| 订单价格计算逻辑重复 | 代码债   | 每次调价要改 4 处,常漏改 1 处 | 改动 2h vs 同类 0.5h | 高     |
| 库存服务依赖订单库表  | 架构债   | 订单表变更需两个团队排期       | 涉及需求平均多 3 天    | 中     |
| 核心接口无契约测试    | 测试债   | 发布靠人工回归半天           | 每版本 0.5 人日       | 高     |

优先级列用高/中/低三档而不是百分制评分。估算精度本就有限,粗糙的分档比看起来精确的分数更耐用。

还有一条提醒:量化结果不要用于横向比较团队或个人。一旦债务数字变成评价依据,数字就会停止增长——不是因为债务减少,而是因为没人愿意记录它。

用可观测数据支撑判断

直觉会偏差,工程数据是更好的依据。把提交日志里的改动热点与缺陷系统里的缺陷密度组合起来,做一张二维图:

          高缺陷密度
             |
   [需要重点关注]  |  [稳定且核心]
  频繁改 + 常出错  |  频繁改但少出错
             |
  低频 ----------+---------- 高频
             |              改动频率
   [冷代码]       |  [需警惕的热点]
  少改少错        |  频繁改但暂未出错
             |
          低缺陷密度

右上角(频繁改动且经常出错)偿还优先级最高:改动多说明它承载需求,错误多说明结构撑不住当前变化率。左下角的冷代码即使难看也不值得投入——它不在变化路径上,重构不产生收益。

数据提取很简单,版本控制的提交记录就够了:

# 统计近半年改动最频繁的文件
git log --since=6.months --name-only --pretty=format: 
  | sort | uniq -c | sort -rn | head -30

另一个有价值的信号是改动的影响面:一次需求平均改多少个文件、跨多少个服务。这个数随时间上升通常说明架构边界在退化——原本一个模块能完成的事,现在要穿透好几层。

这些数据不只是用来找债务,也能验证偿还效果:重构后同一区域的改动耗时是否下降、缺陷密度是否改善。没有事后验证,”重构完成了”只是说法。

偿还策略与配比

常见三种方式各有位置。

顺手还:每次改到某段代码就把它改好一点。成本最低,积少成多,缺点是不解决结构性问题——顺手改不动循环依赖。

专项还:针对具体债务立项,明确范围与验收标准,适合架构债和依赖债,但要专门争取资源。专项成本要设上限:给明确工期,到期必须能上线,否则容易变成无限期的重构工程。

随重构还:改某个功能时顺带调整结构。风险是功能改动与重构混在一起,出问题难以回滚。稳妥做法分两步提交——先做不影响行为的整理(提取函数、去重复、补测试),再做行为改动,分别上线观察。

配比没有标准答案,常见实践是给迭代预留一部分容量(例如 15%~20%)用于偿还。低于这个比例,偿还速度跟不上产生速度;长期需要更高比例,说明债务已到需要专项治理的程度。

优先级排序的标准只有一条:利息。先还利息最高的,而不是最刺眼的。所有人都抱怨的命名问题若位于不常改动的模块,优先级应低于只有两人知晓、但处在改动热点上的结构缺陷。

怎么向非技术管理者说

用”代码质量”这个词沟通几乎必然失败,它没有对应的业务影响。有效的翻译是把债务换成对方关心的量:交付速度与故障成本。

用交付速度说话。把同类型需求的交付周期做成趋势:三个月前类似需求平均五天,现在要八天。这个趋势本身就是债务的证据,而且能换算成人力成本——多出的三天乘以团队人日成本,就是一个可理解的金额。

用故障成本说话。列出近期几次故障的止损时长与影响量级,标注其中多少次与已知债务项直接相关。不必精确到”这段坏代码损失了多少”,只要能说明这两次故障的根因都在同一处结构上,就足以支撑投入的判断。

用机会成本说话。某个本可快速上线的新能力,因要绕过现有结构需要额外多久。

表达时的实用技巧是不问”要不要重构”,而是给选项:A 方案直接做新功能,交付周期 X,但同类问题三个月后会再出现一次;B 方案先花 Y 天处理结构,之后这类需求周期降到 Z。决策质量取决于选项质量,而不是取决于对方懂不懂技术。

还有一点要诚实:偿还债务的收益多数是避免未来的损失,而不是带来可见的增长。承认这一点比夸大收益更容易建立长期信任。也要承认另一面——不是所有债都要还,运行稳定且不在改动路径上的系统,维持现状往往是合理选择。判断要不要还,看的是利息是否真的在持续产生,而不是它是否符合当下认同的写法。

1kViews
34Likes
2Comments
2Participants
8 minread time

Comments (2)

  • 码同客 · 2026-09-21 22:18

    我们公司禁用了这个方案,说是安全问题,看完大概知道原因了。

    Reply
  • Liora · 2026-09-22 07:30

    收藏夹又多了个吃灰的(不,这次真会看)。

    Reply