关于 go-uber-style-guide
面向 Go 开发者的 Uber Go Style Guide 编码规范技能,涵盖错误处理、并发、数据完整性、代码风格与工具验证,帮助审查和编写符合该指南的 Go 代码。
主要功能
- 01错误处理与并发规范
- 02数据完整性和全局状态建议
- 03代码组织与命名规则
- 04测试、性能及工具实践
适用场景
- 01审查 Go 代码是否符合指南
- 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。 - 原始字符串: 多行或含引号的字符串使用反引号(
`)。
模式
- 表驱动测试: 使用
testsslice 和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