go-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Best Practices Skill
Go最佳实践技能
Apply idiomatic Go patterns and best practices from Gopher Guides training materials.
应用源自Gopher Guides培训资料的Go语言惯用设计模式与最佳实践。
When Helping with Go Code
协助Go代码开发时的注意事项
Error Handling
错误处理
- Wrap errors with context: Use
fmt.Errorf("operation failed: %w", err) - Check errors immediately: Don't defer error checking
- Return errors, don't panic: Panics are for unrecoverable situations only
- Create sentinel errors for expected conditions:
var ErrNotFound = errors.New("not found") - Use errors.Is() and errors.As() for error comparison
go
// Good
if err != nil {
return fmt.Errorf("failed to process user %s: %w", userID, err)
}
// Avoid
if err != nil {
log.Fatal(err) // Don't panic on recoverable errors
}- 为错误添加上下文包装:使用
fmt.Errorf("操作失败: %w", err) - 立即检查错误:不要延迟错误检查
- 返回错误,不要panic:Panic仅用于不可恢复的情况
- 为预期条件创建哨兵错误:
var ErrNotFound = errors.New("not found") - 使用errors.Is()和errors.As()进行错误比较
go
// 推荐写法
if err != nil {
return fmt.Errorf("处理用户%s失败: %w", userID, err)
}
// 不推荐写法
if err != nil {
log.Fatal(err) // 不要在可恢复错误时panic
}Interface Design
接口设计
- Accept interfaces, return structs: Functions should accept interfaces but return concrete types
- Keep interfaces small: Prefer single-method interfaces
- Define interfaces at point of use: Not where the implementation lives
- Don't export interfaces unnecessarily: Only if users need to mock
go
// Good - interface defined by consumer
type Reader interface {
Read(p []byte) (n int, err error)
}
func ProcessData(r Reader) error { ... }
// Avoid - exporting implementation details
type Service interface {
Method1() error
Method2() error
Method3() error // Too many methods
}- 接受接口,返回结构体:函数应接受接口类型,但返回具体结构体类型
- 保持接口精简:优先选择单方法接口
- 在使用处定义接口:而非在实现处定义
- 不要不必要地导出接口:仅当用户需要进行mock时才导出
go
// 推荐写法 - 接口由使用者定义
type Reader interface {
Read(p []byte) (n int, err error)
}
func ProcessData(r Reader) error { ... }
// 不推荐写法 - 暴露实现细节
type Service interface {
Method1() error
Method2() error
Method3() error // 方法过多
}Concurrency
并发处理
- Don't communicate by sharing memory; share memory by communicating
- Use channels for coordination, mutexes for state
- Always pass context.Context as first parameter
- Use errgroup for coordinating goroutines
- Avoid goroutine leaks: Ensure goroutines can exit
go
// Good - using errgroup
g, ctx := errgroup.WithContext(ctx)
for _, item := range items {
item := item // capture loop variable
g.Go(func() error {
return process(ctx, item)
})
}
if err := g.Wait(); err != nil {
return err
}- 不要通过共享内存来通信;要通过通信来共享内存
- 使用通道进行协调,互斥锁用于状态管理
- 始终将context.Context作为第一个参数传递
- 使用errgroup协调节程
- 避免协程泄漏:确保协程能够退出
go
// 推荐写法 - 使用errgroup
g, ctx := errgroup.WithContext(ctx)
for _, item := range items {
item := item // 捕获循环变量
g.Go(func() error {
return process(ctx, item)
})
}
if err := g.Wait(); err != nil {
return err
}Testing
测试
- Use table-driven tests for multiple scenarios
- Call t.Parallel() for independent tests
- Use t.Helper() in test helpers
- Test behavior, not implementation
- Use testify for assertions when it improves readability
go
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive numbers", 2, 3, 5},
{"with zero", 5, 0, 5},
{"negative numbers", -2, -3, -5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}- 使用表驱动测试处理多场景
- 为独立测试调用t.Parallel()
- 在测试辅助函数中使用t.Helper()
- 测试行为,而非实现细节
- 当提升可读性时,使用testify进行断言
go
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"正数相加", 2, 3, 5},
{"含零相加", 5, 0, 5},
{"负数相加", -2, -3, -5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d, 预期值为 %d", tt.a, tt.b, got, tt.want)
}
})
}
}Package Organization
包组织
- Package names should be short and lowercase: not
useruserService - Avoid package-level state: Use dependency injection
- One package per directory: No multi-package directories
- internal/ for non-public packages: Prevents external imports
- 包名应简短且小写:使用而非
useruserService - 避免包级别的状态:使用依赖注入
- 一个目录对应一个包:不要在一个目录下放置多个包
- 使用internal/存放非公开包:防止被外部导入
Naming Conventions
命名规范
- Use MixedCaps or mixedCaps: Not underscores
- Acronyms should be consistent: ,
URL,HTTP(all caps for exported, all lower otherwise)ID - Short names for short scopes: for loop index,
ifor errorserr - Descriptive names for exports: not
ReadConfigRC
- 使用MixedCaps或mixedCaps命名:不要使用下划线
- 缩写词需保持一致:导出的缩写词全大写(如、
URL、HTTP),非导出的全小写ID - 短作用域使用短名称:循环索引用,错误变量用
ierr - 导出项使用描述性名称:使用而非
ReadConfigRC
Code Organization
代码组织
- Declare variables close to use
- Use defer for cleanup immediately after resource acquisition
- Group related declarations
- Order: constants, variables, types, functions
- 变量声明应靠近使用位置
- 资源获取后立即使用defer进行清理
- 相关声明放在一起
- 声明顺序:常量、变量、类型、函数
Anti-Patterns to Avoid
需避免的反模式
- Empty interface (or
interface{}): Use specific types when possibleany - Global state: Prefer dependency injection
- Naked returns: Always name what you're returning
- Stuttering: should be
user.UserServiceuser.Service - init() functions: Prefer explicit initialization
- Complex constructors: Use functional options pattern
- 空接口(或
interface{}):尽可能使用具体类型any - 全局状态:优先使用依赖注入
- 裸返回:始终明确返回值的名称
- 命名重复(Stuttering):应改为
user.UserServiceuser.Service - init()函数:优先选择显式初始化
- 复杂构造函数:使用函数式选项模式
When in Doubt
存疑时的参考
- Refer to Effective Go
- Check the Go standard library for examples
- Use and
go vetfor automated guidancestaticcheck
This skill is powered by Gopher Guides training materials. For comprehensive Go training, visit gopherguides.com.
- 参考Effective Go
- 查看Go标准库中的示例
- 使用和
go vet获取自动化指导staticcheck
本技能由Gopher Guides培训资料提供支持。如需全面的Go语言培训,请访问gopherguides.com。