它真的有用吗
「先修 hotspot 回报最大」是一个假设。这一页是试图推翻它的记录,对象是任何人都能核对的公开仓库。
条件
8 个成熟的 TypeScript 仓库,共 202,604 次提交。
| 仓库 | 历史 | 文件数 | 测试期内被修复的文件 |
|---|---|---|---|
| vuejs/core | 2018– | 478 | 185 |
| jestjs/jest | 2014– | 931 | 111 |
| vitejs/vite | 2020– | 560 | 220 |
| microsoft/playwright | 2019– | 1398 | 657 |
| nrwl/nx | 2017– | 4712 | 2200 |
| angular/angular | 2014– | 5604 | 371 |
| nestjs/nest | 2017– | 1676 | 21(已排除) |
| storybookjs/storybook | 2016– | 3986 | 22(已排除) |
方法:用分割日之前 12 个月(dowsing 的默认窗口)训练,用之后 12 个月评估。这个分割很关键:如果用同一时间段测量,修复本身会进入 churn,指标就只是在预测自己的输入。
缺陷标签要求 conventional commit 的 fix: 前缀并且引用 issue。nest 与 storybook 被排除在评分之外——两者都使用 squash merge 且未遵守约定,严格标签下修复样本不足 30 个。这是局限,不是结论。
输在哪里:整体排序
为了排除曝光量的混杂,把总体限定为「测试期内被改动过的文件」,按 AUC 排序:
| 仓库 | hotspot | churn | LoC |
|---|---|---|---|
| vuejs/core | 0.641 | 0.589 | 0.685 |
| jestjs/jest | 0.605 | 0.594 | 0.625 |
| vitejs/vite | 0.679 | 0.648 | 0.719 |
| microsoft/playwright | 0.637 | 0.636 | 0.722 |
| nrwl/nx | 0.641 | 0.629 | 0.684 |
| angular/angular | 0.700 | 0.674 | 0.724 |
8 个仓库中有 7 个,单独的代码行数就胜过了 hotspot。 这在缺陷预测研究中并不意外:文件大小反复被报告为一条难以击败的基线。把训练窗口拉长到 20 年没有改变结果,放宽标签也没有。
也就是说,如果你只看整仓排序,wc -l 是一个有竞争力的工具,安装 dowsing 的理由并不充分。
赢在哪里:排行榜顶部
但 AUC 衡量的是整个排序,而没有人这样使用工具。真实的用法是「看前 10 到 20 个」。各仓库的基准率差异极大(0.4% 到 41%),所以用提升倍数(相对基准率的倍数)来对齐。
| 仓库 | 基准率 | hotspot | churn | LoC |
|---|---|---|---|---|
| vuejs/core | 38.7% | 2.3x | 1.9x | 2.2x |
| jestjs/jest | 11.9% | 4.2x | 4.6x | 4.2x |
| vitejs/vite | 37.1% | 2.6x | 2.4x | 2.7x |
| microsoft/playwright | 37.8% | 2.6x | 2.6x | 2.4x |
| nrwl/nx | 41.2% | 2.3x | 2.4x | 1.2x |
| angular/angular | 5.7% | 10.4x | 10.4x | 5.2x |
| 平均 | 4.1x | 4.1x | 3.0x |
在前 20 名里,hotspot 明显胜过 LoC(4.1x 对 3.0x)。差距最大的是 nx(2.3x 对 1.2x——LoC 接近随机)和 angular(10.4x 对 5.2x)。文件大小会把「很大但很稳定」的文件顶上来,乘以变更频率后它们就掉下去了。
相关性在整体,价值在顶部。 所以这里正确的评价指标是 precision@K,而不是 AUC。
复杂度到底贡献了什么
在这份数据上,churn 单独与 hotspot 打平(4.1x 对 4.1x),乘以复杂度似乎毫无作用——这等于否定了核心公式本身。
原因出在正例的定义。改变「什么算缺陷」之后:
| 正例定义 | hotspot | churn | LoC |
|---|---|---|---|
| 至少被修复 1 次 | 4.1x | 4.1x | 3.0x |
| 被修复 3 次以上(慢性) | 14.4x | 12.2x | 7.7x |
| 被修复 5 次以上 | 16.3x | 15.5x | 8.4x |
对于反复被修复的文件,hotspot 在 5 个仓库中有 4 个胜过 churn(vue +2.0 / vite +1.4 / playwright +0.4 / angular +8.5,唯一落后的是 nx 的 −1.0)。
解读是:复杂度预测的不是「文件会不会坏」,而是「一次修复够不够」。 简单的文件修一次就完了,复杂的文件会在同一个地方反复修。这是关于 churn × complexity 用途的一个具体且可证伪的主张,也正是这个工具站得住脚的场景。
使用时这意味着什么
- 读顶部,而不是读排序。 证据支持的是「前 10–20 个值得你关注」,而不是「第 400 名比第 380 名更安全」。
- 目标是慢性文件。 如果一个 hotspot 被反复修复而且还在被修,那里就是复杂度正在让你付出代价的地方。只坏过一次的文件不构成使用这个工具的理由。
- 整仓相关性不是安装它的理由。 那个用 LoC 基本就能得到。LoC 做不到的是找出反复回来的文件,以及没有依赖关系却总是一起变更的包。
这次验证的局限
- 参与评分的是 6 个仓库,全部是流行的开源库与框架。应用型代码库和私有 monorepo 的表现可能不同。
fix:+ issue 引用只是「这里曾有缺陷」的代理指标。它会漏掉未遵守约定的修复,也会把并非缺陷的修复计入。- 每个仓库只有一个分割日,没有跨多个时间窗口的交叉验证。
- 这里完全没有测量按排名行动之后是否真的有改善。那需要测量变更前后,而这正是
dowsing diff的职责。
dowsing 的所有阈值与权重都是假设,并且明确标注为假设。如果你复现后得到不同的结论,那是有价值的结果——请提 issue。