golang-code-style

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-code-style
skill takes precedence.
社区默认规范。若公司内部有明确替代
samber/cc-skills-golang@golang-code-style
的技能规范,以公司规范为准。

Go Code Style

Go代码风格

Style rules that require human judgment — linters handle formatting, this skill handles clarity. For naming see
samber/cc-skills-golang@golang-naming
skill; for design patterns see
samber/cc-skills-golang@golang-design-patterns
skill; for struct/interface design see
samber/cc-skills-golang@golang-structs-interfaces
skill.
"Clear is better than clever." — Go Proverbs
When ignoring a rule, add a comment to the code.
需要人工判断的风格规则——格式化由Linter处理,本规范专注于代码的清晰度。命名规范请参考
samber/cc-skills-golang@golang-naming
技能;设计模式请参考
samber/cc-skills-golang@golang-design-patterns
技能;结构体/接口设计请参考
samber/cc-skills-golang@golang-structs-interfaces
技能。
"清晰胜于机巧。" —— Go谚语
若需忽略某条规则,请在代码中添加注释说明。

Line Length & Breaking

行长度与换行

No rigid line limit, but lines beyond ~120 characters MUST be broken. Break at semantic boundaries, not arbitrary column counts. Function calls with 4+ arguments MUST use one argument per line — even when the prompt asks for single-line code:
go
// Good — each argument on its own line, closing paren separate
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
    handleUsers(
        w,
        r,
        serviceName,
        cfg,
        logger,
        authMiddleware,
    )
})
When a function signature is too long, the real fix is often fewer parameters (use an options struct) rather than better line wrapping. For multi-line signatures, put each parameter on its own line.
无硬性行长度限制,但超过约120字符的代码行必须换行。需在语义边界处换行,而非随意按列数拆分。包含4个及以上参数的函数调用,必须每行一个参数——即使要求单行代码时也需遵守:
go
// 规范写法——每个参数单独一行,闭合括号另起一行
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
    handleUsers(
        w,
        r,
        serviceName,
        cfg,
        logger,
        authMiddleware,
    )
})
若函数签名过长,最优解决方案通常是减少参数数量(使用选项结构体),而非优化换行方式。对于多行签名,需将每个参数单独放在一行。

Variable Declarations

变量声明

SHOULD use
:=
for non-zero values,
var
for zero-value initialization. The form signals intent:
var
means "this starts at zero."
go
var count int              // zero value, set later
name := "default"          // non-zero, := is appropriate
var buf bytes.Buffer       // zero value is ready to use
非零值初始化应使用
:=
,零值初始化应使用
var
。不同形式传递不同意图:
var
表示“该变量初始为零值”。
go
var count int              // 零值,后续赋值
name := "default"          // 非零值,使用:=更合适
var buf bytes.Buffer       // 零值可直接使用

Slice & Map Initialization

切片与Map初始化

Slices and maps MUST be initialized explicitly, never nil. Nil maps panic on write; nil slices serialize to
null
in JSON (vs
[]
for empty slices), surprising API consumers.
go
users := []User{}                       // always initialized
m := map[string]int{}                   // always initialized
users := make([]User, 0, len(ids))      // preallocate when capacity is known
m := make(map[string]int, len(items))   // preallocate when size is known
Do not preallocate speculatively —
make([]T, 0, 1000)
wastes memory when the common case is 10 items.
切片和Map必须显式初始化,绝不能为nil。Nil Map写入时会触发panic;Nil切片在JSON中会序列化为
null
(而空切片会序列化为
[]
),这会给API调用者带来意外问题。
go
users := []User{}                       // 始终显式初始化
m := map[string]int{}                   // 始终显式初始化
users := make([]User, 0, len(ids))      // 已知容量时预分配空间
m := make(map[string]int, len(items))   // 已知大小時预分配空间
不要盲目预分配——当常见场景仅需10个元素时,
make([]T, 0, 1000)
会浪费内存。

Composite Literals

复合字面量

Composite literals MUST use field names — positional fields break when the type adds or reorders fields:
go
srv := &http.Server{
    Addr:         ":8080",
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
}
复合字面量必须使用字段名——使用位置字段的话,当类型新增或重排字段时会导致代码失效:
go
srv := &http.Server{
    Addr:         ":8080",
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
}

Control Flow

控制流

Reduce Nesting

减少嵌套

Errors and edge cases MUST be handled first (early return). Keep the happy path at minimal indentation:
go
func process(data []byte) (*Result, error) {
    if len(data) == 0 {
        return nil, errors.New("empty data")
    }

    parsed, err := parse(data)
    if err != nil {
        return nil, fmt.Errorf("parsing: %w", err)
    }

    return transform(parsed), nil
}
错误和边缘情况必须优先处理(提前返回)。让主逻辑路径保持最小缩进:
go
func process(data []byte) (*Result, error) {
    if len(data) == 0 {
        return nil, errors.New("empty data")
    }

    parsed, err := parse(data)
    if err != nil {
        return nil, fmt.Errorf("parsing: %w", err)
    }

    return transform(parsed), nil
}

