关于 go-code-review
go-code-review 是面向 Go 项目的代码审查技能,依据项目 Go 版本、改动上下文和适用清单组织审查,并要求在报告问题前完成核验。
主要功能
- 01核对 Go 版本基线
- 02审阅完整函数上下文
- 03按范围选择审查清单
- 04错误处理与并发指引
- 05按严重性校准问题
适用场景
- 01审查改动的 Go 函数
- 02检查错误处理与资源清理
- 03结合 Go 版本评估并发改动
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill go-code-review安装后在 Claude Code 中输入/go-code-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/common-mistakes.mdconcurrency.mderror-handling.mdinterfaces.md详细介绍
Go 代码审查
审查工作流
按以下顺序执行。在满足以下所有 通过 条件之前,不要报告问题。
-
确定基准
go.mod——打开go.mod并读取go指令。
**通过:**能够准确说出go X.YY的值(在审查前言或工作笔记中)。仅在与此基准版本匹配时应用版本限定的建议(循环变量捕获适用于 1.22 之前的版本,slog/结构化日志从 1.21 开始,errors.Join从 1.20 开始)。 -
阅读周边代码——对于每个已更改的
.go文件,阅读包含改动的完整函数或逻辑单元,而不只是差异片段。
**通过:**每个已更改文件中,至少阅读了一个包含改动的完整外层函数(或包级init/var 块)。 -
确定检查清单范围——判断适用审查检查清单中的哪些部分(错误处理、并发、接口/类型、资源、命名)。加载这些部分的参考资料;与差异无关的部分可跳过。
**通过:**审查(或工作笔记)列出了应用的检查清单部分,或将不适用的部分标记为 N/A,并给出与差异相关的一句话理由(例如“改动不涉及并发”)。 -
报告前核验——加载并遵循 review-verification-protocol。
通过:对于将要报告的每个问题,均满足协议中的报告前核验清单(已阅读实际代码、检查周边上下文、区分“错误”与“风格不同”等)。
硬性门槛(相同步骤,表述更简洁)
| 步骤 | 客观通过条件 |
|---|---|
| 1 | 在给出版本限定的建议之前,已记录 go.mod 中的 go X.YY。 |
| 2 | 已阅读每个已更改文件的完整外层上下文,而不只是差异。 |
| 3 | 已列出适用的检查清单部分,或根据差异给出理由并标记为 N/A;按需打开参考资料。 |
| 4 | 对每个报告的问题,均已完成 review-verification-protocol。 |
输出格式
按以下格式报告问题:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
快速参考
| 问题类型 | 参考资料 |
|---|---|
| 未检查错误、未包装错误、errors.Join | references/error-handling.md |
| 竞态条件、通道误用、goroutine 生命周期 | 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 泄漏(存在上下文取消或关闭信号)
- 仅由发送方关闭通道,且恰好关闭一次
- 使用互斥锁或 sync 类型保护共享状态
- 使用 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、通道或 sync 类型 → 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 Workflow 中的 第 4 步:加载 review-verification-protocol,并针对每个问题完成报告前检查。