Go Code Review
Structured code review process for Go. Reviews should be constructive, specific,
and cite the relevant principle behind each finding.
Operating Modes
Pick the mode that matches the request before starting:
- Diff review (default) — review only the changed lines plus enough
surrounding context to judge them. Use for PRs and working-tree changes.
- File/package review — review the named files or packages in full,
including their tests.
- Full audit — sweep the entire codebase. Use the strategy in
"Auditing Large Codebases" below and aggregate everything into one report.
Review Process
Execute these steps in order. For each finding, classify severity:
- 🔴 BLOCKER — Must fix before merge. Correctness, data loss, security.
- 🟡 WARNING — Should fix. Maintainability, idiomatic Go, clarity.
- 🟢 SUGGESTION — Consider improving. Style, naming, documentation.
0. Run the Toolchain First
Before reading code manually, let the tools catch the mechanical issues
(skip any tool that is not installed and note it in the report):
go build ./... # it must compile
go vet ./... # suspicious constructs
golangci-lint run # if the repo has a config
go test -race ./... # tests pass, no data races
Report tool findings alongside manual findings — a failing go vet is
an automatic 🔴 BLOCKER. Never report an issue a tool already proves
absent.
1. Correctness & Safety
Error Handling
- Every error is checked. No blank identifier
_ discarding errors silently.
- Errors are wrapped with context:
fmt.Errorf("fetch user %d: %w", id, err).
- Error values compared with
errors.Is() / errors.As(), never ==.
- No
panic outside of init() or truly unrecoverable situations.
- Errors handled exactly once — no log-and-return patterns.
Nil Safety
- Pointer receivers checked before dereference when nil is a valid state.
- Map reads guarded or use comma-ok idiom.
- Channel operations consider closed/nil channels.
- Slice operations check bounds where relevant.
Concurrency
- Shared mutable state protected by
sync.Mutex or channels.
- No goroutine leaks — every goroutine has a clear termination path.
- Context propagation: all blocking calls accept and respect
context.Context.
sync.WaitGroup or errgroup.Group used for goroutine lifecycle.
2. API Design
- Exported functions have doc comments starting with the function name.
- Accept interfaces, return concrete types.
- Use functional options (
WithTimeout(d)) over config structs for optional params.
- Context is always the first parameter:
func Foo(ctx context.Context, ...).
- Return
error as the last return value.
- Avoid
bool parameters — prefer named types or options.
3. Idiomatic Go
- Uses
:= for local variables, var for zero-value intent.
- No unnecessary
else after return/continue/break.
- Guard clauses and early returns reduce nesting.
defer used for cleanup, placed right after resource acquisition.
range used over manual index iteration where appropriate.
- Struct literals use field names.
- Interfaces defined at consumer, not producer.
4. Package Structure
- Package names are short, lowercase, singular nouns.
- No circular dependencies between packages.
internal/ used for non-public packages.
cmd/ contains main packages, one per binary.
- Clear separation of concerns — no god packages.
5. Testing
- Test functions follow
TestXxx naming convention.
- Table-driven tests used for multiple input/output combinations.
- Test helpers use
t.Helper() for clean stack traces.
- No test logic in
init() — use TestMain when needed.
- Tests use
testify/assert or testify/require consistently, or stdlib only.
- Edge cases covered: empty input, nil, zero values, max values.
t.Parallel() used where safe.
6. Documentation
- All exported types, functions, and constants have doc comments.
- Doc comments start with the name of the entity.
- Package-level doc comment in
doc.go for non-trivial packages.
- Complex algorithms or business logic have inline comments explaining why.
7. Dependencies
go.mod has no replace directives in committed code (except monorepos).
- No unused dependencies.
- Dependencies are from well-maintained, reputable sources.
- Indirect dependencies are understood and acceptable.
Auditing Large Codebases
When the scope exceeds ~20 files, do not read everything in one linear
pass. Split the audit into independent passes:
- Enumerate packages (
go list ./...) and group them by layer
(handlers, services, stores, shared libraries).
- Run one focused pass per concern from sections 1-7 (correctness,
API design, idioms, structure, testing, docs, dependencies).
- If your environment supports delegating work to parallel sub-agents
or tasks, assign each pass to one — they are independent by design.
Otherwise run them sequentially, one concern at a time.
- Require every finding to cite
file.go:line and severity so the
final aggregation is mechanical: merge, deduplicate, sort by severity.
Review Output Format
## Code Review Summary
**Files reviewed:** <list>
**Overall assessment:** APPROVE | REQUEST CHANGES | COMMENT
### Findings
#### 🔴 BLOCKER: <title>
- **File:** `path/to/file.go:42`
- **Issue:** <what is wrong>
- **Why:** <which principle or guideline>
- **Fix:** <concrete suggestion>
#### 🟡 WARNING: <title>
...
#### 🟢 SUGGESTION: <title>
...
### What's Done Well
<genuine positive observations — always include at least one>
1---2name: go-code-review3description: Go Code Review4---56# Go Code Review78Structured code review process for Go. Reviews should be constructive, specific,9and cite the relevant principle behind each finding.1011## Operating Modes1213Pick the mode that matches the request before starting:1415- **Diff review** (default) — review only the changed lines plus enough16 surrounding context to judge them. Use for PRs and working-tree changes.17- **File/package review** — review the named files or packages in full,18 including their tests.19- **Full audit** — sweep the entire codebase. Use the strategy in20 "Auditing Large Codebases" below and aggregate everything into one report.2122## Review Process2324Execute these steps in order. For each finding, classify severity:25- 🔴 **BLOCKER** — Must fix before merge. Correctness, data loss, security.26- 🟡 **WARNING** — Should fix. Maintainability, idiomatic Go, clarity.27- 🟢 **SUGGESTION** — Consider improving. Style, naming, documentation.2829## 0. Run the Toolchain First3031Before reading code manually, let the tools catch the mechanical issues32(skip any tool that is not installed and note it in the report):3334```bash35go build ./... # it must compile36go vet ./... # suspicious constructs37golangci-lint run # if the repo has a config38go test -race ./... # tests pass, no data races39```4041Report tool findings alongside manual findings — a failing `go vet` is42an automatic 🔴 BLOCKER. Never report an issue a tool already proves43absent.4445## 1. Correctness & Safety4647### Error Handling48- Every error is checked. No blank identifier `_` discarding errors silently.49- Errors are wrapped with context: `fmt.Errorf("fetch user %d: %w", id, err)`.50- Error values compared with `errors.Is()` / `errors.As()`, never `==`.51- No `panic` outside of `init()` or truly unrecoverable situations.52- Errors handled exactly once — no log-and-return patterns.5354### Nil Safety55- Pointer receivers checked before dereference when nil is a valid state.56- Map reads guarded or use comma-ok idiom.57- Channel operations consider closed/nil channels.58- Slice operations check bounds where relevant.5960### Concurrency61- Shared mutable state protected by `sync.Mutex` or channels.62- No goroutine leaks — every goroutine has a clear termination path.63- Context propagation: all blocking calls accept and respect `context.Context`.64- `sync.WaitGroup` or `errgroup.Group` used for goroutine lifecycle.6566## 2. API Design6768- Exported functions have doc comments starting with the function name.69- Accept interfaces, return concrete types.70- Use functional options (`WithTimeout(d)`) over config structs for optional params.71- Context is always the first parameter: `func Foo(ctx context.Context, ...)`.72- Return `error` as the last return value.73- Avoid `bool` parameters — prefer named types or options.7475## 3. Idiomatic Go7677- Uses `:=` for local variables, `var` for zero-value intent.78- No unnecessary `else` after return/continue/break.79- Guard clauses and early returns reduce nesting.80- `defer` used for cleanup, placed right after resource acquisition.81- `range` used over manual index iteration where appropriate.82- Struct literals use field names.83- Interfaces defined at consumer, not producer.8485## 4. Package Structure8687- Package names are short, lowercase, singular nouns.88- No circular dependencies between packages.89- `internal/` used for non-public packages.90- `cmd/` contains main packages, one per binary.91- Clear separation of concerns — no god packages.9293## 5. Testing9495- Test functions follow `TestXxx` naming convention.96- Table-driven tests used for multiple input/output combinations.97- Test helpers use `t.Helper()` for clean stack traces.98- No test logic in `init()` — use `TestMain` when needed.99- Tests use `testify/assert` or `testify/require` consistently, or stdlib only.100- Edge cases covered: empty input, nil, zero values, max values.101- `t.Parallel()` used where safe.102103## 6. Documentation104105- All exported types, functions, and constants have doc comments.106- Doc comments start with the name of the entity.107- Package-level doc comment in `doc.go` for non-trivial packages.108- Complex algorithms or business logic have inline comments explaining *why*.109110## 7. Dependencies111112- `go.mod` has no replace directives in committed code (except monorepos).113- No unused dependencies.114- Dependencies are from well-maintained, reputable sources.115- Indirect dependencies are understood and acceptable.116117## Auditing Large Codebases118119When the scope exceeds ~20 files, do not read everything in one linear120pass. Split the audit into independent passes:1211221. Enumerate packages (`go list ./...`) and group them by layer123 (handlers, services, stores, shared libraries).1242. Run one focused pass per concern from sections 1-7 (correctness,125 API design, idioms, structure, testing, docs, dependencies).1263. If your environment supports delegating work to parallel sub-agents127 or tasks, assign each pass to one — they are independent by design.128 Otherwise run them sequentially, one concern at a time.1294. Require every finding to cite `file.go:line` and severity so the130 final aggregation is mechanical: merge, deduplicate, sort by severity.131132## Review Output Format133134```text135## Code Review Summary136137**Files reviewed:** <list>138**Overall assessment:** APPROVE | REQUEST CHANGES | COMMENT139140### Findings141142#### 🔴 BLOCKER: <title>143- **File:** `path/to/file.go:42`144- **Issue:** <what is wrong>145- **Why:** <which principle or guideline>146- **Fix:** <concrete suggestion>147148#### 🟡 WARNING: <title>149...150151#### 🟢 SUGGESTION: <title>152...153154### What's Done Well155<genuine positive observations — always include at least one>156```