Go规范是工具链强制要求:package名须小写单数且与目录严格一致;error必须最后一个返回并立即检查;gofmt仅格式化,goimports才管理import分组;context必须首位传入。

Go 代码不是“写完能跑就行”,而是“不按规范写,连编译器和工具链都会拒绝你”。gofmt 不是格式美化工具,是语法共识的强制执行者;package 名不匹配目录名,go build 直接报 found packages main and xxx in xxx;error 不放最后返回位、不立即检查,调用链就断了——这些不是风格问题,是 Go 工具链默认认定的“错误”。
gofmt 和 goimports 必须一起用,且保存时自动触发
很多人配了格式化却还是满屏 import 错乱,根本原因是只用了 gofmt。它只管缩进、括号、空格,不管 import 增删和分组;真正要靠的是 goimports——它是 gofmt 的超集。
-
goimports会自动按三组排序:标准库("fmt")、第三方("github.com/gin-gonic/gin")、本地模块("myapp/internal/cache") - 手动加空行分隔 import、手排包顺序、用下划线对齐路径,全会被覆盖,还可能引发 diff 冲突
- VS Code 用户必须确认:
"go.formatTool": "goimports"+"editor.formatOnSave": true+"editor.codeActionsOnSave": {"source.organizeImports": true} - 安装命令必须带
@latest:go install golang.org/x/tools/cmd/goimports@latest,否则 Go 1.21+ 会失败 - 文件顶部有
//go:build或//go:generate注释时,goimports会跳过整块,别忘了删掉再试
package 名必须小写、单数、与目录名严格一致
包名不是标签,是导入路径的语义终点。写错会导致符号无法解析、IDE 跳转失效、CI 构建失败。
- 目录
internal/auth/jwt→ 包名只能是jwt,不是authjwt、jwt_v2或Jwt - 禁止复数:
handlers改成handler,models改成model(除非你真导出多个 model 类型且天然是一组) - 禁止下划线和驼峰:
user_db、userDB都不行,就用user - 模块路径(
go mod init example.com/myapp)必须和最终 import 路径一致,不能用./或myapp - 私有仓库如
gitlab.internal/foo/bar,模块名就得设为gitlab.internal/foo/bar,否则go get解析失败
error 必须最后一个返回,且每个 if err != nil 都得终止流程
Go 不抛异常,error 是函数签名的一部分。把它放在中间、忽略后继续执行、或用 log.Fatal 替代传播,等于主动放弃调试能力。
立即学习“go语言免费学习笔记(深入)”;
- 函数签名必须是
func ReadFile(string) ([]byte, error),不是(error, []byte)或([]byte, string, error) -
if err != nil后没return、panic或封装后return,后续逻辑就运行在错误状态上 - 多层调用中只在最外层检查
err,中间层错误信息全丢;正确做法是每层都return fmt.Errorf("xxx: %w", err)保留错误链 - 定义可判断变量:
var ErrNotFound = errors.New("not found"),然后用errors.Is(err, ErrNotFound)判断,别用字符串匹配 - HTTP handler 中禁止
if err != nil { log.Printf(...) }后继续返回空结构体——该返回 404 或 500 就得返回
context 参数永远放第一位,且绝不塞进 struct
context.Context 不是可选参数,是 Go 生态里所有阻塞操作(DB 查询、HTTP 调用、定时器)的上下文载体。把它藏在 struct 里、或放到参数中间,会让中间件和超时控制彻底失效。
- 函数签名必须是
func DoSomething(ctx context.Context, id string) error,不是func DoSomething(id string, ctx context.Context) - 不要把
ctx存进 struct 字段:type Service struct { ctx context.Context }—— 这会让每个实例绑定固定生命周期,无法支持 per-request context - 传入
context.Background()或context.TODO()只应在启动初始化阶段;业务逻辑中必须由上游传入,比如 HTTP handler 里的r.Context() - 用
context.WithTimeout或context.WithCancel衍生子 context 时,记得调用cancel(),否则 goroutine 泄漏
最容易被忽略的其实是 import 分组和包名一致性——它们不报错,但会让 go list 解析失败、vendoring 异常、跨团队协作时满屏改名。工具链不会提醒你“这里包名错了”,只会让你在 go build 或 go test 时卡住几小时。


















