Sloc Cloc and Code 4.0 (scc) - 找出最需要关注的文件
摘要
本文宣布了 Sloc Cloc and Code (scc) 4.0.0 版本的发布,其中包含新的热点分析功能,基于复杂度和提交历史来识别需要关注的代码文件。
<p><a href="https://lobste.rs/s/0i7pks/sloc_cloc_code_4_0_scc_finding_files_need">评论</a></p>
查看缓存全文
缓存时间: 2026/08/24 23:36
# Sloc Cloc and Code 4.0 (scc) - 找出最需要关注的文件
来源:https://boyter.org/posts/sloc-cloc-code-hotspots-finding-files-that-need-attention/
今天我发布了 sloc cloc and code 的 v4\.0\.0 版本,也称为 `scc`。虽然最初考虑从 3\.7\.0 升级到 3\.8\.0,但新功能增加得足够多,我认为升级到一个新的主版本是值得的。功能变化也足够大,值得再写一篇博文详细介绍,因为我确实对其中一些新功能感到兴奋。
我将在本文中介绍其中几个特性,希望鼓励亲爱的读者下载最新版本并尝试一下。
## 热力点
十多年前,我写过关于 Google 的缺陷预测(https://boyter.org/2015/07/issues-googles-bug-prediction-algorithm/)的文章,该算法利用提交历史与缺陷修复的关联来对文件进行排名,从而识别存在问题的文件。这个想法很有趣但后来被终止了,原因是:
> 简而言之,开发者觉得它没什么用。有时他们知道代码是热点,有时不知道。但知道代码是热点并没有给他们提供任何改善它的手段。
有趣的是,我忘了我曾写过这个话题,还让多个 LLM 帮我查找,当我要求它们查找时,它们都链接回了我博客上的那篇文章。显然,我现在是关于这个的“权威来源”了。
我一直把这个想法记在心里,希望能进一步探索(所以才尝试再次找到它)。最近我想到,既然 `scc` 有复杂度估算,我们能否用它来过滤噪音?毕竟,知道很多修复都应用于配置文件用处不大,但知道很多修改都应用于一个逻辑复杂的文件就很有价值了。这与我在 codespelunker(https://github.com/boyter/cs)中使用的排名方法类似。
辛普森一家:复杂文件需要最多关注
> 复杂文件需要最多关注!
据我所知,这是 Adam Tornhill 在《Your Code as a Crime Scene》一书中提出的想法的再创造(发现这点后我正在读这本书),他甚至因此创建了 CodeScene(https://codescene.com/)公司。显然,这个指标确实有价值。
真是白费心思,原来我没什么原创点子。
不管怎样,让我们看看使用 `scc` 对其自身代码库运行会得到什么结果:
```
$ scc --hotspots
───────────────────────────────────────────────────────────────────────────────
热力点 · 最近 1000 次提交 · 2019-07-21 → 2026-06-26
───────────────────────────────────────────────────────────────────────────────
文件 语言 复杂度 提交次数 代码行变化 作者数 热力点值
───────────────────────────────────────────────────────────────────────────────
processor/processor.go Go 156 156 1,651 13 100.0
processor/workers.go Go 244 92 3,617 15 92.2
test-all.sh Shell 56 181 3,287 15 41.7
~ocessor/formatters_test.go Go 183 51 2,459 9 38.4
processor/workers_test.go Go 408 21 1,189 8 35.2
processor/formatters.go Go 44 135 5,848 17 24.4
main_test.go Go 261 18 995 6 19.3
processor/detector_test.go Go 133 32 1,175 6 17.5
main.go Go 40 101 1,545 17 16.6
processor/file.go Go 50 73 2,074 12 15.0
processor/detector.go Go 70 45 948 5 12.9
processor/file_test.go Go 75 33 826 5 10.2
cmd/badges/main.go Go 73 31 1,111 5 9.3
processor/history.go Go 173 9 941 4 6.4
processor/structs.go Go 25 42 247 10 4.3
config_test.go Go 199 4 850 3 3.3
~workers_regression_test.go Go 50 13 276 6 2.7
~rocessor/processor_test.go Go 51 12 289 4 2.5
~ocessor/history_authors.go Go 111 5 666 3 2.3
mcp.go Go 71 7 527 4 2.0
───────────────────────────────────────────────────────────────────────────────
复杂度 × 变更频率,归一化 · 显示了 90 个文件中的 20 个
───────────────────────────────────────────────────────────────────────────────
```
如你所见,输出正确识别了 `processor/processor.go` 和 `processor/workers.go` 是代码库中的热力点。根据我个人经验,可以确认这是正确的。
你为什么要在意?因为这个摘要正在做复杂度或变更频率单独无法做到的事情。
辛普森荷马解释如何做
> 解释如何做!
简而言之,`热力点 = 复杂度 × 提交次数`,在 0\-100 的范围内归一化。我们计算当前 HEAD 版本文件的复杂度,然后向后追溯,查看每个文件被修改了多少次。注意这仅统计 HEAD 中的文件。已被删除的高变更文件不在计算之列。
让我们将其与简单计数进行比较:
```
$ scc --by-file -i go -s complexity
───────────────────────────────────────────────────────────────────────────────
语言 文件数 总行数 空行数 注释行数 代码行数 总复杂度
───────────────────────────────────────────────────────────────────────────────
Go 69 40,137 3,049 2,131 34,957 4,478
───────────────────────────────────────────────────────────────────────────────
processor/workers_test.go 2,156 374 69 1,713 408
main_test.go 992 80 26 886 261
processor/workers.go 966 146 102 718 244
processor/report_test.go 971 98 109 764 237
config_test.go 786 50 85 651 199
```
通过运行简单的行数统计,限制为 Go 文件并按复杂度排序,我们看到 `workers_test.go`、`main_test.go` 和 `config_test.go` 排名都很高。这些文件技术上很复杂,但没有一个是真正的核心开发工作所在。复杂度本身只告诉你哪里有包含大量条件判断的大型文件。结果发现,这些往往是测试文件。它们仍然在列表中,只是排名靠后了。当然,高变更频率的测试文件仍然会如预期那样排在前面。
反过来按变更频率(即提交次数)排名。现在 `test-all.sh` 以 181 次提交位居第一,`structs.go` 也以 42 次提交浮上来。两者都经常变化,但都不是缺陷或逻辑所在。变更频率本身告诉你什么经常变化,这通常是配置、脚本和样板代码,但还不能完全代表缺陷或逻辑。
然而,变更频率和复杂度两者的重叠是一个合理的代理指标。哪些文件既复杂又变更频繁!注意,这与 Google 尝试并失败的做法并不完全相同。这个指标不是“历史上容易出错”的指向标,而是“难以处理”的指示器,可能暗示代码需要被拆分。
那么为什么这很有用呢?好吧,没人编辑的复杂代码可能不是问题。它能工作,然后你就继续前行。你经常修改的简单文件可能也不是问题。你添加一行配置,编译器检查通过,然后你继续前行。然而,一个既复杂又变更频繁的文件通常是问题所在。它是你遇到最多合并冲突、最多测试失败、修改起来最痛苦的地方。
现在我已经对 `scc` 代码库了解这些,但想象一下,如果我不熟悉它呢?我刚刚识别出了应用程序核心引擎所在的位置。
回到 Google,他们标记了有风险的文件,但开发者并不在意,因为知道热点在哪里并不能帮助你采取任何行动。知道“这个容易出错”只是在你的 CI/CD 中增加了一个标记,给你更多工作(把它记在我的技术债信用卡上)。知道你熟悉代码库中的热点用处不大。然而,在接入和学习一个代码库时,知道热点极其有用,而这个指标正是在回答那个问题。
> Google 失败了,因为热点标记没有提供可操作的建议,但针对不熟悉的代码库提出类似想法,就变成了接入地图。 \- 我
你还可以指定计算时追溯的 git 提交深度。通过回溯较少或较多的提交(时间),我们可以发现热力点如何转移。
回溯 50 次提交与 10 次对比:
```
$ scc --hotspots --depth 50
───────────────────────────────────────────────────────────────────────────────
热力点 · 最近 50 次提交 · 2026-04-13 → 2026-06-26
───────────────────────────────────────────────────────────────────────────────
文件 语言 复杂度 提交次数 代码行变化 作者数 热力点值
───────────────────────────────────────────────────────────────────────────────
processor/processor.go Go 156 14 415 4 100.0
main_test.go Go 261 8 295 4 95.6
processor/history.go Go 173 9 941 4 71.3
processor/workers.go Go 244 6 130 5 67.0
processor/workers_test.go Go 408 3 157 3 56.0
...
$ scc --hotspots --depth 10
───────────────────────────────────────────────────────────────────────────────
热力点 · 最近 10 次提交 · 2026-06-25 → 2026-06-26
───────────────────────────────────────────────────────────────────────────────
文件 语言 复杂度 提交次数 代码行变化 作者数 热力点值
───────────────────────────────────────────────────────────────────────────────
processor/workers.go Go 244 2 15 1 100.0
processor/processor.go Go 156 3 20 1 95.9
config_test.go Go 199 2 5 2 81.6
processor/history.go Go 173 1 19 1 35.5
regression_test.go Go 152 1 6 1 31.1
```
现在需要注意的一点是,这并没有告诉你代码库有什么问题。它只告诉你应该考虑从哪里着手查看。可能有一个特别严重的 bug 就藏在那个配置文件里。它只是一个指示器!
不过,正如那句话所说,所有模型都是错的,但有些是有用的。
请注意,上述功能都不需要安装 git。虽然它确实需要仓库有 `.git` 文件夹和所需文件,但 `scc` 内置了 `github.com/go\-git/go\-git`,仍然只是一个包含你所需所有功能的单个二进制文件安装包。
当然,这些计算并非免费... 那么获得这种能力的代价是什么?
现在做事情需要时间 - 汉弗莱·阿普比爵士
> 现在做事情需要时间! \- 汉弗莱·阿普比爵士
这对 `scc` 来说以前从未如此。它的运行和产生结果一直相当快(我拒绝说“快如闪电”)。但它以前只处理“现在”,即代码库的当前状态。
处理随时间变化的事情意味着需要回溯 git 历史,默认是最近 1000 次提交(当然你可以覆盖这个设置)。结果就是它比标准 scc 进程慢。
```
$ hyperfine 'scc' 'scc --hotspots'
基准测试 1: scc
时间(均值 ± 标准差): 11.2 ms ± 0.4 ms [用户: 15.2 ms, 系统: 7.6 ms]
范围(最小 ... 最大): 10.6 ms ... 13.5 ms 194 次运行
基准测试 2: scc --hotspots
时间(均值 ± 标准差): 4.739 s ± 0.068 s [用户: 3.341 s, 系统: 1.611 s]
范围(最小 ... 最大): 4.707 s ... 4.930 s 10 次运行
总结
scc 运行速度
比 scc --hotspots 快 421.57 ± 14.52 倍
```
以上是在我的 Macbook Air 2020 M1 上针对 `scc` 自身代码库计算的结果。它本身不算慢,但肯定没有在同台机器上运行标准 `scc` 处理该代码库所需的约 12ms 那么快。这算快吗?我不知道。我自己没用过 codescene。也许有人能告诉我。我试过运行 bugspots(https://github.com/igrigorik/bugspots)做对比,但由于代码库较老,可能无法运行。
## 变更耦合度
既然我已经从 CodeScene 借鉴了热力点想法,我想我也可以借鉴变更耦合度功能。其基本思想是,如果文件经常出现在同一个提交中,无论是否存在编译器强制的依赖关系,这些文件之间就存在相互依赖。
对 `scc` 自身运行该功能会产生以下精简输出:
```
$ scc --coupling
───────────────────────────────────────────────────────────────────────────────
变更耦合度 · 最近 1000 次提交 · 2019-07-25 → 2026-07-20
───────────────────────────────────────────────────────────────────────────────
文件 A 文件 B 共同提交次数 耦合度
───────────────────────────────────────────────────────────────────────────────
languages.json processor/constants.go 198 67.8%
LANGUAGES.md languages.json 167 61.2%
LANGUAGES.md processor/constants.go 153 58.2%
SCC-OUTPUT-REPORT.html processor/constants.go 149 37.7%
```
输出显示,`languages.json` 的变更通常会修改 `processor/constants.go`。这确实属实,上面的其他输出也是如此,因为每次添加或修改新语言时,上述每个文件都会更改。
虽然有趣,但当应用于特定文件时,它的用处更大:
```
$ scc --coupling-for ./processor/detector.go
───────────────────────────────────────────────────────────────────────────────
变更耦合度 · 最近 1000 次提交 · 2019-07-25 → 2026-07-20
───────────────────────────────────────────────────────────────────────────────
关联文件 共同提交次数 耦合度
───────────────────────────────────────────────────────────────────────────────
processor/detector_test.go 26 49.1%
processor/workers.go 15 12.1%
processor/structs.go 9 11.1%
processor/file_test.go 7 9.7%
processor/workers_test.go 6 9.7%
processor/processor_test.go 5 9.4%
```
现在这就有趣多了,尽管在这个例子中展示了显而易见的关联。如果你修改了检测器,你可能也需要修改它的测试。这个功能真正有用的地方在于,当你处理随机文件并想知道可能的爆炸半径(而这些关联并未被你的编译器检查覆盖)时。
然而,这种耦合可能存在同样的问题,即非代码文件也可能被检测到。因此,我们可以应用热力点技巧,按复杂度加权,得到下面的结果:
```
$ scc --coupling-weighted --coupling-for ./processor/detector.go
───────────────────────────────────────────────────────────────────────────────
变更耦合度 · 最近 1000 次提交 · 2019-07-25 → 2026-07-20
───────────────────────────────────────────────────────────────────────────────
关联文件 共同提交次数 分数
───────────────────────────────────────────────────────────────────────────────
processor/detector_test.go 26 100.0
processor/workers.go ...
相似文章
@tom_doerr: 本地分析超过4000万行代码库 https://github.com/giancarloerra/SocratiCode…
SocratiCode 是一个开源代码库上下文引擎,允许AI在本地分析和理解大型代码库(超过4000万行),无需配置,完全保护隐私。
Backplanes 的 Spotlight
Backplanes 推出了 Spotlight,为 Claude Code 和 Codex 提供会话报告,帮助改进您的代码。
CLI编码代理现状,2026年中(37分钟阅读)
详细比较了CLI编码代理,包括Claude Code、Codex CLI、Omp和OpenCode,指出前三者生成的结果质量相似,而OpenCode稍逊一筹,但可与多种模型配合使用。
@DanKornas:要求 Claude Code 进行小型修复或重构,可能会产生过多的测试、冗长的解释,或广泛的框架重写…
CCPlugins 是一组精选的 24 个专业命令,通过结构化的开发工作流扩展 Claude Code CLI,包括清理、提交、格式化、测试、重构和安全检查,并带有安全控制和 MIT 许可证。
multica-ai/andrej-karpathy-skills
一个单独的 CLAUDE.md 文件,实现了四项原则,以改进 Claude Code 的编码行为,源自 Andrej Karpathy 对 LLM 编码陷阱的观察。