mcpField
G
go-coding-standards

go-coding-standards

github.com/eduardo-sl开发83201 次安装2 周前更新
代码仓库

关于 go-coding-standards

go-coding-standards 是面向 Go 开发者和编码 Agent 的开发技能,涵盖惯用约定与代码质量检查,并提供文件组织、接收器和标签的详细参考。

主要功能

  1. 01导入分组与命名规则
  2. 02变量和结构体初始化指引
  3. 03嵌套、排序与行长约定
  4. 04接收器、时间和结构体标签指引
  5. 05goimports、go vet 和 staticcheck 检查

适用场景

  1. 01按 Go 惯用约定审查代码
  2. 02整理声明与结构体字段
  3. 03完成代码前执行质量检查

安装

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

$npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-coding-standards

安装后在 Claude Code 中输入/go-coding-standards确认已加载。

附带文件references/file-organization.mdreferences/receivers-and-tags.md

详细介绍

Go 编码规范

基于 Effective Go、Go Code Review Comments 和经过生产验证的惯用写法制定的 Go 惯例。 所有代码 MUST 通过 goimports、go vet 和 staticcheck(或 golangci-lint run)检查,且不得出现错误。

社区默认规范。若项目技能或 Agent 配置规则明确取代 go-coding-standards,则以该规则为准。

按需加载的详细参考资料:

  • references/file-organization.md — 声明分组、文件顺序、 拆分过长的函数签名。
  • references/receivers-and-tags.md — 接收者一致性和结构体标签 示例。

仅当以下摘要不够充分时,才阅读参考文件。

1. 导入排序

按以下顺序分组导入,并以空行分隔:

import (
    // 1. Standard library
    "context"
    "fmt"
    "log/slog"
    "net/http"

    // 2. External packages
    "github.com/gorilla/mux"

    // 3. Internal/project packages
    "github.com/myorg/myproject/internal/service"
)

绝不使用点导入。仅在解决命名冲突时使用别名。

2. 命名

命名由 go-naming 技能负责:包、重复包名前缀、首字母缩写、 getter、接收者、错误和测试名称。此处保留两条规则,因为它们 与声明有关:不得遮蔽预声明标识符(len、 new、error、min、max);当代码库采用此 惯例时,未导出的包级变量可以带 _ 前缀(_defaultTimeout)。

3. 变量声明

顶层

顶层声明使用 var。若类型与表达式相符,则不要指定类型:

// ✅ Good
var _defaultPort = 8080
var _logger = slog.Default()

// ❌ Bad — redundant type
var _defaultPort int = 8080
局部
  • 局部变量优先使用 :=。
  • 仅当有意且有意义地进行零值初始化时才使用 var。
// ✅ Good — zero value is meaningful
var buf bytes.Buffer

// ✅ Good — short declaration
name := getUserName()

4. 结构体初始化

始终使用字段名;除非零值字段具有特定含义,否则省略它们——位置 字面量在添加或重新排列字段时可能悄无声息地出错:

// ✅ Good
user := User{Name: "Alice", Email: "[email protected]"}

// ❌ Bad — positional
user := User{"Alice", "[email protected]", 0}

5. 减少嵌套

优先处理错误和特殊情况,并尽早返回,让正常路径保持在最左侧。 若 if 已经返回,则去掉对应的 else;优先先赋默认值,再按需覆盖:

// ✅ Good — guard clauses; no else
for _, v := range data {
    if !v.IsValid() {
        continue
    }
    if err := v.Process(); err != nil {
        return fmt.Errorf("process %s: %w", v.ID, err)
    }
}

a := 10 // default, then override
if condition {
    a = 20
}

6. 分组和排序

  • 将相关的 const 和 var 声明分组在同一个括号块中。
  • 文件顺序:常量和变量、构造函数、导出方法 (按重要性排序,而非按字母顺序)、未导出方法、辅助函数。
  • 方法紧接在其所属类型之后。

7. 行长度

软性上限为 99 个字符。将过长的函数签名拆分为每行一个参数, 并将右括号单独放在一行。示例: references/file-organization.md。

8. Defer 的使用

使用 defer 进行清理,以便在获取资源时清晰表达意图:

mu.Lock()
defer mu.Unlock()

f, err := os.Open(path)
if err != nil {
    return fmt.Errorf("open %s: %w", path, err)
}
defer f.Close() // read-only; check Close errors on files you write

9. 枚举

将零值保留给明确的 Unknown 哨兵值,避免未设置的值被误认为 真实状态:

type Status int

const (
    StatusUnknown Status = iota
    StatusActive
    StatusInactive
)

10. 正确使用 time 包

  • 时长使用 time.Duration,不要使用原始整数。
  • 时间点使用 time.Time。使用 time.Since(start),而非 time.Now().Sub(start)。
  • 当格式无法承载 time.Duration(JSON、YAML)时,在字段名中注明单位 — TimeoutSeconds int — 并在边界处进行转换。
// ✅ Good
func poll(interval time.Duration) { ... }
poll(10 * time.Second)

// ❌ Bad
func poll(intervalSecs int) { ... }
poll(10)

11. 接收者

每种类型选择一种接收者,并在所有方法中保持一致——混用接收者 会导致方法集随值的持有方式而变化。若有任何方法会修改值、结构体 包含 sync.Mutex 或其他不可复制字段、结构体较大,或者其他方法 已经需要指针,则使用指针接收者。小型不可变类型使用值接收者。 接收者命名为类型名后接一到两个字母(s *Server),且所有方法 保持一致,绝不使用 this 或 self。示例:references/receivers-and-tags.md。

12. 结构体标签

编译器不会检查标签,因此拼写错误不会报错。

  • 对跨越序列化边界的类型,所有字段都要添加标签,即使字段名已经匹配也一样。
  • 对任何绝不能离开进程的内容,使用 json:"-" —— 未导出的字段会被静默跳过,看起来像是疏忽。
  • omitempty 无法区分“缺失”和“零值”;需要区分时请使用指针或包装类型。omitempty 永远不会省略结构体;对于 time.Time 等字段,请使用 omitzero(Go 1.24+),它会遵循 Go 的零值和 IsZero()。
  • 运行 go vet —— 其 structtag 检查可发现格式错误的标签。

13. 函数形式

  • 条件包含三个或更多操作数时,为各部分命名(isOwner、isAdmin),让 if 语句读起来就是业务规则。
  • ctx context.Context 放在首位,其后是输入参数,再后是选项。
  • 参数超过约四个,或多个同类型参数相邻时,应使用选项结构体或函数式选项 —— 两者均由 go-design-patterns skill 负责。
  • 仅在函数短到可以看清所有返回值时使用裸返回。

验证清单

在认为代码已完成前:

  1. goimports 运行无误
  2. go vet ./... 通过
  3. golangci-lint run 通过(如果已配置)
  4. 没有遮蔽内置标识符
  5. 所有导入都已正确分组和排序
  6. 结构体初始化使用字段名
  7. 没有不必要的嵌套或 else 代码块
  8. 同一类型的所有方法接收者类型保持一致
  9. 每个序列化结构体字段都带有显式标签

评论

支持 Markdown