mcpField
G
golang

golang

github.com/saisudhir14开发9525 次安装30 周前更新
代码仓库

关于 golang

面向 Go 开发的 Skill,汇集 Google、Uber 和 Go 团队的生产实践建议,并更新至 Go 1.25。内容覆盖常见开发主题,另附专项子 Skill 和参考资料。

主要功能

  1. 01错误处理与并发指导
  2. 02命名、导入和数据结构实践
  3. 03测试与性能建议
  4. 04Lint、项目布局与安全
  5. 05Go 1.25 功能与常见陷阱

适用场景

  1. 01检查 Go 代码的可维护性
  2. 02为 Go 项目制定实践规范
  3. 03检查并发与错误处理方案
  4. 04准备 Go 测试与 Lint

安装

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

$npx skills add https://github.com/saisudhir14/claude-skills --skill golang

安装后在 Claude Code 中输入/golang确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
LICENSE
references/
testing.md
error-handling.md
performance.md
concurrency.md
patterns.md
gotchas.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/。

核心原则

可读代码按以下优先级体现这些特征:

  1. 清晰:读者一眼就能看懂代码的目的和原因
  2. 简洁:以最简单的方式实现目标
  3. 精炼:信息量高,冗余少
  4. 可维护:易于正确修改
  5. 一致:与周围代码库保持一致

错误处理

完整指南: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.MutexMap、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 声明之前,中间不留空行

参考资料

  1. Google Go 风格指南
  2. Uber Go 风格指南
  3. Effective Go
  4. Go 代码审查注释
  5. Go 1.23 发行说明
  6. Go 1.24 发行说明
  7. Go 1.25 发行说明

评论

支持 Markdown