Go Code Review
Review Workflow
Follow this sequence to avoid false positives and catch version-specific issues:
- 1. Check
go.mod — Note the Go version. This determines which patterns apply (loop variable capture is only an issue pre-1.22, slog is available from 1.21, errors.Join from 1.20). Skip version-gated checks that don't apply. - Scan changed files — Read full functions, not just diffs. Many Go bugs hide in what surrounds the change.
- Check each category — Work through the checklist below, loading references as needed.
- Verify before reporting — Load beagle-go:review-verification-protocol before submitting findings.
Output Format
Report findings as:
CODEBLOCK0
Quick Reference
| Issue Type | Reference |
|---|
| Missing error checks, wrapping, errors.Join | references/error-handling.md |
| Race conditions, channel misuse, goroutine lifecycle |
references/concurrency.md |
| Interface pollution, naming, generics |
references/interfaces.md |
| Resource leaks, defer misuse, slog, naming |
references/common-mistakes.md |
Review Checklist
Error Handling
- - [ ] All errors checked (no
_ = err without justifying comment) - [ ] Errors wrapped with context (
fmt.Errorf("...: %w", err)) - [ ]
errors.Is/errors.As used instead of string matching - [ ]
errors.Join used for aggregating multiple errors (Go 1.20+) - [ ] Zero values returned alongside errors
Concurrency
- - [ ] No goroutine leaks (context cancellation or shutdown signal exists)
- [ ] Channels closed by sender only, exactly once
- [ ] Shared state protected by mutex or sync types
- [ ] WaitGroups used to wait for goroutine completion
- [ ] Context propagated through call chain
- [ ] Loop variable capture handled (pre-Go 1.22 codebases only)
Interfaces and Types
- - [ ] Interfaces defined by consumers, not producers
- [ ] Interface names follow
-er convention - [ ] Interfaces minimal (1-3 methods)
- [ ] Concrete types returned from constructors
- [ ]
any preferred over interface{} (Go 1.18+) - [ ] Generics used where appropriate instead of
any or code generation
Resources and Lifecycle
- - [ ] Resources closed with
defer immediately after creation - [ ] HTTP response bodies always closed
- [ ] No
defer in loops without closure wrapping - [ ]
init() functions avoided in favor of explicit initialization
Naming and Style
- - [ ] Exported names have doc comments
- [ ] No stuttering names (
user.UserService → user.Service) - [ ] No naked returns in functions > 5 lines
- [ ] Context passed as first parameter
- [ ]
slog used over log for structured logging (Go 1.21+)
Severity Calibration
Critical (Block Merge)
- - Unchecked errors on I/O, network, or database operations
- Goroutine leaks (no shutdown path)
- Race conditions on shared state (concurrent map access without sync)
- Unbounded resource accumulation (defer in loop, unclosed connections)
Major (Should Fix)
- - Errors returned without context (bare
return err) - Missing WaitGroup for spawned goroutines
- INLINECODE20 for recoverable errors
- Context not propagated to downstream calls
Minor (Consider Fixing)
- -
interface{} instead of any in Go 1.18+ codebases - Missing doc comments on exports
- Stuttering names
- Slice not preallocated when size is known
Informational (Note Only)
- - Suggestions to add generics where code generation exists
- Refactoring ideas for interface design
- Performance optimizations without measured impact
When to Load References
- - Reviewing error return patterns → error-handling.md
- Reviewing goroutines, channels, or sync types → concurrency.md
- Reviewing type definitions, interfaces, or generics → interfaces.md
- General review (resources, naming, init, performance) → common-mistakes.md
Valid Patterns (Do NOT Flag)
These are acceptable Go patterns — reporting them wastes developer time:
- -
_ = err with reason comment — Intentionally ignored errors with explanation - Empty interface /
any — For truly generic code or interop with untyped APIs - Naked returns in short functions — Acceptable in functions < 5 lines with named returns
- Channel without close — When consumer stops via context cancellation, not channel close
- Mutex protecting struct fields — Even if accessed only via methods, this is correct encapsulation
//nolint directives with reason — Acceptable when accompanied by explanation- Defer in loop — When function scope cleanup is intentional (e.g., processing files in batches)
- Functional options pattern —
type Option func(*T) with With* constructors is idiomatic sync.Pool for hot paths — Acceptable for reducing allocation pressure in performance-critical codecontext.Background() in main/tests — Valid root context for top-level callsselect with default — Non-blocking channel operation, intentional pattern- Short variable names in small scope —
i, err, ctx, ok are idiomatic Go
Context-Sensitive Rules
Only flag these issues when the specific conditions apply:
| Issue | Flag ONLY IF |
|---|
| Missing error check | Error return is actionable (can retry, log, or propagate) |
| Goroutine leak |
No context cancellation path exists for the goroutine |
| Missing defer | Resource isn't explicitly closed before next acquisition or return |
| Interface pollution | Interface has > 1 method AND only one consumer exists |
| Loop variable capture |
go.mod specifies Go < 1.22 |
| Missing slog |
go.mod specifies Go >= 1.21 AND code uses
log package for structured output |
Before Submitting Findings
Load and follow review-verification-protocol before reporting any issue.
Go 代码审查
审查工作流程
按以下顺序操作,以避免误报并捕获特定版本的问题:
- 1. 检查 go.mod — 记录 Go 版本。这决定了哪些模式适用(循环变量捕获仅在 1.22 版本之前是问题,slog 从 1.21 版本可用,errors.Join 从 1.20 版本可用)。跳过不适用的版本限制检查。
- 扫描变更文件 — 阅读完整函数,而不仅仅是差异。许多 Go 错误隐藏在变更周围的代码中。
- 检查每个类别 — 按照下面的检查清单逐一检查,根据需要加载参考资料。
- 提交前验证 — 在提交发现结果之前,加载 beagle-go:review-verification-protocol。
输出格式
按以下格式报告发现结果:
text
[FILE:LINE] ISSUE_TITLE
严重级别:严重 | 主要 | 次要 | 参考信息
问题描述及其重要性说明。
快速参考
references/concurrency.md |
| 接口污染、命名、泛型 |
references/interfaces.md |
| 资源泄漏、defer 误用、slog、命名 |
references/common-mistakes.md |
审查检查清单
错误处理
- - [ ] 所有错误都已检查(没有未经注释说明的 _ = err)
- [ ] 错误已附带上下文进行包装(fmt.Errorf(...: %w, err))
- [ ] 使用 errors.Is/errors.As 而非字符串匹配
- [ ] 使用 errors.Join 聚合多个错误(Go 1.20+)
- [ ] 返回错误时同时返回零值
并发
- - [ ] 没有 goroutine 泄漏(存在上下文取消或关闭信号)
- [ ] 通道仅由发送方关闭,且仅关闭一次
- [ ] 共享状态受互斥锁或同步类型保护
- [ ] 使用 WaitGroup 等待 goroutine 完成
- [ ] 上下文通过调用链传播
- [ ] 循环变量捕获已处理(仅限 Go 1.22 之前的代码库)
接口和类型
- - [ ] 接口由消费者定义,而非生产者
- [ ] 接口名称遵循 -er 约定
- [ ] 接口最小化(1-3 个方法)
- [ ] 构造函数返回具体类型
- [ ] 优先使用 any 而非 interface{}(Go 1.18+)
- [ ] 在适当情况下使用泛型替代 any 或代码生成
资源和生命周期
- - [ ] 资源在创建后立即使用 defer 关闭
- [ ] HTTP 响应体始终关闭
- [ ] 循环中没有未包裹闭包的 defer
- [ ] 避免使用 init() 函数,优先使用显式初始化
命名和风格
- - [ ] 导出的名称有文档注释
- [ ] 没有重复命名(user.UserService → user.Service)
- [ ] 超过 5 行的函数没有裸返回
- [ ] 上下文作为第一个参数传递
- [ ] 使用 slog 而非 log 进行结构化日志记录(Go 1.21+)
严重级别校准
严重(阻止合并)
- - I/O、网络或数据库操作中未检查的错误
- Goroutine 泄漏(无关闭路径)
- 共享状态的竞态条件(无同步的并发 map 访问)
- 无限制的资源累积(循环中的 defer、未关闭的连接)
主要(应修复)
- - 返回错误时没有上下文(裸 return err)
- 缺少对生成的 goroutine 的 WaitGroup
- 对可恢复错误使用 panic
- 上下文未传播到下游调用
次要(考虑修复)
- - 在 Go 1.18+ 代码库中使用 interface{} 而非 any
- 导出的内容缺少文档注释
- 重复命名
- 已知大小但未预分配的切片
参考信息(仅作记录)
- - 建议在存在代码生成的地方添加泛型
- 接口设计的重构思路
- 未经测量影响的性能优化
何时加载参考资料
- - 审查错误返回模式 → error-handling.md
- 审查 goroutine、通道或同步类型 → concurrency.md
- 审查类型定义、接口或泛型 → interfaces.md
- 一般审查(资源、命名、init、性能) → common-mistakes.md
有效模式(请勿标记)
这些是可接受的 Go 模式——报告它们会浪费开发人员时间:
- - _ = err 附带原因注释 — 有意忽略的错误并附有解释
- 空接口 / any — 用于真正的泛型代码或与无类型 API 的互操作
- 短函数中的裸返回 — 在少于 5 行且有命名返回值的函数中可接受
- 未关闭的通道 — 当消费者通过上下文取消而非通道关闭停止时
- 互斥锁保护结构体字段 — 即使仅通过方法访问,这也是正确的封装
- 带有原因的 //nolint 指令 — 附有解释时可接受
- 循环中的 defer — 当函数作用域清理是有意为之(例如,批量处理文件)
- 函数选项模式 — type Option func(T) 配合 With 构造函数是惯用写法
- 热路径中的 sync.Pool — 用于减少性能关键代码中的分配压力,可接受
- main/测试中的 context.Background() — 顶层调用的有效根上下文
- 带 default 的 select — 非阻塞通道操作,有意为之的模式
- 小作用域中的短变量名 — i、err、ctx、ok 是 Go 的惯用写法
上下文相关规则
仅在特定条件适用时标记这些问题:
| 问题 | 仅在以下情况标记 |
|---|
| 缺少错误检查 | 错误返回值是可操作的(可以重试、记录或传播) |
| Goroutine 泄漏 |
goroutine 不存在上下文取消路径 |
| 缺少 defer | 资源在下一次获取或返回前未显式关闭 |
| 接口污染 | 接口有 > 1 个方法且只有一个消费者 |
| 循环变量捕获 | go.mod 指定 Go < 1.22 |
| 缺少 slog | go.mod 指定 Go >= 1.21 且代码使用 log 包进行结构化输出 |
提交发现结果前
在报告任何问题之前,加载并遵循 review-verification-protocol。