Refactoring & Architecture Audit Checklist
Red flags in package coupling, globals, and init() abuse.
Search across all documentation pages
Red flags in package coupling, globals, and init() abuse.
Walk this checklist before a major release, after onboarding spikes, or when compile times and incident rates climb together.
Global mutable state for DB, config, or clients: Replace with constructor injection from main.
var db *sql.DB used across packagesNewService(repo Repository) wired once at startupImport cycles between production packages: Extract shared contracts or invert dependency direction.
handler imports service imports handlerinternal/contractsinit() performs I/O or panics on misconfiguration: Fail in main with clear errors instead.
init()cfg, err := Load(); if err != nil { log.Fatal(err) }Business rules only in HTTP handlers: Move validation and workflows to services with unit tests.
ServeHTTP with SQLRegister(ctx, email) tested without httptestSecrets or endpoints hardcoded in library packages: Load from env/flags in composition root only.
const apiURL = "https://prod..."Config.APIURL validated at bootutil, common, helpers): Split by capability (internal/timeutil, internal/validate).net/http, gin, or gRPC generated code: Move adapters to internal/adapter/*.internal/ to freeze API surface.cmd/ binaries duplicating wiring divergently: Extract shared Provide* helpers or Wire set.context.Context on I/O functions: Add ctx as first parameter; plumb from handlers.var ErrNotFound = errors.New(...) and errors.Is._) without comment explaining side effect: Document registration purpose or remove.models, controllers) with cross-imports: Reorganize by feature slice.replace directives committed for local laptop paths: Use go.work locally; keep go.mod portable.slog or team logger at edges.http.Server timeouts and MaxBytesReader.go generate, sqlc, or buf in CI.One to two hours for a medium module with go mod graph and package import review.
Deeper dives need per-feature owners.
go mod graph, golangci-lint (depguard, forbidigo), custom go:generate import cycle checks, and go vet ./....
No.
Fix Critical, then strand High items into feature tickets - big-bang rewrites rarely ship.
No fixed line count - if three teams edit it weekly and imports fan out, split regardless of LOC.
grep -R "func init()" internal/ cmd/ and classify each as register-only vs I/O.
Ban I/O inits in lint rules.
Tiny, stable helpers with no business meaning and one owner team.
Rename to specific purpose when it grows.
Items 1-12 must be green before RPC extraction - otherwise you distribute spaghetti.
Import graph screenshot, incident IDs, compile time trend, and test flake rate.
Yes with a senior sponsor - great onboarding exercise reading package imports.
After quick hacks - add depguard linter rule blocking var .* \*sql.DB outside cmd/.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026