go-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go 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:
    user
    not
    userService
  • Avoid package-level state: Use dependency injection
  • One package per directory: No multi-package directories
  • internal/ for non-public packages: Prevents external imports
  • 包名应简短且小写:使用
    user
    而非
    userService
  • 避免包级别的状态:使用依赖注入
  • 一个目录对应一个包:不要在一个目录下放置多个包
  • 使用internal/存放非公开包:防止被外部导入

Naming Conventions

命名规范

  • Use MixedCaps or mixedCaps: Not underscores
  • Acronyms should be consistent:
    URL
    ,
    HTTP
    ,
    ID
    (all caps for exported, all lower otherwise)
  • Short names for short scopes:
    i
    for loop index,
    err
    for errors
  • Descriptive names for exports:
    ReadConfig
    not
    RC
  • 使用MixedCaps或mixedCaps命名:不要使用下划线
  • 缩写词需保持一致:导出的缩写词全大写(如
    URL
    HTTP
    ID
    ),非导出的全小写
  • 短作用域使用短名称:循环索引用
    i
    ,错误变量用
    err
  • 导出项使用描述性名称:使用
    ReadConfig
    而非
    RC

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 (
    interface{}
    or
    any
    )
    : Use specific types when possible
  • Global state: Prefer dependency injection
  • Naked returns: Always name what you're returning
  • Stuttering:
    user.UserService
    should be
    user.Service
  • init() functions: Prefer explicit initialization
  • Complex constructors: Use functional options pattern
  • 空接口(
    interface{}
    any
    :尽可能使用具体类型
  • 全局状态:优先使用依赖注入
  • 裸返回:始终明确返回值的名称
  • 命名重复(Stuttering)
    user.UserService
    应改为
    user.Service
  • init()函数:优先选择显式初始化
  • 复杂构造函数:使用函数式选项模式

When in Doubt

存疑时的参考

  • Refer to Effective Go
  • Check the Go standard library for examples
  • Use
    go vet
    and
    staticcheck
    for automated guidance

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