Languages / Go
Go code analysis: dead code, call graphs and complexity
For dead code in Go, the official deadcode tool and staticcheck's U1000 check are the first choices. repowise adds a repository-wide view: package-level reachability across modules, HTTP and gRPC handlers registered with gin, echo, chi, fiber, gorilla/mux and net/http treated as used, database queries in loops flagged, and each finding ranked by git history.
Last reviewed: November 2026
Full tier
What repowise detects in Go
Checked against the engine source. Every row runs locally with zero LLM calls.
| Files parsed | .go |
|---|---|
| Import resolution | Full. go.mod module paths and filesystem replace directives, several go.mod files in one repository, package fan-out to every file in the directory |
| Call graph | Resolved calls with a confidence per edge |
| Dead code | Package-granular reachability; unreachable files and unused exports |
| Health markers | Complexity; class metrics do not apply to Go |
| Performance markers | database/sql and GORM queries in range loops, defer in loops, regexp.MustCompile in loops, goroutine spawn explosion |
| Coverage reports | go test -coverprofile output |
| Frameworks recognised | net/http HandleFunc, gin, echo, chi, fiber, gorilla/mux, gRPC RegisterXxxServer |
Specific to this language
How Go code is read
Reachability works per package
Go imports packages, not files, so repowise judges liveness per package: an imported package keeps all its files reachable, and files beside a cmd/foo/main.go are live. func init does not rescue a package nothing imports.
Handlers are reached through routers
A handler passed to r.GET in gin or echo, r.Get in chi or fiber, a gorilla/mux route, http.HandleFunc, or a gRPC RegisterXxxServer call is linked from the registration site, so it is not reported as unused.
Go-specific risk markers
Queries through database/sql (Query, QueryRow, Exec and their Context forms) or GORM finishers inside a for range loop are flagged as N+1. defer inside a loop, regexp.MustCompile per iteration and unbounded goroutine spawning get their own markers.
Honest comparison
Native Go tools, and when they are enough
| Tool | What it covers |
|---|---|
| deadcode (golang.org/x/tools) | Unreachable functions from main, by whole-program analysis. Exact for what it covers. |
| staticcheck | U1000 for unused identifiers, plus many correctness checks. |
| gocyclo, gocognit | Cyclomatic and cognitive complexity per function. |
| go tool cover | Coverage from go test -coverprofile, which repowise reads directly. |
| callgraph (golang.org/x/tools) | Static call graphs with several algorithms. |
Where repowise adds value
- Files and packages ranked by confidence and git history, with the owner named, not a flat list.
- The Go services and the TypeScript or Python code next to them in one graph.
- Health and performance markers (GORM in loops, defer in loops) next to hotspots and co-change.
- No build or toolchain needed on the analysis machine.
How to run it
Analyse a Go repository
From the repository root. Agents get the same results through the get_dead_code, get_health and get_context MCP tools.
uv tool install repowise # or: pipx install repowise
repowise init --yes --no-prose # index the repo, no API key
repowise dead-code --min-confidence 0.7 --safe-only
repowise health --refactoring-targets
go test -coverprofile=cover.out ./...
repowise coverage add cover.out
repowise serve # graph, health and dead code in a local dashboardQuestions
What people ask before they try it.
Is repowise better than deadcode or staticcheck for Go?
For unused functions inside one Go module, deadcode and staticcheck are exact because they use the type checker. repowise is the pick for repository-wide cleanup across several modules and languages, for findings ranked by git history, and for giving an agent the results over MCP.
How does it treat func init?
init does not make a package live. A package nothing imports is reported even if it has an init function, which is usually the case you want to see.
Does it read go test coverage?
Yes. repowise coverage add cover.out reads a coverprofile, detected from its mode: first line.
Does repowise send my Go code to an LLM?
No. Parsing, the call graph, dead code, health scores, performance markers and git history all run locally with zero LLM calls. repowise init --yes --no-prose needs no API key, and the analysis itself makes no network calls. Written documentation prose is the one opt-in step that uses a model, and it can run on a local Ollama model.
Related reading
Index a Go repository.
Public repositories are free on repowise.dev. The CLI is open source and runs on your machine with no API key.