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.

repowise capabilities for Go
Files parsed.go
Import resolutionFull. 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 graphResolved calls with a confidence per edge
Dead codePackage-granular reachability; unreachable files and unused exports
Health markersComplexity; class metrics do not apply to Go
Performance markersdatabase/sql and GORM queries in range loops, defer in loops, regexp.MustCompile in loops, goroutine spawn explosion
Coverage reportsgo test -coverprofile output
Frameworks recognisednet/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

Native Go analysis tools
ToolWhat it covers
deadcode (golang.org/x/tools)Unreachable functions from main, by whole-program analysis. Exact for what it covers.
staticcheckU1000 for unused identifiers, plus many correctness checks.
gocyclo, gocognitCyclomatic and cognitive complexity per function.
go tool coverCoverage 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 dashboard

Questions

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.

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.