
beats
Measure the structural fingerprint of a Go codebase.
beats clusters Go functions by the skeleton of how they are written — independent of names, comments, domain vocabulary or semantic meaning. The goal is to find meaningful patterns in code by looking at what it does structurally, not what it means semantically.
What is beats?
beats identifies recurring structural patterns across an entire Go codebase to answer one question: does the golang code across the repository coalesce to form a structural pattern and if so, how can we identify and evaluate the same?
Beats define Structural fingerprint as follows.
For each function, beats computes three features:
-
Token sequence — an ordered list of AST mnemonics representing the structural skeleton of the function body. Each token is a normalised AST node, for example : CALL for a function call, ASSIGN for a variable assignment, RETURN for a return statement (with arity), IF, FOR, RANGE, and so on. No names, no literals — only structure.
-
Call targets (fan-out) — the set of external functions invoked within the function body.
-
Direct imports — the set of packages actually used within the function (not just imported by the file). This captures the dependency shape at the function level, not the file level.
No attempt is made to understand what a call target does or what an import statement provides. That would reintroduce vocabulary dependence and defeat the purpose.
These three features — along with some additional metadata — form a FunctionMetadata record. Beats collects FunctionMetadata across the entire codebase and clusters them using a weighted similarity function:
| Feature |
Weight |
| Token sequence similarity |
50% |
| Jaccard similarity of import sets |
30% |
| Jaccard similarity of call target sets |
20% |
The output is N clusters, each with a coherence value — a measure of how tightly packed the function metadata within it are. Coherence is broken into two axes:
|
High Call Cohesion |
Low Call Cohesion |
| High Import Cohesion |
Tight domain-local pattern — shares both package context and call vocabulary. Most actionable. |
Domain-cohesive, structurally diverse — shared package domain, divergent calls. May benefit from splitting. |
| Low Import Cohesion |
Cross-cutting structural pattern — different domains, same structural role (e.g. cron registration, adapters). |
Likely noise — coincidental structural similarity rather than convention. Treat with scepticism. |
📦 Installation
Prerequisites
Install via Homebrew (macOS / Linux)
brew tap somak2kai/beats
brew install beats
Upgrade to the latest release at any time:
brew upgrade beats
Install from source
Clone the repository and build the CLI:
git clone https://github.com/somak2kai/beats.git
cd beats
go build -o beats ./cmd/
Move the binary somewhere on your $PATH:
mv beats /usr/local/bin/
Or run directly without installing:
go run ./cmd/ <command> [flags]
Verify
beats --version
🚀 Usage
beats has two main commands: init to index a repository and analyze to cluster and report on it.
beats init — index a repository
Walks a Go codebase and writes FunctionMetadata records into a local Badger store.
beats init --repo=<path-to-go-repository>
Example:
beats init --repo=/home/user/projects/myservice
What gets indexed:
- All Go source files under the repository root (excluding
vendor/ and test files by default, auto generated files such as pb.go)
- For each exported and unexported function: token sequence, call targets, direct imports, file path, line number, package name
beats analyze — cluster and report
Reads the function index written by init, runs the clustering algorithm, and produces an HTML report at <repo>/.beats/report.html.
beats analyze --repo=<path-to-go-repository>
Example:
beats analyze --repo=/home/user/projects/myservice
Open the report:
open /home/user/projects/myservice/.beats/report.html
The report shows all clusters sorted by combined coherence, with per-cluster member lists, top imports, Cyclo P95, package distribution, and a coherence quadrant breakdown.
📄 License
MIT License
Copyright (c) 2026 somak2kai
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
📊 Report Analyser
The analyzer/ package contains a Python script for parsing and summarising beats HTML reports in the terminal.
→ analyzer/README.md
What it covers:
- How to run
analyze_report.py against any beats report
- Coherence quadrant reference table
- Sample output from a real analysis run (Gitea, ~500 clusters)
🔬 SCIP Validation Tool
The x/tools/cmp/ package contains a comparison tool that validates beats clusters against SCIP (Sourcegraph Code Intelligence Protocol) reference data. It computes precision, recall, and F1 per cluster to measure how well the structural fingerprint aligns with semantic reference graphs.
→ x/tools/cmp/README.md
What it covers:
- Installing and running
scip-go on a repository
- Running the beats vs SCIP comparison
- How to interpret recall, precision, and F1 in the beats context
- Why low precision is expected (and desirable) behaviour