如何解读结果
怎么读这些数字——以及同样重要的,该忽略什么。
hotspot 分数
score = rank(改动频率) × rank(复杂度)取值 0 到 1。只有两项都高的文件才会排到前面。
| 情况 | 分数 | 含义 |
|---|---|---|
| 改动频繁 × 复杂 | 高 | 投入产出最高。从这里开始。 |
| 复杂但没人碰 | 0 | 修了没有回报。放着不管。 |
| 改动频繁但简单 | 低 | 它没有给你带来麻烦。 |
从未改动过的文件永远是 0
这是刻意的:它阻止工具去推荐那些复杂但稳定的代码。清理一个能跑、又没人碰的东西,什么也换不来。
这是排名,不是绝对值
分数是在本仓库内的相对位置。「分数 0.9」不代表「危险」,只代表「在这个仓库里排前 10%」。它不能跨仓库比较。
测试文件排在前面是正常的
在真实数据上,测试文件会排得很高。这不是 bug。维护测试是实打实的成本,一个不断损坏又不断被修的测试,确实是问题。
如果你只想看生产代码,请对结果做过滤。
耦合度
degree(A,B) = 2·|A∩B| / (|A|+|B|)耦合度低但共变更次数高,依然重要。 两个都很活跃的包,分母——各自的总改动次数——很大,会把耦合度压下去。
| 配对 | 耦合度 | 共变更 | 解读 |
|---|---|---|---|
| A ↔ B | 26% | 90 | 低于 30% 的默认阈值,但共享 90 次提交这件事不能忽略 |
如果默认阈值下什么都没出现,就把它调低。
dowsing coupling --hidden --min-degree 15隐藏耦合有两副面孔
kind: "hidden" 只意味着「它们一起改动,且依赖图里没有边」。这是不是问题,完全取决于背后是什么。 两个真实例子。
确实有问题:重复代码
@acme/admin-web ↔ @acme/customer-web 33%, 91 次
src/lib/server-api.ts ↔ src/lib/server-api.ts 13
src/app/(authed)/page.tsx ↔ 同名路径 15两边有同名文件,同一处修改被手工复制。这是抽取到共享包的候选。
不是问题:刻意的约定
@acme/api-contract ↔ @acme/audit-event-names 25%, 49 次
openapi.json ↔ audit-event-names/src/index.ts 34这是「新增 API 时同时新增审计事件名」的约定——设计上完全合理。无需修改。
一定要看背后是什么
不要在没有运行 dowsing why <a> <b> 的情况下就断定「它们耦合了,所以要修」。在数字上,上面两个例子是同一种「隐藏耦合」。
Code Health 与 LoC 基线
每个 health 分数旁边都会打印只用代码行数会得到的分数。
| 文件 | health | LoC 基线 | 解读 |
|---|---|---|---|
env-guard.ts | 4.9 | 10.0 | 短小但密集。 数行数会错过它。 |
big-but-simple.ts | 8.0 | 8.0 | 没有差距——这个 health 只是在说「文件很大」 |
差距很小时,那个 health 分数就是文件长度换了身衣服。不要从中读出比「这个文件很大」更多的东西。
每个阈值都是假设
| 阈值 | 默认值 | 来源 |
|---|---|---|
| 圈复杂度告警 | 10 | McCabe / ESLint |
| 认知复杂度告警 | 15 | SonarSource |
| 最小耦合度 | 30% | Code Maat |
| minor contributor | < 5% | Bird et al. |
每一个都有文献依据,但没有一个保证适合你的仓库。它们全都可配置。先测量现状,再校准。出处见为什么是这些指标。
bus factor 说的是风险,不是人
bus factor 为 1 并不意味着「这个人是瓶颈」。它意味着这个包的知识集中在一个人身上,他一旦离开,包就无法维护。
不要用它评价个人
用这些数字给开发者排名是明确不建议的。这是一个让团队理解结构性风险的指标。Tornhill 也给出同样的警告。
这个计算还依赖于 .mailmap 合并身份、以及 bot 提交被排除。如果一个人用多个邮箱提交,dowsing 会高估 bus factor——包看起来比实际更安全。
被排除了什么
每次运行都会报告它剔除了什么。
excluded: 6 merge, 0 bot, 1 format-only, 0 ignore-revs (51 large changesets flagged)| 类型 | 原因 |
|---|---|
| 合并提交 | 它们本身不携带修改 |
| bot 提交 | dependabot 之类会扭曲 ownership |
| 纯格式化 | 只改空白的提交会撑大 churn |
| ignore-revs | 遵循 .git-blame-ignore-revs |
| 大 changeset(> 50 文件) | 纠缠提交会伪造耦合——只从耦合中排除,churn 中仍保留 |
生成代码(generated/、*.gen.ts)与锁文件默认不在分析范围内。