Eliminate Unnecessary
else

消除不必要的
else

When the
if
body ends with
return
/
break
/
continue
, the
else
MUST be dropped. Use default-then-override for simple assignments — assign a default, then override with independent conditions or a
switch
:
go
// Good — default-then-override with switch (cleanest for mutually exclusive overrides)
level := slog.LevelInfo
switch {
case debug:
    level = slog.LevelDebug
case verbose:
    level = slog.LevelWarn
}

// Bad — else-if chain hides that there's a default
if debug {
    level = slog.LevelDebug
} else if verbose {
    level = slog.LevelWarn
} else {
    level = slog.LevelInfo
}
if
语句块以
return
/
break
/
continue
结尾时,必须删除
else
。简单赋值场景使用“默认值+覆盖”模式——先赋值默认值,再通过独立条件或
switch
覆盖:
go
// 规范写法——使用switch实现默认值+覆盖(互斥条件下最简洁)
level := slog.LevelInfo
switch {
case debug:
    level = slog.LevelDebug
case verbose:
    level = slog.LevelWarn
}

// 不规范写法——else-if链隐藏了默认值的存在
if debug {
    level = slog.LevelDebug
} else if verbose {
    level = slog.LevelWarn
} else {
    level = slog.LevelInfo
}

Complex Conditions & Init Scope

复杂条件与初始化作用域

When an
if
condition has 3+ operands, MUST extract into named booleans — a wall of
||
is unreadable and hides business logic. Keep expensive checks inline for short-circuit benefit. Details
go
// Good — named booleans make intent clear
isAdmin := user.Role == RoleAdmin
isOwner := resource.OwnerID == user.ID
isPublicVerified := resource.IsPublic && user.IsVerified
if isAdmin || isOwner || isPublicVerified || permissions.Contains(PermOverride) {
    allow()
}
Scope variables to
if
blocks when only needed for the check:
go
if err := validate(input); err != nil {
    return err
}
if
条件包含3个及以上操作数时,必须提取为命名布尔变量——一整行的
||
难以阅读,且会隐藏业务逻辑。将开销较大的检查内联以利用短路求值特性。详情
go
// 规范写法——命名布尔变量明确表达意图
isAdmin := user.Role == RoleAdmin
isOwner := resource.OwnerID == user.ID
isPublicVerified := resource.IsPublic && user.IsVerified
if isAdmin || isOwner || isPublicVerified || permissions.Contains(PermOverride) {
    allow()
}
仅当变量仅用于条件检查时,将其作用域限定在
if
块内:
go
if err := validate(input); err != nil {
    return err
}

Switch Over If-Else Chains

用Switch替代If-Else链

When comparing the same variable multiple times, prefer
switch
:
go
switch status {
case StatusActive:
    activate()
case StatusInactive:
    deactivate()
default:
    panic(fmt.Sprintf("unexpected status: %d", status))
}
当多次比较同一个变量时,优先使用
switch
go
switch status {
case StatusActive:
    activate()
case StatusInactive:
    deactivate()
default:
    panic(fmt.Sprintf("unexpected status: %d", status))
}

Function Design

函数设计

  • Functions SHOULD be short and focused — one function, one job.
  • Functions SHOULD have ≤4 parameters. Beyond that, use an options struct (see
    samber/cc-skills-golang@golang-design-patterns
    skill).
  • Parameter order:
    context.Context
    first, then inputs, then output destinations.
  • Naked returns help in very short functions (1-3 lines) where return values are obvious, but become confusing when readers must scroll to find what's returned — name returns explicitly in longer functions.
go
func FetchUser(ctx context.Context, id string) (*User, error)
func SendEmail(ctx context.Context, msg EmailMessage) error  // grouped into struct
  • 函数应短小且聚焦——一个函数只做一件事。
  • 函数参数应**≤4个**。超过4个时,使用选项结构体(参考
    samber/cc-skills-golang@golang-design-patterns
    技能)。
  • 参数顺序
    context.Context
    放在首位,其次是输入参数,最后是输出目标。
  • 裸返回在极短函数(1-3行)中很有用,此时返回值一目了然;但在长函数中会让读者需要滚动查找返回内容,因此长函数应显式命名返回值。
go
func FetchUser(ctx context.Context, id string) (*User, error)
func SendEmail(ctx context.Context, msg EmailMessage) error  // 参数分组为结构体

Prefer
range
for Iteration

优先使用
range
迭代

SHOULD use
range
over index-based loops. Use
range n
(Go 1.22+) for simple counting.
go
for _, user := range users {
    process(user)
}
应使用
range
替代基于索引的循环。简单计数场景可使用
range n
(Go 1.22+)。
go
for _, user := range users {
    process(user)
}

