go-coding-standards
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Coding Standards
Go编码标准
Idiomatic Go conventions grounded in Effective Go, Go Code Review Comments, and production-proven idioms.
All code MUST pass , , and without errors.
goimportsgolintgo vet基于《Effective Go》、《Go代码评审注释》以及经生产验证的惯用写法制定的地道Go编码约定。
所有代码必须能通过、和检查且无错误。
goimportsgolintgo vet1. 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 , NOT
Name(). Setters: useGetName().SetName() - Constructors: returns
NewFoo(). If only one type in package:*Foo.New()
- 导出函数/方法使用MixedCaps(首字母大写),未导出的使用mixedCaps(首字母小写)。除测试文件外,禁止使用下划线。
- Getter方法:使用,而非
Name()。Setter方法:使用GetName()。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 + suffix (
-er,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 for top-level declarations. Do NOT specify type when it matches the expression:
vargo
// ✅ Good
var _defaultPort = 8080
var _logger = zap.NewNop()
// ❌ Bad — redundant type
var _defaultPort int = 8080使用进行顶层声明。当表达式类型与变量类型匹配时,无需指定类型:
vargo
// ✅ 推荐
var _defaultPort = 8080
var _logger = zap.NewNop()
// ❌ 不推荐——类型冗余
var _defaultPort int = 8080Local
局部声明
- Prefer for local variables.
:= - Use only when zero-value initialization is intentional and meaningful.
var
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 blocks:
elsego
// ✅ 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
}消除不必要的块:
elsego
// ✅ 推荐
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:
- Constants and variables
- / constructor functions
New() - Exported methods (sorted by importance, not alphabetically)
- Unexported methods
- 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"}
)文件内函数排序:
- 常量与变量
- /构造函数
New() - 导出方法(按重要性排序,而非字母顺序)
- 未导出方法
- 辅助函数
接收器方法应紧跟在类型声明之后。
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 for cleanup. It makes intent clear at the point of acquisition:
defergo
mu.Lock()
defer mu.Unlock()
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()使用进行清理操作。在资源获取处明确意图:
defergo
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
time10. 正确使用time
包
time- Use for durations, NOT raw integers.
time.Duration - Use for instants. Use
time.Timeinstead oftime.Since(start).time.Now().Sub(start) - External APIs: accept or
intand convert internally.float64
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:
- runs clean
goimports - passes
go vet ./... - passes (if configured)
golangci-lint run - No shadowed built-in identifiers
- All imports properly grouped and ordered
- Struct initializations use field names
- No unnecessary nesting or else blocks
代码完成前需确认:
- 运行无问题
goimports - 检查通过
go vet ./... - 检查通过(若已配置)
golangci-lint run - 无遮蔽的内置标识符
- 所有导入已正确分组和排序
- 结构体初始化使用字段名
- 无不必要的嵌套或else块