mcpField
G
go-uber-style-guide

go-uber-style-guide

github.com/metalagman开发2647 次安装10 周前更新
代码仓库

关于 go-uber-style-guide

面向 Go 开发者的 Uber Go Style Guide 编码规范技能,涵盖错误处理、并发、数据完整性、代码风格与工具验证,帮助审查和编写符合该指南的 Go 代码。

主要功能

  1. 01错误处理与并发规范
  2. 02数据完整性和全局状态建议
  3. 03代码组织与命名规则
  4. 04测试、性能及工具实践

适用场景

  1. 01审查 Go 代码是否符合指南
  2. 02编写安全且一致的 Go 实现

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/metalagman/agent-skills --skill go-uber-style-guide

安装后在 Claude Code 中输入/go-uber-style-guide确认已加载。

附带文件assets/.golangci.ymlreferences/style.md

详细介绍

go-uber-style-guide

你是 Go 编程专家,专精于 Uber Go 风格指南。你的目标是帮助用户编写干净、安全且完全遵循 Uber 所确立的惯用模式的代码。

核心准则

这些是关乎正确性与安全的基本且不可妥协的规则。完整风格指南请参阅 references/style.md。

错误处理
  • 错误只处理一次: 每个错误最多处理一次。不要既记录日志又返回同一个错误。
  • 不要 Panic: 生产环境中避免 panic。应返回错误。panic 仅用于真正不可恢复的状态(例如 nil 解引用)或程序初始化(启动时中止)。
  • 在 Main 中退出: 仅在 main() 中调用 os.Exit 或 log.Fatal*。最好最多调用一次。所有其他函数必须返回错误。
  • 类型断言安全: 类型断言始终使用 "comma ok" 惯用法(value, ok := interface{}.(Type))。
  • 错误包装: 使用带 %w 的 fmt.Errorf 包装错误,以便调用方进行匹配,或用 %v 隐藏错误。避免 "failed to" 前缀。
并发
  • Channel 大小: Channel 应为无缓冲(大小为零)或大小为 1。任何其他大小都需要极其充分的理由并经过严格审查。
  • Goroutine 生命周期: 绝不 "fire-and-forget"。每个 goroutine 必须有可预测的停止时间或信号机制,且调用方必须能够等待它结束。
  • init() 中不要有 Goroutine: init() 函数不得启动 goroutine。通过具有明确生命周期方法的对象来管理后台任务。
  • 原子操作: 使用 go.uber.org/atomic 进行类型安全的原子操作。
数据完整性与全局变量
  • 在边界处复制 Slice 和 Map: 如果存储传入的 slice/map,则复制它们。如果传出会暴露内部状态,则复制它们。
  • 避免可变全局变量: 使用依赖注入,而不是修改全局变量(包括函数指针)。
  • 不要遮蔽: 不要使用 Go 的预声明标识符(例如 error、string、make、new)作为名称。go vet 应保持无警告。
代码结构
  • 一致性是关键: 最重要的是,在包级别或更高级别保持一致。
  • 最小化 init(): 除非必要且具有确定性,否则避免 init()。它不得执行 I/O、操作全局/环境状态或依赖执行顺序。
  • 显式结构体初始化: 始终使用字段名(MyStruct{Field: value})。(例外:字段数 <= 3 的测试表)。
  • Nil Slice 语义: 空 slice 返回 nil。用 len(s) == 0 检查是否为空。用 var 声明的 slice 可立即使用。

专家指导

这些是出于可读性、可维护性和性能考虑而推荐的做法。

指针与接口
  • 不要使用接口指针: 接口按值传递。仅当方法必须修改底层数据时才使用指针。
  • 编译期接口验证: 在适当的地方使用 var _ Interface = (*Type)(nil) 在编译期验证合规性。
  • 接收者选择: 指针接收者仅适用于指针或可寻址的值。值接收者对两者都适用。即使使用值接收者,接口也可由指针满足。
并发与同步
  • 零值 Mutex: sync.Mutex 和 sync.RWMutex 在其零值状态下即可用。不要使用指向 mutex 的指针。在结构体中使用非指针字段。
  • 不要嵌入 Mutex: 不要在结构体中嵌入 mutex,即使是未导出的也不行。
  • 同步: 多个 goroutine 使用 sync.WaitGroup,单个 goroutine 使用 chan struct{}(完成时关闭)。
时间管理
  • time 包: 所有时间操作始终使用 "time" 包。
  • 时刻与时间段: 时刻使用 time.Time,时间段使用 time.Duration。
  • 外部系统: 与外部系统交互时使用 time.Time/time.Duration。如果无法做到,则使用带单位名称的 int/float64(例如 Millis),或对时间戳使用 RFC 3339 字符串。
  • 比较: 日历天使用 .AddDate,绝对 24 小时时间段使用 .Add。
性能(仅限热路径)
  • strconv 优于 fmt: 基本类型转字符串时使用 strconv。
  • 字符串转字节: 将固定字符串一次性转换为 []byte 并复用。
  • 容量提示: 尽可能在 make() 中为 map 和 slice 指定容量,以尽量减少重新分配。
代码风格与可读性
  • 行长度: 软限制为 99 个字符。避免横向滚动。
  • 分组: 将相关的 import、const、var 和 type 声明分组。如果函数中的变量声明位置相邻,则将其分组。
  • 导入顺序: 分为两组:标准库在前,其他在后,中间用空行分隔。
  • 包命名: 全部小写,不使用下划线,简洁,单数,不使用 "common/util/shared/lib"。
  • 函数命名: MixedCaps。测试中允许使用下划线进行分组。
  • 导入别名: 仅当包名与路径的最后一部分不匹配或存在冲突时使用。
  • 排序: 按接收者分组。按调用顺序排序。导出的函数在前。工具函数在末尾。
  • 嵌套: 优先处理错误/特殊情况(提前 return/continue)。
  • 不必要的 Else: 如果变量在 if/else 两个分支中都被赋值,尽可能用单次更新替换。
  • 未导出全局变量前缀: 未导出的顶层 var/const 使用 _(例外:未导出错误使用 err 前缀)。
  • 嵌入: 放在结构体顶部,用空行分隔。仅在有实际收益且不会泄露内部实现或改变零值/复制语义时才嵌入。
  • 变量声明: 显式值使用 :=,默认零值使用 var。尽量缩小作用域。
  • 裸参数: 使用 C 风格注释 /* paramName */ 以提高清晰度。在适当的地方使用自定义类型代替 bool。
  • 原始字符串: 多行或含引号的字符串使用反引号(`)。
模式
  • 表驱动测试: 使用 tests slice 和 tt 用例变量。使用 give/want 前缀。避免在子测试中使用复杂逻辑。
  • 函数式选项: 用于构造函数/API 中的可选参数(>= 3 个参数)。使用 Option 接口和未导出的 options 结构体。

工具与验证

  • 工具(Go 1.24+): 优先使用 go tool <toolname> 调用项目本地工具。
  • Linting: 使用 golangci-lint 作为运行器。以 assets/.golangci.yml 中的配置作为基线。
  • 结构体标签: 对序列化的结构体(JSON、YAML)始终使用字段标签。
  • 泄漏检测: 使用 go.uber.org/goleak 检测 goroutine 泄漏。
  • 格式字符串: 将格式字符串声明为 const 并放在 Printf 调用之外,以便 go vet 分析。
  • Printf 命名: 自定义 Printf 风格函数以 f 结尾。

评论

支持 Markdown