Value vs Pointer Arguments

值类型与指针类型参数

Pass small types (
string
,
int
,
bool
,
time.Time
) by value. Use pointers when mutating, for large structs (~128+ bytes), or when nil is meaningful. Details
小类型(
string
int
bool
time.Time
)按值传递。当需要修改参数、参数为大型结构体(约128字节以上)或nil有特殊含义时,使用指针传递。详情

Code Organization Within Files

文件内代码组织

  • Group related declarations: type, constructor, methods together
  • Order: package doc, imports, constants, types, constructors, methods, helpers
  • One primary type per file when it has significant methods
  • Blank imports (
    _ "pkg"
    ) register side effects (init functions). Restricting them to
    main
    and test packages makes side effects visible at the application root, not hidden in library code
  • Dot imports pollute the namespace and make it impossible to tell where a name comes from — never use in library code
  • Unexport aggressively — you can always export later; unexporting is a breaking change
  • 相关声明分组:类型、构造函数、方法放在一起
  • 顺序:包文档、导入、常量、类型、构造函数、方法、辅助函数
  • 当某个类型有大量方法时,一个文件对应一个核心类型
  • 空白导入
    _ "pkg"
    )用于注册副作用(init函数)。应将其限制在
    main
    包和测试包中,让副作用在应用根目录可见,而非隐藏在库代码中
  • 点导入会污染命名空间,且无法区分名称来源——库代码中绝不允许使用
  • 尽量隐藏内部实现——后续可随时对外暴露;但对外暴露后再隐藏属于破坏性变更

String Handling

字符串处理

Use
strconv
for simple conversions (faster),
fmt.Sprintf
for complex formatting. Use
%q
in error messages to make string boundaries visible. Use
strings.Builder
for loops,
+
for simple concatenation.
简单转换使用
strconv
(速度更快),复杂格式化使用
fmt.Sprintf
。错误信息中使用
%q
以清晰显示字符串边界。循环中拼接字符串使用
strings.Builder
,简单拼接使用
+

Type Conversions

类型转换

Prefer explicit, narrow conversions. Use generics over
any
when a concrete type will do:
go
func Contains[T comparable](slice []T, target T) bool  // not []any
优先使用显式、窄范围的类型转换。当可以使用具体类型时,用泛型替代
any
go
func Contains[T comparable](slice []T, target T) bool  // 不要用[]any

Philosophy

设计理念

  • "A little copying is better than a little dependency"
  • Use
    slices
    and
    maps
    standard packages
    ; for filter/group-by/chunk, use
    github.com/samber/lo
  • "Reflection is never clear" — avoid
    reflect
    unless necessary
  • Don't abstract prematurely — extract when the pattern is stable
  • Minimize public surface — every exported name is a commitment
  • “少量复制优于少量依赖”
  • 使用标准库
    slices
    maps
    ;过滤/分组/分块场景使用
    github.com/samber/lo
  • “反射绝不清晰”——除非必要,否则避免使用
    reflect
  • 不要过早抽象——当模式稳定后再提取抽象
  • 最小化公共接口——每个对外暴露的名称都是一种承诺

Parallelizing Code Style Reviews

代码风格评审并行化

When reviewing code style across a large codebase, use up to 5 parallel sub-agents (via the Agent tool), each targeting an independent style concern (e.g. control flow, function design, variable declarations, string handling, code organization).
当在大型代码库中评审代码风格时,可使用最多5个并行子Agent(通过Agent工具),每个Agent负责一个独立的风格检查点(例如控制流、函数设计、变量声明、字符串处理、代码组织)。

Enforce with Linters

用Linter强制执行

Many rules are enforced automatically:
gofmt
,
gofumpt
,
goimports
,
gocritic
,
revive
,
wsl_v5
. → See the
samber/cc-skills-golang@golang-linter
skill.
许多规则可自动强制执行:
gofmt
gofumpt
goimports
gocritic
revive
wsl_v5
。→ 参考
samber/cc-skills-golang@golang-linter
技能。

Cross-References

交叉参考

  • → See the
    samber/cc-skills-golang@golang-naming
    skill for identifier naming conventions
  • → See the
    samber/cc-skills-golang@golang-structs-interfaces
    skill for pointer vs value receivers, interface design
  • → See the
    samber/cc-skills-golang@golang-design-patterns
    skill for functional options, builders, constructors
  • → See the
    samber/cc-skills-golang@golang-linter
    skill for automated formatting enforcement
  • → 标识符命名规范请参考
    samber/cc-skills-golang@golang-naming
    技能
  • → 指针与值接收器、接口设计请参考
    samber/cc-skills-golang@golang-structs-interfaces
    技能
  • → 函数选项、构建器、构造函数请参考
    samber/cc-skills-golang@golang-design-patterns
    技能
  • → 自动化格式化强制执行请参考
    samber/cc-skills-golang@golang-linter
    技能