go-coding-standards

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Coding Standards

Go编码标准

Idiomatic Go conventions grounded in Effective Go, Go Code Review Comments, and production-proven idioms. All code MUST pass
goimports
,
golint
, and
go vet
without errors.
基于《Effective Go》、《Go代码评审注释》以及经生产验证的惯用写法制定的地道Go编码约定。 所有代码必须能通过
goimports
golint
go vet
检查且无错误。

1. Import Ordering

1. 导入排序

Group imports in this order, separated by blank lines:
go
import (
    // 1. Standard library
    "context"
    "fmt"
    "net/http"

    // 2. External packages
    "github.com/gorilla/mux"
    "go.uber.org/zap"

    // 3. Internal/project packages
    "github.com/myorg/myproject/internal/service"
)
NEVER use dot imports. Use aliasing only to resolve conflicts.
导入按以下顺序分组,组之间用空行分隔:
go
import (
    // 1. 标准库
    "context"
    "fmt"
    "net/http"

    // 2. 外部包
    "github.com/gorilla/mux"
    "go.uber.org/zap"

    // 3. 内部/项目包
    "github.com/myorg/myproject/internal/service"
)
严禁使用点导入。仅在需要解决命名冲突时使用别名。

2. Naming Conventions

2. 命名规范

Packages

  • Short, lowercase, single-word names. No underscores, no camelCase.
  • Name should describe what the package provides, not what it contains.
  • Avoid generic names:
    util
    ,
    common
    ,
    helpers
    ,
    misc
    ,
    base
    .
  • 简短、小写、单字名称。禁止使用下划线或驼峰式命名。
  • 包名应描述其提供的功能,而非包含的内容。
  • 避免使用通用名称:
    util
    common
    helpers
    misc
    base

Functions & Methods

函数与方法

  • MixedCaps (exported) or mixedCaps (unexported). No underscores except in test files.
  • Getters: use
    Name()
    , NOT
    GetName()
    . Setters: use
    SetName()
    .
  • Constructors:
    NewFoo()
    returns
    *Foo
    . If only one type in package:
    New()
    .
  • 导出函数/方法使用MixedCaps(首字母大写),未导出的使用mixedCaps(首字母小写)。除测试文件外,禁止使用下划线。
  • Getter方法:使用
    Name()
    ,而非
    GetName()
    。Setter方法:使用
    SetName()
  • 构造函数:
    NewFoo()
    返回
    *Foo
    。若包中仅有一种类型,可直接使用
    New()

Variables

变量

  • Short names in tight scopes:
    i
    ,
    n
    ,
    err
    ,
    ctx
    .
  • Descriptive names for wider scopes:
    userCount
    ,
    retryTimeout
    .
  • Prefix unexported package-level globals with
    _
    :
    var _defaultTimeout = 5 * time.Second
    .
  • Do NOT shadow built-in identifiers (
    error
    ,
    len
    ,
    cap
    ,
    new
    ,
    make
    ,
    close
    ).
  • 窄作用域内使用短名称:
    i
    n
    err
    ctx
  • 宽作用域内使用描述性名称:
    userCount
    retryTimeout
  • 未导出的包级全局变量前缀加
    _
    var _defaultTimeout = 5 * time.Second
  • 禁止遮蔽内置标识符(
    error
    len
    cap
    new
    make
    close
    )。

Interfaces

接口

  • Single-method interfaces: method name +
    -er
    suffix (
    Reader
    ,
    Writer
    ,
    Closer
    ).
  • Define interfaces where they are consumed, not where they are implemented.
  • 单方法接口:方法名加
    -er
    后缀(
    Reader
    Writer
    Closer
    )。
  • 在接口被使用的位置定义,而非在被实现的位置。

3. Variable Declarations

3. 变量声明

Top-level

顶层声明

Use
var
for top-level declarations. Do NOT specify type when it matches the expression:
go
// ✅ Good
var _defaultPort = 8080
var _logger = zap.NewNop()

// ❌ Bad — redundant type
var _defaultPort int = 8080
使用
var
进行顶层声明。当表达式类型与变量类型匹配时,无需指定类型:
go
// ✅ 推荐
var _defaultPort = 8080
var _logger = zap.NewNop()

// ❌ 不推荐——类型冗余
var _defaultPort int = 8080

Local

局部声明

  • Prefer
    :=
    for local variables.
  • Use
    var
    only when zero-value initialization is intentional and meaningful.
go
// ✅ Good — zero value is meaningful
var buf bytes.Buffer

// ✅ Good — short declaration
name := getUserName()
  • 优先使用
    :=
    声明局部变量。
  • 仅当零值初始化是有意且有意义时,才使用
    var
go
// ✅ 推荐——零值有意义
var buf bytes.Buffer

// ✅ 推荐——短声明
name := getUserName()

4. Struct Initialization

4. 结构体初始化

ALWAYS use field names. Never rely on positional initialization:
go
// ✅ Good
user := User{
    Name:  "Alice",
    Email: "alice@example.com",
    Age:   30,
}

// ❌ Bad — positional, breaks on field reordering
user := User{"Alice", "alice@example.com", 30}
Omit zero-value fields unless clarity requires them:
go
// ✅ Good — zero values omitted
user := User{
    Name: "Alice",
}
始终使用字段名初始化。绝不要依赖位置初始化:
go
// ✅ 推荐
user := User{
    Name:  "Alice",
    Email: "alice@example.com",
    Age:   30,
}

