关于 golang
面向 Go 开发的 Skill,汇集 Google、Uber 和 Go 团队的生产实践建议,并更新至 Go 1.25。内容覆盖常见开发主题,另附专项子 Skill 和参考资料。
主要功能
- 01错误处理与并发指导
- 02命名、导入和数据结构实践
- 03测试与性能建议
- 04Lint、项目布局与安全
- 05Go 1.25 功能与常见陷阱
适用场景
- 01检查 Go 代码的可维护性
- 02为 Go 项目制定实践规范
- 03检查并发与错误处理方案
- 04准备 Go 测试与 Lint
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ saisudhir14/ claude-skills --skill golang安装后在 Claude Code 中输入/golang确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdLICENSEreferences/testing.mderror-handling.mdperformance.mdconcurrency.mdpatterns.mdgotchas.md详细介绍
Go 最佳实践
来自 Google、Uber 和 Go 团队的生产级模式。已针对 Go 1.25 更新。
子技能:
skills/go-error-handling、skills/go-concurrency、skills/go-testing、skills/go-performance、skills/go-code-review、skills/go-linting、skills/go-project-layout、skills/go-security。深入参考资料见references/。
核心原则
可读代码按以下优先级体现这些特征:
- 清晰:读者一眼就能看懂代码的目的和原因
- 简洁:以最简单的方式实现目标
- 精炼:信息量高,冗余少
- 可维护:易于正确修改
- 一致:与周围代码库保持一致
错误处理
完整指南:
skills/go-error-handling/SKILL.md| 参考资料:references/error-handling.md
- 返回错误,生产代码中绝不 panic
- 在调用方需要使用
errors.Is/errors.As时用%w包装;在边界处使用%v - 保持上下文简洁:使用
"new store: %w",而非"failed to create new store: %w" - 只处理一次错误:不要记录并返回同一个错误
- 错误字符串:小写,不带标点
- 错误流程缩进:优先处理错误,让正常流程保持最少缩进
- 多个互不相关的故障使用
errors.Join(Go 1.20+) - 哨兵错误:变量使用
Err前缀,类型使用Error后缀
if err != nil {
return fmt.Errorf("load config: %w", err)
}
并发
完整指南:
skills/go-concurrency/SKILL.md| 参考资料:references/concurrency.md
- 通道容量:使用 0(无缓冲)或 1;其他容量都需要说明理由
- 记录 goroutine 的生命周期:说明它们何时以及如何退出
- 对于会返回错误的 goroutine,优先使用
errgroup.Group,而非手动使用sync.WaitGroup - 优先使用同步函数:让调用方自行添加并发
- 互斥锁的零值:不要使用指针;不要嵌入公开结构体
- 类型化原子操作(Go 1.19+):
atomic.Int64、atomic.Bool、atomic.Pointer[T] sync.Map(Go 1.24+):在键集合互不重叠时性能显著提升
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)
for _, item := range items {
g.Go(func() error { return process(ctx, item) })
}
return g.Wait()
命名
- 始终使用混合大小写:不要使用下划线(使用
MaxLength,而非MAX_LENGTH) - 首字母缩写:大小写保持一致(使用
URL、ID、HTTP,而非Url、Id、Http) - 短变量:长度由作用域决定(循环中使用
i,全局变量使用DefaultTimeout) - 接收者名称:使用 1-2 个字母的缩写,各方法保持一致,绝不使用
this/self - 包名:使用小写单词,不要使用
util/common/misc - 避免重复:使用
http.Serve,而非http.HTTPServe;使用c.WriteTo,而非c.WriteConfigTo
指针接收者与值接收者
| 指针接收者 | 值接收者 |
|---|---|
| 会修改接收者 | 小型、不可变结构体 |
| 大型结构体 | 不会修改状态 |
| 包含 sync.Mutex | Map、func 或 chan |
| 与其他方法保持一致 | 基本类型 |
导入
import (
"context"
"fmt"
"github.com/google/uuid"
"golang.org/x/sync/errgroup"
"yourcompany/internal/config"
)
- 分为三组:标准库、外部库、内部库(组间用空行分隔)
- 仅在避免名称冲突时重命名
- 除存在循环依赖的测试文件外,不使用点导入
- 仅在 main 或测试中使用空白导入(
import _ "pkg")
模块管理(Go 1.24+)
使用工具指令在 go.mod 中跟踪工具依赖:
tool (
golang.org/x/tools/cmd/stringer
github.com/golangci/golangci-lint/cmd/golangci-lint
)
go get -tool golang.org/x/tools/cmd/stringer # add
go tool stringer -type=Status # run
go get tool # update all
go install tool # install to GOBIN
结构体
- 初始化时始终指定字段名(按位置赋值会在字段变更时出问题)
- 省略零值字段
- 不要在公开结构体中嵌入类型(会无意间暴露 API)
- 零值结构体使用
var:var user User
切片与映射
- 优先使用 nil 切片:
var t []string(仅在需要编码为 JSON[]时使用[]string{}) - 在边界处复制:使用
copy()或maps.Clone(),以防止修改 - 预分配:已知大小时使用
make([]T, 0, len(input)) - 使用
slices和maps包:slices.Sort、slices.Clone、maps.Clone、maps.Equal
泛型(Go 1.18+)
- 在为不同类型编写相同代码时使用
- 使用
cmp.Ordered或自定义约束来确保类型安全 - 泛型类型别名(Go 1.24+):
type Set[T comparable] = map[T]struct{} - 不要过度泛化:具体类型或接口足够时,就使用它们
func Filter[T any](s []T, pred func(T) bool) []T {
result := make([]T, 0, len(s))
for _, v := range s {
if pred(v) {
result = append(result, v)
}
}
return result
}
迭代器(Go 1.23+)
通过函数进行范围遍历,创建自定义迭代器:
func Backward[T any](s []T) func(yield func(int, T) bool) {
return func(yield func(int, T) bool) {
for i := len(s) - 1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}
for i, v := range Backward(items) {
fmt.Println(i, v)
}
字符串/字节迭代器(Go 1.24+):strings.Lines、strings.SplitSeq、strings.SplitAfterSeq
结构化日志(Go 1.21+)
slog.Info("user created", "id", userID, "email", email)
slog.With("service", "auth").Info("starting")
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})
- 在测试中使用
slog.DiscardHandler(Go 1.24+)来抑制日志 - 使用一致的键名,并通过
slog.Group将相关字段分组
性能
完整指南:
skills/go-performance/SKILL.md| 参考:references/performance.md
- 优先使用
strconv而非fmt:使用strconv.Itoa(n),而不是fmt.Sprintf("%d", n) - 避免重复进行
[]byte转换:转换一次并重复使用 - 预分配 map 容量:
make(map[string]int, len(items)) - 使用
strings.Builder,并通过Grow()进行拼接
测试
完整指南:
skills/go-testing/SKILL.md| 参考:references/testing.md
- 表驱动测试,子测试使用
t.Parallel() - 优先使用
go-cmp而非reflect.DeepEqual,以获得清晰的差异输出 - 提供实用的失败信息:包含输入、实际值和期望值
- 设置失败时使用
t.Fatal - 接口应由使用方定义,而不是提供方
- 使用
T.Context和T.Chdir(Go 1.24+) - 使用
b.Loop()(Go 1.24+):基准测试更简洁,无需b.ResetTimer() - 使用
synctest.Test(Go 1.25+):借助合成时间进行确定性的并发测试
资源管理(Go 1.24+)
runtime.AddCleanup:一个对象可执行多项清理操作,不会发生循环引用泄漏(取代SetFinalizer)weak.Pointer[T]:用于缓存、规范化和观察者的弱引用os.Root:限定范围的文件访问,可防止路径遍历攻击
模式
完整参考:
references/patterns.md
- 函数式选项:使用
WithTimeout(d)、WithLogger(l)配置构造函数 - 接口合规性:
var _ http.Handler = (*Handler)(nil) - 使用 Defer 进行清理:开销很小,安全性值得付出
- 优雅关闭:处理信号 +
srv.Shutdown(ctx) - 枚举从 1 开始:0 表示无效/未设置
- 使用
time.Duration:不要用原始整数表示时间 - 使用双值类型断言:
t, ok := i.(string),避免 panic - Context 放在首位:
func Process(ctx context.Context, ...) - 避免可变全局变量:使用依赖注入
- 避免
init():优先在main中显式初始化 - 使用
//go:embed(Go 1.16+):嵌入静态文件 - 字段标签:在要进行序列化的结构体上显式指定
json:"name" - 容器感知型 GOMAXPROCS(Go 1.25+):基于 cgroup 自动调优
常见注意事项
完整参考:
references/gotchas.md
| 注意事项 | 修复方法 |
|---|---|
| 循环变量捕获(Go 1.22 之前) | 已在 Go 1.22+ 中修复(每次迭代使用独立变量) |
| Defer 会立即计算参数 | 在闭包中捕获 |
| nil 接口与 nil 指针 | 显式返回 nil |
| 检查错误前使用结果 | 始终先检查 err(Go 1.25 会强制执行) |
| Map 迭代顺序 | 使用 slices.Sorted(maps.Keys(m)) 对键排序 |
| Slice 追加时共享底层数组 | 使用完整切片表达式 a[:2:2] |
Lint
完整指南:
skills/go-linting/SKILL.md
- 使用 golangci-lint 作为标准的 Linter 聚合工具
- 推荐的 Linter:errcheck、govet、staticcheck、revive、gosimple、goimports、errorlint、bodyclose
- 添加为工具依赖(Go 1.24+):
go get -tool github.com/golangci/golangci-lint/cmd/golangci-lint - 在 CI 中运行:在 GitHub Actions 中使用
golangci/golangci-lint-action - 谨慎抑制检查:优先修复问题,而不是添加
//nolint注释
项目布局
完整指南:
skills/go-project-layout/SKILL.md
cmd/:每个可执行文件对应一个子目录,保持main.go简洁internal/:私有包,由 Go 工具链强制执行访问限制- 避免使用
pkg/、src/、models/、utils/:根据用途命名包 - 扁平结构即可:小型项目不应有过深的目录树
- Dockerfile:多阶段构建,使用
CGO_ENABLED=0和 distroless 基础镜像
安全
完整指南:
skills/go-security/SKILL.md
- 参数化 SQL 查询:绝不插入用户输入
os.Root(Go 1.24+):限定文件访问范围,防止路径遍历- 在边界处验证:解码到类型化结构体,并验证字段
- 绝不硬编码或记录密钥:使用
Secret类型,并通过String()脱敏 - 仅使用标准加密库:使用
crypto/rand生成随机字节,使用bcrypt处理密码 - HTTP 超时:始终设置
ReadTimeout、WriteTimeout、IdleTimeout govulncheck:扫描依赖项,查找已知漏洞go test -race:始终在 CI 中使用竞态检测器运行
实验性功能(Go 1.25)
encoding/json/v2:使用GOEXPERIMENT=jsonv2启用。性能更佳,支持流式处理,以及按调用自定义封送器。
文档
- 注释应为完整句子,并以声明的名称开头
- 包注释:放在
package声明之前,中间不留空行