谈论 Writing Better Go:10 次 Code Reviews 的经验教训

发布: (2026年1月9日 GMT+8 16:37)
6 min read
原文: Dev.to

Source: Dev.to

处理错误

  • 不要悄悄忽略 error(例如使用 _ 接收 error)
  • 不要认为这个 error “可以接受”,例如 if err != nil { return nil }
  • 遇到 error 时立即检查并处理,例如记录日志后认为该 error 已经处理完毕,不再向上层传递
  • 不要重复报错 — 如果已经记录了日志再 return,让调用方再次记录日志会导致日志重复

示例(坏的 vs. 好的)

// Bad
if err != nil {
    slog.Error(err)
    return err
}

// Good
if err != nil {
    slog.Error(err)
    return nil // 或者在这里处理 error 并且不再向上层返回
}

减轻调用方的负担

  • return result, nil – 返回结果且没有 error,调用方易于理解
  • return nil, err – 有 error 需要处理,且没有结果
  • return nil, nil – 很糟糕,因为调用方必须自行判断到底是结果还是 error,应该避免
  • return result, err – 也不佳,因为调用方必须同时检查两个返回值才能确定是否可用

使用 Interface

  • 不要一开始就创建 interface(很多情况下是从 Java 等语言迁移过来的)或仅仅为了在测试中 mock
  • 使用 accept interfaces, return concrete types – 通过 interface 接收,但返回具体类型
  • 先使用 concrete type,等到真的需要 interface 时再引入
  • Litmus Test:如果不使用 interface 也能测试通过,就不必创建
  • 不要仅为了让测试通过而创建 interface – 尽量编写不依赖 interface 的测试

先用 Mutex 再用 Channels

  • 使用 channel 会让代码变得复杂,并且容易出现 panic(例如关闭仍在使用的 channel、向已关闭的 channel 发送)或 deadlock
  • 先采用简单的方式:使用 sync.Mutexsync.WaitGroup 来管理共享状态
  • 只有在 profiling 明显出现瓶颈时才增加 goroutine 或 channel
  • 仅在确实需要处理复杂并发逻辑时才使用 channel,而不是用于简单任务

声明(Declare)靠近使用位置

  • 在同一文件中,将 constants、variables、functions、types 声明放在实际使用的附近
  • 只有在需要被外部 package 使用时才 Export(首字母大写)
  • 在函数内部,变量的声明应尽可能靠近使用点
  • 将作用域限制得尽可能小,例如在 if 语句中只写一条语句

避免 Runtime Panic

  • 在使用外部输入前先进行校验
  • 如果已经控制了数据流向,就不要再使用 if x == nil,而是信任该处的 error 处理
  • 对指针解引用(如 *p = 10)前必须先检查 nil
  • 最安全的做法是 尽量避免使用指针,如果设计上可以做到

排版(Spacing)

  • 不要把逻辑层层嵌套(nested iffor
  • 使用 Return EarlyFlatter Structure:先处理 error,再继续后续工作,或立即剔除不需要继续执行的条件

文件与包的命名

  • 避免使用模糊的名称,如 util.gomisc.goconstants.gointerfaces/interfaces.go
  • 名称应直接说明其职责,而不是所在的结构层级
  • 将文件放在与相关代码相近的位置,不要散落在不同目录

声明的分组与顺序

  • 按语义分组,而不是按类型分组(例如不要把 controller 分成不同组)
  • 按重要性排序声明:
    1. 对外 API 的函数/结构体(需要让使用者首先看到)
    2. 为上述代码提供说明或支持的辅助函数

变量命名

  • 不要在名称后面加类型后缀(如 userMapidStrinjectFn)——变量名应描述它保存的内容
  • 名称长度应与作用域相匹配:作用域短的变量可以使用短名,全局变量则应使用能够清晰说明意义的名称

文档

  • 文档应回答 “为什么” 而不仅是 “做了什么”
  • 给出功能或代码存在的原因,以及为何采用当前实现方式
  • 注释应传达代码的目的或价值,而不是重复描述实现细节
  • Documentation 是意图,而非实现方式 – 帮助阅读者理解设计动机

摘自 Konrad Reiche 的《Writing Better Go: Lessons from 10 Code Reviews》(作者 Asleep‑Actuary‑4428)

Back to Blog

相关文章

阅读更多 »

Go的秘密生活:错误处理

第12章:碎玻璃的声音 星期一的早晨,厚重的灰色雾气笼罩着整座城市。档案馆内部,寂静至极,随后被打破……