go项目代码审查必须聚焦context未cancel、error未unwrap、defer参数提前求值、sync.map误用四类高频线上问题;pr需强制填写what、why、how、test、related五字段,缺一拒合;审查优先定位main、init、http.handlerfunc及context.with相关函数,严查资源释放、ctx配对、error链完整性与并发安全。

Go 项目代码审查不能靠“看看有没有语法错误”,真正卡住上线的,往往是 context 没 cancel、error 没 unwrap、defer 参数提前求值、sync.Map 误用这四类问题。
PR 提交前必须填满的 5 个字段
不是“建议填写”,是 CI 拒绝合并的硬门槛。缺任意一项,自动打回:
-
What:用一句话说清改了什么(不是“修复 bug”,而是“HTTP handler 中补全resp.Body.Close()防泄漏”) -
Why:说明触发修改的直接原因(如“go tool trace显示 goroutine 数持续上涨”或“日志中出现http: response.WriteHeader on hijacked connection”) -
How:描述关键改动点(例如“在handleUserRequest函数入口加ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second),并在 defer 中调用cancel()”) -
Test:明确列出覆盖的测试项(如“新增TestHandleUserRequest_Timeout,验证超时后ctx.Done()被监听”) -
Related:关联 issue 编号或线上 error ID(如ISSUE-1234或ERR-789012)
评审时优先盯死的 4 类函数签名
别从头读代码。先用 grep -n "func main" *.go、grep -n "func init" *.go、grep -n "http.HandlerFunc" *.go、grep -n "context.With" *.go 定位关键节点:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
func main():查是否有阻塞调用(如time.Sleep、无 timeout 的http.ListenAndServe)、全局资源(sql.DB、redis.Client)是否在main结束前 close -
func init():查是否有跨包初始化依赖(如var a = b,而b在另一个文件的init中定义)、是否调用了未初始化的全局变量 -
http.HandlerFunc:查是否漏掉defer resp.Body.Close()、是否用http.Error返回 500 却没记录日志、是否直接返回err而非包装为fmt.Errorf("xxx: %w", err) -
context.WithCancel/context.WithTimeout:查是否配对cancel()、cancel()是否在defer中、是否在 goroutine 内部调用defer cancel()(会导致不执行)
error 处理审查最容易忽略的 3 个细节
Go 的 error 不是字符串拼接,链式行为必须显式维护:
- 所有自定义 error 类型,如果实现了
Error()方法,但没实现Unwrap(),会导致errors.Is(err, myErr)永远返回false - 用
fmt.Errorf("%s", err)包装错误会断开 error 链;必须用fmt.Errorf("xxx: %w", err)(%w是唯一能保留链的动词) - 函数返回 error 时,若内部有多个 error 来源(如 DB 查询 + HTTP 调用),不能只返回第一个;应统一用
fmt.Errorf("db query failed: %w", dbErr)或fmt.Errorf("http call failed: %w", httpErr),避免掩盖根因
并发与资源释放的硬性检查项
这些不是“建议优化”,是线上事故高频来源,必须逐行确认:
- 所有
os.Open、http.Get、sql.Query等返回可关闭资源的调用,必须配defer xxx.Close(),且defer语句必须在资源获取成功后立即写(不能放在 if 分支里漏掉) - 所有启动 goroutine 的地方,必须确认
ctx是否已传入、是否监听ctx.Done()、是否在退出前清理(如关闭 channel、释放锁) - 用
sync.Map前先问:这个 map 是高频读+极低频写?如果不是,直接用map+sync.RWMutex更可控;sync.Map不支持len()和range,容易引发隐性 bug
最常被跳过的其实是 error 链完整性——%w 写错一个,下游 errors.Is 就失效;还有 defer 参数求值时机,看似写了 defer log.Println(i),其实打印的是循环开始前的旧值。这类问题不会编译报错,但会让故障排查时间翻倍。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