// ❌ 不推荐——位置初始化,字段顺序变更时会出错
user := User{"Alice", "alice@example.com", 30}
除非为了清晰性,否则省略零值字段:
go
// ✅ 推荐——省略零值字段
user := User{
    Name: "Alice",
}

5. Reduce Nesting

5. 减少嵌套

Handle errors and special cases first with early returns. Reduce indentation levels:
go
// ✅ Good — early return
func process(data []Item) error {
    for _, v := range data {
        if !v.IsValid() {
            log.Printf("invalid item: %v", v)
            continue
        }

        if err := v.Process(); err != nil {
            return err
        }

        v.Send()
    }
    return nil
}
Eliminate unnecessary
else
blocks:
go
// ✅ Good
a := 10
if condition {
    a = 20
}

// ❌ Bad
var a int
if condition {
    a = 20
} else {
    a = 10
}
优先通过提前返回处理错误和特殊情况,减少缩进层级:
go
// ✅ 推荐——提前返回
func process(data []Item) error {
    for _, v := range data {
        if !v.IsValid() {
            log.Printf("invalid item: %v", v)
            continue
        }

        if err := v.Process(); err != nil {
            return err
        }

        v.Send()
    }
    return nil
}
消除不必要的
else
块:
go
// ✅ 推荐
a := 10
if condition {
    a = 20
}

// ❌ 不推荐
var a int
if condition {
    a = 20
} else {
    a = 10
}

6. Grouping and Ordering

6. 分组与排序

Group related declarations:
go
const (
    _defaultPort    = 8080
    _defaultTimeout = 30 * time.Second
)

var (
    _validTypes  = map[string]bool{"json": true, "xml": true}
    _defaultUser = User{Name: "guest"}
)
Function ordering within a file:
  1. Constants and variables
  2. New()
    / constructor functions
  3. Exported methods (sorted by importance, not alphabetically)
  4. Unexported methods
  5. Helper functions
Receiver methods should appear immediately after the type declaration.
将相关声明分组:
go
const (
    _defaultPort    = 8080
    _defaultTimeout = 30 * time.Second
)

var (
    _validTypes  = map[string]bool{"json": true, "xml": true}
    _defaultUser = User{Name: "guest"}
)
文件内函数排序:
  1. 常量与变量
  2. New()
    /构造函数
  3. 导出方法(按重要性排序,而非字母顺序)
  4. 未导出方法
  5. 辅助函数
接收器方法应紧跟在类型声明之后。

7. Line Length

7. 行长度

Soft limit of 99 characters. Break long function signatures:
go
func (s *Store) CreateUser(
    ctx context.Context,
    name string,
    email string,
    opts ...CreateOption,
) (*User, error) {
软限制为99个字符。长函数签名需换行:
go
func (s *Store) CreateUser(
    ctx context.Context,
    name string,
    email string,
    opts ...CreateOption,
) (*User, error) {

8. Defer Usage

8. Defer使用

Use
defer
for cleanup. It makes intent clear at the point of acquisition:
go
mu.Lock()
defer mu.Unlock()

f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()
使用
defer
进行清理操作。在资源获取处明确意图:
go
mu.Lock()
defer mu.Unlock()

f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()

9. Enums

9. 枚举

Start enums at 1 (or use explicit sentinel) so zero-value signals "unset":
go
type Status int

const (
    StatusUnknown Status = iota
    StatusActive
    StatusInactive
)
枚举从1开始(或使用显式标记值),使零值表示“未设置”:
go
type Status int

const (
    StatusUnknown Status = iota
    StatusActive
    StatusInactive
)

10. Use
time
Package Properly

10. 正确使用
time

  • Use
    time.Duration
    for durations, NOT raw integers.
  • Use
    time.Time
    for instants. Use
    time.Since(start)
    instead of
    time.Now().Sub(start)
    .
  • External APIs: accept
    int
    or
    float64
    and convert internally.
go
// ✅ Good
func poll(interval time.Duration) { ... }
poll(10 * time.Second)

// ❌ Bad
func poll(intervalSecs int) { ... }
poll(10)
  • 持续时间使用
    time.Duration
    类型,而非原始整数。
  • 时间点使用
    time.Time
    类型。使用
    time.Since(start)
    替代
    time.Now().Sub(start)
  • 外部API:接收
    int
    float64
    类型,在内部进行转换。
go
// ✅ 推荐
func poll(interval time.Duration) { ... }
poll(10 * time.Second)

// ❌ 不推荐
func poll(intervalSecs int) { ... }
poll(10)

Verification Checklist

验证检查清单

Before considering code complete:
  1. goimports
    runs clean
  2. go vet ./...
    passes
  3. golangci-lint run
    passes (if configured)
  4. No shadowed built-in identifiers
  5. All imports properly grouped and ordered
  6. Struct initializations use field names
  7. No unnecessary nesting or else blocks
代码完成前需确认:
  1. goimports
    运行无问题
  2. go vet ./...
    检查通过
  3. golangci-lint run
    检查通过(若已配置)
  4. 无遮蔽的内置标识符
  5. 所有导入已正确分组和排序
  6. 结构体初始化使用字段名
  7. 无不必要的嵌套或else块