用部署记录测量技术债利息并决定何时偿还
目标
根据部署和工作记录,计算 DORA 指标和各模块的利息(计划外工作时间),并用重构候选项的回收期、净节省、延迟成本,按规则决定“现在要不要还”。
为什么重要
是还技术债务,还是发布功能,通常是靠声音大小来决定的。如果把利息、本金和被推迟的功能的价值放在同一个单位上,这场争论就变成了计算。没有衡量过的利息,看起来像是不存在,直到某一天吞掉整个团队的时间。
材料
/opt/fixtures/founder/debt/deploys.csv—deploy_id,service,committed_at,deployed_at,failed,recovered_at,unplanned(时间为 UTC,12 周)/opt/fixtures/founder/debt/worklog.csv—week,module,planned_hours,unplanned_hours(第 1–12 周)/opt/fixtures/founder/debt/options.json—team_hours_per_week, hourly_cost_krw, horizon_weeks, refactor{모듈: {cost_hours, interest_reduction}}, feature{name, value_per_week_krw}(占位符为模块名)
定义(名称沿用 dora.dev)
- 部署频率 = 服务的部署数 ÷ 12。变更前置时间 = deployed_at − committed_at(小时)。变更失败率 = failed=1 ÷ 全部。失败部署恢复时间 = failed=1 的部署的 recovered_at − deployed_at(小时)的中位数。部署返工率 = unplanned=1 ÷ 全部。
- 利息 = 模块的 unplanned_hours。
mean(12 周平均)、first4(第 1–4 周平均)、last4(第 9–12 周平均)。 - 节省 = last4 × interest_reduction。回收期 = cost_hours ÷ 节省。期间净节省 = (节省 × horizon_weeks − cost_hours) × hourly_cost_krw。延迟成本 = cost_hours ÷ team_hours_per_week × value_per_week_krw。
- 决策:如果回收期最短的候选项的净节省 > 延迟成本,则为该模块名称,否则为
"feature_first"。 - 舍入:小时、周保留到小数点后第二位,比率第四位,韩元取整数。
步骤
- 在
/root/founder/debt/dora.py中创建frequency(fix, service)(每周部署数,小数)。 - 在
dora.py中加入lead_time(fix, service)→{"median_h": x, "mean_h": y}。 - 在
dora.py中加入change_fail_rate(fix, service)。 - 在
dora.py中加入recovery_median(fix, service)(小时)。 - 在
/root/founder/debt/dora.json中,为每个服务写入frequency_per_week, lead_time_median_h, lead_time_mean_h, change_fail_rate, recovery_median_h, rework_rate。 - 在
/root/founder/debt/interest.json中,为每个模块写入mean, first4, last4, rising(last4 > first4)。 - 在
/root/founder/debt/plan.json中,为每个重构候选项写入saved_per_week, payback_weeks, net_saved_krw, delay_cost_krw。 - 在
/root/founder/debt/decision.json中写入decision(定义中的规则)、worst_service(变更失败率最高的服务)、fastest_payback(回收期最短的候选项)。
参考
statistics.median、statistics.mean、datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ")- 常见错误:前置时间只看平均数,把变更失败率的分母定为事故数或天数,用平均数来计算恢复时间,用 12 周平均值去判断在增加的利息,漏掉延迟成本。
部署频率
在 /root/founder/debt/dora.py 中创建 frequency(fix, service)——该服务的部署数 ÷ 12。
把 deploys.csv 按服务筛选出的行数,除以 12 周。
变更前置时间——中位数和平均数
在 dora.py 中加入 lead_time(fix, service) → {median_h, mean_h}。
把每次部署的 (deployed_at − committed_at) 换算成小时,建立列表,使用 statistics 的 median 和 mean。
变更失败率——分母是部署
在 dora.py 中加入 change_fail_rate(fix, service)。
用部署之后需要立即介入的部署(failed=1)的数量,除以该服务的全部部署数。
失败部署恢复时间
在 dora.py 中加入 recovery_median(fix, service)(只含失败的部署,小时,中位数)。
是 failed=1 的部署的 (recovered_at − deployed_at) 的中位数。没有失败则为 None。
各服务的 DORA 一页纸
在 /root/founder/debt/dora.json 中,为每个服务写入 frequency_per_week、lead_time_median_h、lead_time_mean_h、change_fail_rate、recovery_median_h、rework_rate。
部署返工率 = unplanned=1 ÷ 全部部署。小时保留到小数点后第二位,比率保留到第四位。
各模块的利息与趋势
在 /root/founder/debt/interest.json 中,为每个模块写入 mean、first4、last4、rising。
按 week 顺序排序后,分别求前 4 周和后 4 周的平均数。
回收期、净节省、延迟成本
在 /root/founder/debt/plan.json 中,为每个重构候选项写入 saved_per_week、payback_weeks、net_saved_krw、delay_cost_krw。
节省是最近的利息(last4)× interest_reduction。延迟成本是重构占用团队时间的周数 × 功能的每周价值。
按规则作出决定
在 /root/founder/debt/decision.json 中写入 decision、worst_service、fastest_payback。
只看回收期最短的候选项,如果它的净节省大于延迟成本,则为该模块名称,否则为 feature_first。