Skip to content

如何解读结果

怎么读这些数字——以及同样重要的,该忽略什么

hotspot 分数

score = rank(改动频率) × rank(复杂度)

取值 0 到 1。只有两项都高的文件才会排到前面。

情况分数含义
改动频繁 × 复杂投入产出最高。从这里开始。
复杂但没人碰0修了没有回报。放着不管。
改动频繁但简单它没有给你带来麻烦。

从未改动过的文件永远是 0

这是刻意的:它阻止工具去推荐那些复杂但稳定的代码。清理一个能跑、又没人碰的东西,什么也换不来。

这是排名,不是绝对值

分数是在本仓库内的相对位置。「分数 0.9」不代表「危险」,只代表「在这个仓库里排前 10%」。它不能跨仓库比较。

测试文件排在前面是正常的

在真实数据上,测试文件会排得很高。这不是 bug。维护测试是实打实的成本,一个不断损坏又不断被修的测试,确实是问题。

如果你只想看生产代码,请对结果做过滤。

耦合度

degree(A,B) = 2·|A∩B| / (|A|+|B|)

耦合度低但共变更次数高,依然重要。 两个都很活跃的包,分母——各自的总改动次数——很大,会把耦合度压下去。

配对耦合度共变更解读
A ↔ B26%90低于 30% 的默认阈值,但共享 90 次提交这件事不能忽略

如果默认阈值下什么都没出现,就把它调低。

bash
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 分数旁边都会打印只用代码行数会得到的分数

文件healthLoC 基线解读
env-guard.ts4.910.0短小但密集。 数行数会错过它。
big-but-simple.ts8.08.0没有差距——这个 health 只是在说「文件很大」

差距很小时,那个 health 分数就是文件长度换了身衣服。不要从中读出比「这个文件很大」更多的东西。

每个阈值都是假设

阈值默认值来源
圈复杂度告警10McCabe / ESLint
认知复杂度告警15SonarSource
最小耦合度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)与锁文件默认不在分析范围内。

接下来

基于 MIT 许可发布