☰
Uber Go 语言编码规范:import 分组(Import Group Ordering)完整实践指南
2026/9/26 10:28:40 网站建设 项目流程
  • 文档

【免费下载链接】uber_go_guide_cn

Uber Go 语言编码规范中文版. The Uber Go Style Guide .

项目地址:https://gitcode.com/gh_mirrors/ub/uber_go_guide_cn
点击查看免费下载

本指南是 uber_go_guide_cn(Uber Go 语言编码规范中文版)中「import 分组」一节的深度解读与工程化落地手册。它以 Uber 内部对 Go 代码 import 语句的组织约定为核心,告诉你为什么要把导入分成「标准库」与「其他库」两组、goimports 如何默认帮你完成分组、以及如何把这套规范与导入别名、声明分组、包命名等相邻规范协同落地到编辑器与 CI 流程中。读完本文,你将能够写出与 Uber 风格一致、可被 goimports/golangci-lint 自动校验的 import 块,并理解其背后的可维护性动机。

一、规范原文:import 必须分为两组

规范正文非常简洁,但约束力明确:

导入应该分为两组:

  • 标准库
  • 其他库

也就是说,在任意 Go 源文件的 import 块中,只允许出现两个分组:第一组是 Go 标准库(fmt、os、net/http等),第二组是除此之外的一切(第三方库、企业内部包、go.uber.org系包等)。两组之间以一个空行分隔。

这是该规范在 README.md 与 src/import-group.md 中的原始表述。它的底层依据是:默认情况下,这正是 goimports 应用的分组方式——规范不是凭空发明的新规则,而是把社区主流工具(goimports)的默认行为固化为团队纪律。

二、正反示例:Bad vs Good

规范给出了如下正反对比,这是必须完整继承的「黄金示例」。

Bad——所有导入挤在一组,没有空行分隔:

import ( "fmt" "os" "go.uber.org/atomic" "golang.org/x/sync/errgroup" )

Good——标准库与第三方库之间以空行隔开,形成两个分组:

import ( "fmt" "os" "go.uber.org/atomic" "golang.org/x/sync/errgroup" )

差异肉眼可见:Bad 版本中,"go.uber.org/atomic"、"golang.org/x/sync/errgroup"与"fmt"、"os"混在一起,阅读时无法一眼区分「这是什么来源的依赖」;Good 版本通过一个空行把标准库(fmt、os)与第三方库(go.uber.org/atomic、golang.org/x/sync/errgroup)清晰切分。

这个示例还隐含着第二个细节:组内默认按字母序排列。"fmt"在"os"之前、"go.uber.org/atomic"在"golang.org/x/sync/errgroup"之前,均符合 goimports 的处理结果。

三、为什么是「两组」:可读性与依赖来源的即时代码审计

分组的意义远超排版美观。把 import 切成「标准库 / 其他库」两组,等于给代码做了一次依赖来源的视觉分类:

  • 标准库组:编译器自带、版本随 Go 工具链走,通常不需要讨论升级策略;
  • 其他库组:第三方依赖,涉及版本管理(go.mod)、许可证、维护状态等工程决策。

当代码评审者扫过 import 块时,两组之间的空行相当于一个视觉锚点,可以快速回答「这个文件依赖了哪些外部包」。结合仓库中相邻的规范 包名规范(包名应全部小写、简短、不用复数、避免common/util/shared/lib等无信息量名称),可以推断:import 分组的深层目的是让每个文件的依赖边界在几毫秒内被人类与工具同时读懂,从而降低大代码库的认知负担——这正是 介绍 中所述「保持代码库易于管理」这一总目标在 import 层面的具体化。

四、goimports:分组的默认执行者

规范明确指出「默认情况下,这是 goimports 应用的分组」。因此落地本规范的第一件事,就是把 goimports 引入你的工作流。

4.1 安装与使用

goimports 是 Go 官方扩展工具链(golang.org/x/tools)提供的命令行工具,通过如下方式安装:

go install golang.org/x/tools/cmd/goimports@latest

对单个文件执行:

goimports -w main.go

-w表示直接写回文件。goimports 的职责有两层:

  1. 格式化:与 gofmt 一样处理缩进、对齐等源码排版;
  2. 管理 import:自动补充缺失的导入、删除未使用的导入,并按「标准库 / 其他库」两分组、组内按字母序整理 import 块。

正是第 2 点,让 lint.md 把 goimports 列入推荐 linter 集合(「[goimports] 格式化代码和管理 imports」),并称其能「为代码质量建立高标准」。也正因为 goimports 默认即采用两分组,团队只要统一使用它,import 分组规范就不依赖人的自觉,而是由工具强制保证。

4.2 与 gofmt 的边界

需要厘清一个常见混淆:gofmt负责源码格式化,但它只对 import 块内做字母排序,不负责分组;「标准库 / 其他库」的分组切分是 goimports 的差异化能力。换句话说,仅靠 gofmt 无法保证本规范生效。这解释了 介绍 中 Uber 推荐「保存时运行 goimports」而非仅依赖 gofmt 的原因。

五、落地到编辑器与 CI:让规范自动生效

5.1 编辑器:保存时自动整理

介绍 给出的官方建议是:

  • 保存时运行goimports;
  • 运行golint和go vet检查错误。

