Code Review Culture for Go
Review norms, nit guidelines, and teaching through PRs.
Search across all documentation pages
Review norms, nit guidelines, and teaching through PRs.
Pull requests are the main classroom for Go idioms on most teams.
A healthy review culture catches bugs early, spreads knowledge, and keeps APIs stable without burning out authors or reviewers.
Go review culture balances fast merge with high trust.
Reviewers focus on behavior under failure, concurrency safety, exported API promises, and operational hooks.
Cosmetic preferences yield to gofmt and linters.
Teaching happens in comments with rationale and links, not unexplained demands.
Quick-reference recipe card - copy-paste ready.
## PR description checklist (author)
- [ ] Linked issue or incident ticket
- [ ] go test ./... and golangci-lint run (paste or CI link)
- [ ] -race run if touching goroutines, maps shared across goroutines, or sync primitives
- [ ] Note breaking API changes and migration steps
- [ ] Screenshots or sample log lines for observable behavior changesWhen to reach for this:
Reviewer examines a handler PR that returns errors incorrectly.
// Before - reviewer blocks: wraps without %w, logs and returns same err
func (s *Server) fetchUser(ctx context.Context, id string) (*User, error) {
u, err := s.repo.Get(ctx, id)
if err != nil {
log.Printf("get user: %v", err)
return nil, fmt.Errorf("get user: %v", err)
}
return u, nil
}// After - single handling policy: wrap with %w, map to HTTP at handler edge
func (s *Server) fetchUser(ctx context.Context, id string) (*User, error) {
u, err := s.repo.Get(ctx, id)
if err != nil {
return nil, fmt.Errorf("fetch user %s: %w", id, err)
}
return u, nil
}
func (s *Server) handleUser(w http.ResponseWriter, r *http.Request) {
u, err := s.fetchUser(r.Context(), r.PathValue("id"))
if err != nil {
if errors.Is(err, ErrNotFound) {
http.NotFound(w, r)
return
}
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(u)
}What this demonstrates:
%w for errors.Is at boundaries.ErrNotFound mapping.| Priority | Look for | Example comment |
|---|---|---|
| P0 | Data races, panics on hot paths | "Shared map without mutex; run -race" |
| P1 | Wrong error semantics | "Use %w so handler can errors.Is" |
| P2 | API/export surface | "This type is public; godoc and stability?" |
| P3 | Missing tests on behavior change | "Add subtest for cancel path" |
| P4 | Observability | "5xx should log request ID" |
| P5 | Naming/style | "Consider rename" only if confusing |
// Reviewers expect context as first param after receiver
func (c *Client) Do(ctx context.Context, req *Request) (*Response, error)
// Flag storing context in structs
type Bad struct {
ctx context.Context // review: do not store context
}Teach accept interfaces, return structs by showing a minimal consumer-defined interface in test files.
golangci-lint.sync, channels, or errgroup touches.| Alternative | Use When | Don't Use When |
|---|---|---|
| Pair before submit | Author is new to concurrency | Trivial doc-only change |
| Mob review | Learning incident fixes | Daily small PRs |
| CODEOWNERS auto-assign | Spread domain knowledge | Sole gatekeeper on all files |
| Lint-only gate | Objective repeated nits | Judgment calls on API design |
Block on correctness and team ADRs. Send nits as optional suggestions unless automated. Promote repeated nits to linters.
For risky changes yes. For small diffs with green CI, reading test diffs may suffice. Trust but verify on concurrency.
Same bar as human code. Author must explain design; reviewers watch for plausible-but-wrong concurrency and bad module bumps.
Use when issues are non-blocking follow-ups tracked in tickets. Never for P0-P1 items.
Encourage questions and test readability feedback. Seniors model gracious responses to build psychological safety.
For new exports yes. For internal refactors, fix godoc in same PR or file follow-up before next release tag.
Short sync or ADR spike. Default to documented team standard until ADR changes.
First response within one business day; complete review within two for medium PRs. Escalate blockers explicitly.
Yes for team-specific choices: error mapping, module layout, observability field names, framework patterns.
Seniors are expected to leave educational comments and improve guidelines per Leveling Guide.
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 16, 2026