
This tutorial explains how to identify unused exported and unexported functions, types, and variables across a Go monorepo—including library packages and cmd/ binaries—using staticcheck with whole-program analysis.
this tutorial explains how to identify unused exported and unexported functions, types, and variables across a go monorepo—including library packages and cmd/ binaries—using staticcheck with whole-program analysis.
When maintaining a large Go monorepo—especially one with shared libraries under internal/ or pkg/, and multiple binaries under cmd/—dead code accumulates silently over time. While go vet catches some local unused identifiers (e.g., unexported functions within a single package), it cannot trace cross-package usage or determine whether an exported function is ever called by any binary in the repo.
The most reliable solution is staticcheck—which absorbed Dominik Honnef’s unused tool—and supports whole-program dead-code detection via the --unused.whole-program=true flag. This mode treats your entire monorepo as a single program: it builds call graphs across all packages and flags identifiers (functions, methods, types, constants, variables) that are never referenced from any entry point—i.e., any main function under cmd/.
✅ Prerequisites:
- Go 1.16+ (with modules enabled)
- A well-formed
go.modat the monorepo root
? Installation (use the last version supporting whole-program analysis):
env GO111MODULE=on go install honnef.co/go/tools/cmd/staticcheck@v0.0.1-2020.1.4
⚠️ Note: Later versions of staticcheck (≥2021.1) removed --unused.whole-program due to high memory usage and correctness challenges. Stick to v0.0.1-2020.1.4 for this capability.
? Run the analysis:
staticcheck --unused.whole-program=true -- ./...
Example output:
./pkg/utils/format.go:12:6: func FormatJSON is unused (U1001) ./internal/auth/token.go:7:12: type TokenValidator is unused (U1001) ./shared/errors.go:3:2: var ErrInvalidState is unused (U1001)
? Tips for effective cleanup:
- Start with
--unused.whole-program=trueon./..., then narrow scope (e.g.,./pkg/...) if results are overwhelming. - Review flagged exports before deleting: some may be intended for future use or external consumption (e.g., public SDKs). Consider adding
//nolint:unusedpragmas where appropriate. - Combine with
go mod graphorgo list -depsto verify dependency reachability when uncertain. - Automate in CI: add a step that fails if new unused identifiers appear (though be cautious—flaky or overly strict checks can hinder velocity).
By leveraging staticcheck’s whole-program mode, teams gain precise, actionable visibility into dead code across their Go monorepo—enabling safer refactoring, smaller binaries, and cleaner, more maintainable codebases.