在 VS Code、GoLand、vim 等主流编辑器中,均有对应插件支持在保存时自动执行 goimports(详见 Go 官方 wiki 的编辑器工具支持页面)。保存即整理,import 分组从此无需手工维护。

5.2 CI:统一 lint runner

除了编辑器层面的即时格式化,规范还应在 CI 中作为检查项存在。lint.md 推荐使用 golangci-lint 作为统一 lint runner,它能在一次运行中启用多种规范 linter。对于 import 分组,除了 goimports 本身(golangci-lint 中对应goimportslinter),还有gci、goimports-local-prefixes等 linter 可配置分组策略。基础推荐集合是:

  • errcheck:确保错误被处理;
  • goimports:格式化代码并管理 import;
  • golint:指出常见风格错误;
  • go vet:分析常见错误;
  • staticcheck:各种静态分析检查。

在 CI 中执行 golangci-lint 时,import 分组不合规的文件会直接报错,从而把「两组分隔」从建议升级为强制门禁。

六、与相邻 import 规范的协同

import 相关规范在 Uber 风格指南中是一套组合拳,理解它们之间的关系,才能写出自洽的 import 块。

6.1 导入别名(Import Aliasing)

import-alias.md 规定:当包名与导入路径最后一个元素不匹配时,必须使用别名:

import ( "net/http" client "example.com/client-go" trace "example.com/trace/v2" )

这里client-go的包声明名与路径末元素不匹配,因此必须显式命名client。注意该示例同样遵循两分组:net/http在标准库组,两个别名导入在第二组。

其余场景下应避免使用别名,除非导入之间发生直接冲突。例如当runtime/trace与golang.net/x/trace同时被导入时,前者保持原名、只给后者加别名nettrace:

import ( "fmt" "os" "runtime/trace" nettrace "golang.net/x/trace" )

别名机制与分组机制正交:分组解决「来源分类」,别名解决「命名冲突与路径-包名不一致」,两者共同作用时仍以两组空行分隔为框架。

6.2 相似的声明放在一组(Group Similar Declarations)

decl-group.md 规定 import 声明应当合并为分组块而非逐条import "a"/import "b":

import ( "a" "b" )

同时强调「只对相关的声明分组,不要把无关声明混入」。这与 import 分组规范在精神上完全一致:分组是 Go 声明组织的基本手段,import 块不过是其最典型应用。该规范还适用于 const、var、type 的相似声明,并允许在函数内部使用分组。

6.3 包名(Package Names)

package-name.md 要求包名全部小写、简短、非复数、避免无信息量的通用名。合理的包名会让「分组内按字母排序后的 import 列表」本身就成为可读的依赖清单;若包名冗长怪异,则往往需要借助 6.1 的别名机制补救——三条规范由此形成闭环。

七、常见问题(FAQ)

Q1:标准库组里可以出现点号导入或下划线导入吗?规范未在 import-group.md 中单独规定_(仅执行 init)或.(点导入)的归属,但按「标准库 / 其他库」二分法,它们应放入其来源所属的分组。点导入在 Uber 规范的其他章节另有约束,实践中应尽量少用。

Q2:企业内部包放在哪一组?按规范的字面定义,非标准库的一切都归「其他库」组。若团队希望在企业包与第三方包之间再切一层,那是超出本规范的团队自定义,需要在 golangci-lint 的gci等配置中显式声明;就本规范而言,两组是唯一法定结构。

Q3:组内排序由谁保证?goimports 默认在组内按导入路径字母序排列,src/import-group.md 的 Good 示例(fmt、os之后是go.uber.org/atomic、golang.org/x/sync/errgroup)即为排序后的结果。手工写代码时遵循「先标准库、后其他库,组内字母序」即可,最终仍以 goimports 输出为准。

Q4:如果团队不使用 goimports,规范还有效吗?规范文本并不绑定特定工具,但「默认情况下,这是 goimports 应用的分组」这一句表明:采用 goimports 是最省力的合规路径。不用它,就必须靠 code review 人工检查两个分组与空行,成本更高且易遗漏。

八、小结与自检清单

import 分组规范用一句话概括:每个 import 块恰好两个分组——标准库在上、其他库在下,中间一个空行,组内按字母序,全部交给 goimports 自动执行。

提交代码前,请对照以下清单自检:

  1. import 块是否被空行切成「标准库 / 其他库」两组?✅
  2. 组内是否按字母序排列?✅
  3. 包名与路径末元素不一致时是否已加别名?✅
  4. 是否存在不必要的别名(无冲突却强行重命名)?✅
  5. 编辑器是否已配置保存时运行 goimports,CI 是否已纳入 golangci-lint?✅

把这五条固化到团队流程中,你的 Go 代码 import 区域将不再依赖个人风格,而是由工具与规范共同保证的一致产出。相关规范原文与配套文档可继续在本仓库查阅:import-group.md、import-alias.md、decl-group.md、package-name.md、lint.md、intro.md。

  • 文档

【免费下载链接】uber_go_guide_cn

Uber Go 语言编码规范中文版. The Uber Go Style Guide .

项目地址:https://gitcode.com/gh_mirrors/ub/uber_go_guide_cn
点击查看免费下载
上一篇:B站缓存视频m4s转mp4完整指南:3步用m4s-converter救回你的下架收藏
下一篇:被云待办坑了一个月后,我换上了这款纯本地的跨平台桌面待办工具

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询