Sloc Cloc and Code 4.0 (scc) - Finding the files that need the most attention
Pangram verdict · v3.3
We believe that this entire text is human-written.
AI likelihood · overall
HumanArticle text · 1,313 words · 1 segments analyzed
2026/08/23 · 3956 words So today I release the v4.0.0 version of sloc cloc and code AKA scc. While I was considering going from 3.7.0 to 3.8.0 enough new functionality landed in it that I figured a move to a new major version was worthwhile. It also was large enough to warrant another blog post going into some details, because I am genuinely excited about some of the new features in it. I am going to go through a few of them in this post and hopefully encourage you dear reader to get the latest version and try it out. Hotspots I had written over 10 years ago about Google’s bug prediction which ranked files using commit history against bug fixes to determine where problematic files existed. It was interesting but discontinued because, quote: TL;DR is that developers just didn’t find it useful. Sometimes they knew the code was a hot spot, sometimes they didn’t. But knowing that the code was a hot spot didn’t provide them with any means of effecting change for the better. Hilariously, I forgot I wrote about this, and got multiple LLMs to find it for me, and they all linked back to that post on my blog when I asked them to find it. Apparently I am the “authoritative source” on it now. I had always kept this in the back of my mind as something I’d like to explore more (hence trying to find it again). Recently I had a thought, since scc has a complexity estimate, can we use that to dampen out the noise? After all knowing a lot of fixes applied to a config file is not very useful, however knowing that lots of changes applied to a file with a lot of logic is. This is the same approach I took to ranking in codespelunker. Complex files need the most attention! As far as I can tell this is a reinvented idea from Adam Tornhill in “Your Code as a Crime Scene” (I am still reading the book after discovering this) and he even went off to create the company CodeScene as a result. Clearly there is some value in this metric. So much for me having an original idea. Anyway, let’s have a look at what you get, with scc running against its own codebase, $ scc --hotspots ─────────────────────────────────────────────────────────────────────────────── Hotspots · last 1000 commits · 2019-07-21 → 2026-06-26 ─────────────────────────────────────────────────────────────────────────────── File Lang Cmplx Commits Lines± Authrs Hotspot ─────────────────────────────────────────────────────────────────────────────── 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 ─────────────────────────────────────────────────────────────────────────────── complexity × change-frequency, normalised · 20 of 90 files shown ─────────────────────────────────────────────────────────────────────────────── As you can see the output has correctly identified that processor/processor.go and processor/workers.go are the hotspots in the codebase. I can confirm this is correct based on my own personal experience. Why should you care? Because that summary is doing something neither complexity nor churn can do by itself. Explain how! In short hotspot = complexity × commit_count normalised on a scale of 0-100. We calculate the complexity for the current HEAD file, then walk backwards seeing how many times each file was changed. Note that this only counts files in HEAD. High churn files that were removed are not counted. Lets compare it to a plain count, $ scc --by-file -i go -s complexity ─────────────────────────────────────────────────────────────────────────────── Language Files Lines Blanks Comments Code 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 By running a simple plain count, limited to Go files and sorted by complexity we see that workers_test.go, main_test.go and config_test.go are all ranked highly. All of these files are technically complex, but none of them are where the hard development work actually exists. Complexity on its own tells you where large files with if conditions exist. Turns out that is often test files. They are still in the list, just demoted. Of course high churn test files will still rise to the top with this as you would expect. Flip it and rank by churn, that is, the number of commits. Now test-all.sh is your number one with 181 commits, and structs.go floats up with 42. Both change constantly, but neither is where the bugs or logic are. Churn on its own tells you what changes a lot, which is often config, scripts, and boilerplate, but not quite a proxy for bugs or logic. What is a reasonable proxy however is the overlap of both churn and complexity. What files are complicated and have a lot of change! Note that this is not quite what Google had tried and failed with. This metric is not a “historically buggy” pointer, but a “hard to work with” indicator, possibly suggesting that code needs to be broken apart. So why is that useful? Well complex code that nobody edits probably isn’t an issue. It works and you move on. Simple files you change all the time probably aren’t an issue either. You add a line of config, the compiler checks it and you move on. However a file that is complex and changes a lot is where problems usually lie. It’s where you get the most merge conflicts, most breaking tests, and pain when it comes to making changes. Now I already knew this for the scc codebase, but imagine I am not familiar with it. I just identified where the beating engine of the application lies. Bringing it back to Google, they flagged risky files and developers didn’t care because knowing where a hotspot is does not help you do anything about it. Knowing “this is buggy” is just another flag in your CI/CD giving you more work (throw it on my technical debt credit card). Knowing the hotspots in a codebase you know about isn’t that useful. However it is extremely helpful to know hotspots when onboarding and learning a codebase, and this is telling you the answer to that exact question. Google failed because a hotspot flag gives you no action, but a similar idea pointed at an unfamiliar codebase becomes an onboarding map. - Me One other thing you can do is specify the depth in git commits that this is calculated for. We can find out where hotspots have shifted by looking backwards over less or more commits (time). Looking back 50 commits vs 10, $ scc --hotspots --depth 50 ─────────────────────────────────────────────────────────────────────────────── Hotspots · last 50 commits · 2026-04-13 → 2026-06-26 ─────────────────────────────────────────────────────────────────────────────── File Lang Cmplx Commits Lines± Authrs Hotspot ─────────────────────────────────────────────────────────────────────────────── 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 ─────────────────────────────────────────────────────────────────────────────── Hotspots · last 10 commits · 2026-06-25 → 2026-06-26 ─────────────────────────────────────────────────────────────────────────────── File Lang Cmplx Commits Lines± Authrs Hotspot ─────────────────────────────────────────────────────────────────────────────── 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