npx skills add https://github.com/cxuu/golang-skills --skill go-packages本技能涵盖遵循 Google 和 Uber Go 风格指南的包组织和导入管理。
建议性:这是一条最佳实践建议。
包名应描述包提供的内容。避免使用像 util、helper、common 或类似的通用名称——它们会使代码难以阅读并导致导入冲突。
// 良好:有意义的包名
db := spannertest.NewDatabaseFromFile(...)
_, err := f.Seek(0, io.SeekStart)
// 不佳:模糊的包名掩盖了含义
db := test.NewDatabaseFromFile(...)
_, err := f.Seek(0, common.SeekStart)
像 util 这样的通用名称可以作为名称的一部分使用(例如 stringutil),但不应该作为整个包名。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
建议性:这是最佳实践指导。
何时合并包:
何时拆分包:
bytes.Buffer、ring.New文件组织: Go 中没有“一个类型,一个文件”的约定。文件应该足够聚焦,以便知道某个内容在哪个文件中,并且足够小,以便轻松找到内容。
规范性:根据 Go Wiki CodeReviewComments,这是必需的。
导入按组组织,组之间用空行分隔。标准库包始终在第一组。
package main
import (
"fmt"
"hash/adler32"
"os"
"github.com/foo/bar"
"rsc.io/goversion/version"
)
使用 goimports 来自动管理此操作。
组合性:Google + Uber 指导
最小分组(Uber): 标准库,然后是其他所有内容。
扩展分组(Google): 标准库 → 其他 → 协议缓冲区 → 副作用。
// 良好:标准库与外部包分开
import (
"fmt"
"os"
"go.uber.org/atomic"
"golang.org/x/sync/errgroup"
)
// 良好:包含协议缓冲区和副作用的完整分组
import (
"fmt"
"os"
"github.com/dsnet/compress/flate"
"golang.org/x/text/encoding"
foopb "myproj/foo/proto/proto"
_ "myproj/rpc/protocols/dial"
)
规范性:根据 Go Wiki CodeReviewComments 和 Google 的 Go 风格指南,这是必需的。
除非为了避免名称冲突,否则避免重命名导入;好的包名不应该需要重命名。如果发生冲突,优先重命名最本地化或项目特定的导入。
必须重命名: 与其他导入冲突,生成的协议缓冲区包(移除下划线,添加 pb 后缀)。
可以重命名: 无信息的名称(例如 v1),与局部变量冲突。
// 良好:协议缓冲区包使用 pb 后缀重命名
import (
foosvcpb "path/to/package/foo_service_go_proto"
)
// 良好:当需要 url 变量时使用 urlpkg
import (
urlpkg "net/url"
)
func parseEndpoint(url string) (*urlpkg.URL, error) {
return urlpkg.Parse(url)
}
import _)规范性:根据 Go Wiki CodeReviewComments 和 Google 的 Go 风格指南,这是必需的。
仅因其副作用而导入的包(使用 import _ "pkg")应仅导入到程序的主包中,或需要它们的测试中。
// 良好:在主包中进行空白导入
package main
import (
_ "time/tzdata"
_ "image/jpeg"
)
import .)规范性:根据 Go Wiki CodeReviewComments 和 Google 的 Go 风格指南,这是必需的。
不要使用点导入。它们使程序更难阅读,因为不清楚像 Quux 这样的名称是当前包中的顶级标识符还是导入包中的标识符。
例外: import . 形式在测试中可能有用,由于循环依赖,这些测试无法成为被测试包的一部分:
package foo_test
import (
"bar/testutil" // 也导入了 "foo"
. "foo"
)
在这种情况下,测试文件不能放在 foo 包中,因为它使用了 bar/testutil,而 bar/testutil 导入了 foo。因此,import . 形式让文件假装是 foo 包的一部分,即使实际上不是。
除了这种情况,不要在程序中使用 import .。
// 不佳:点导入隐藏了来源
import . "foo"
var myThing = Bar() // Bar 来自哪里?
// 良好:显式限定
import "foo"
var myThing = foo.Bar()
来源:Uber Go 风格指南
尽可能避免使用 init()。当 init() 不可避免时,代码应:
init() 函数的顺序或副作用// 不佳:带有 I/O 和环境依赖的 init()
var _config Config
func init() {
cwd, _ := os.Getwd()
raw, _ := os.ReadFile(path.Join(cwd, "config.yaml"))
yaml.Unmarshal(raw, &_config)
}
// 良好:用于加载配置的显式函数
func loadConfig() (Config, error) {
cwd, err := os.Getwd()
if err != nil {
return Config{}, err
}
raw, err := os.ReadFile(path.Join(cwd, "config.yaml"))
if err != nil {
return Config{}, err
}
var config Config
if err := yaml.Unmarshal(raw, &config); err != nil {
return Config{}, err
}
return config, nil
}
可接受的 init() 用途:
database/sql 方言、编码注册表)来源:Uber Go 风格指南
仅在 main() 中调用 os.Exit 或 log.Fatal*。所有其他函数都应返回错误以表示失败。
为何重要:
不明显的控制流:任何函数都可以退出程序
难以测试:退出的函数也会退出测试
跳过清理:defer 语句被跳过
// 不佳:在辅助函数中使用 log.Fatal func readFile(path string) string { f, err := os.Open(path) if err != nil { log.Fatal(err) // 退出程序,跳过 defer } b, err := io.ReadAll(f) if err != nil { log.Fatal(err) } return string(b) }
// 良好:返回错误,让 main() 决定是否退出 func main() { body, err := readFile(path) if err != nil { log.Fatal(err) } fmt.Println(body) }
func readFile(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } b, err := io.ReadAll(f) if err != nil { return "", err } return string(b), nil }
倾向于在 main() 中最多调用一次 os.Exit 或 log.Fatal。将业务逻辑提取到一个单独的函数中,该函数返回错误。
// 良好:使用 run() 模式的单一退出点
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
args := os.Args[1:]
if len(args) != 1 {
return errors.New("missing file")
}
f, err := os.Open(args[0])
if err != nil {
return err
}
defer f.Close() // 将始终运行
b, err := io.ReadAll(f)
if err != nil {
return err
}
// 处理 b...
return nil
}
run() 模式的好处:
main() 函数,具有单一退出点defer 语句总是执行| 主题 | 规则 | 类型 |
|---|---|---|
| 导入组织 | 标准库优先,组间用空行分隔 | 规范性 |
| 导入分组 | 标准库 → 其他(→ 协议缓冲区 → 副作用) | 组合性 |
| 导入重命名 | 仅在必要时;优先重命名本地/项目导入 | 规范性 |
| 空白导入 | 仅在主包或测试中 | 规范性 |
| 点导入 | 仅用于循环测试依赖 | 规范性 |
| Util 包 | 避免;使用描述性名称 | 建议性 |
| 包大小 | 平衡内聚性与独立概念 | 建议性 |
| init() | 避免;如果使用必须是确定性的 | 建议性 |
| 在 main 中退出 | 仅从 main() 退出;返回错误 | 建议性 |
go-style-corego-naminggo-error-handlinggo-defensivego-linting每周安装次数
143
仓库
GitHub 星标数
35
首次出现
2026年1月27日
安全审计
安装于
github-copilot133
gemini-cli132
codex132
kimi-cli131
amp131
opencode131
This skill covers package organization and import management following Google's and Uber's Go style guides.
Advisory : This is a best practice recommendation.
Package names should describe what the package provides. Avoid generic names like util, helper, common, or similar—they make code harder to read and cause import conflicts.
// Good: Meaningful package names
db := spannertest.NewDatabaseFromFile(...)
_, err := f.Seek(0, io.SeekStart)
// Bad: Vague package names obscure meaning
db := test.NewDatabaseFromFile(...)
_, err := f.Seek(0, common.SeekStart)
Generic names like util can be used as part of a name (e.g., stringutil) but should not be the entire package name.
Advisory : This is best practice guidance.
When to combine packages:
When to split packages:
bytes.Buffer, ring.NewFile organization: No "one type, one file" convention in Go. Files should be focused enough to know which file contains something and small enough to find things easily.
Normative : This is required per Go Wiki CodeReviewComments.
Imports are organized in groups, with blank lines between them. The standard library packages are always in the first group.
package main
import (
"fmt"
"hash/adler32"
"os"
"github.com/foo/bar"
"rsc.io/goversion/version"
)
Use goimports to manage this automatically.
Combined : Google + Uber guidance
Minimal grouping (Uber): stdlib, then everything else.
Extended grouping (Google): stdlib → other → protocol buffers → side-effects.
// Good: Standard library separate from external packages
import (
"fmt"
"os"
"go.uber.org/atomic"
"golang.org/x/sync/errgroup"
)
// Good: Full grouping with protos and side-effects
import (
"fmt"
"os"
"github.com/dsnet/compress/flate"
"golang.org/x/text/encoding"
foopb "myproj/foo/proto/proto"
_ "myproj/rpc/protocols/dial"
)
Normative : This is required per Go Wiki CodeReviewComments and Google's Go style guide.
Avoid renaming imports except to avoid a name collision; good package names should not require renaming. In the event of collision, prefer to rename the most local or project-specific import.
Must rename: collision with other imports, generated protocol buffer packages (remove underscores, add pb suffix).
May rename: uninformative names (e.g., v1), collision with local variable.
// Good: Proto packages renamed with pb suffix
import (
foosvcpb "path/to/package/foo_service_go_proto"
)
// Good: urlpkg when url variable is needed
import (
urlpkg "net/url"
)
func parseEndpoint(url string) (*urlpkg.URL, error) {
return urlpkg.Parse(url)
}
import _)Normative : This is required per Go Wiki CodeReviewComments and Google's Go style guide.
Packages that are imported only for their side effects (using import _ "pkg") should only be imported in the main package of a program, or in tests that require them.
// Good: Blank import in main package
package main
import (
_ "time/tzdata"
_ "image/jpeg"
)
import .)Normative : This is required per Go Wiki CodeReviewComments and Google's Go style guide.
Do not use dot imports. They make programs much harder to read because it is unclear whether a name like Quux is a top-level identifier in the current package or in an imported package.
Exception: The import . form can be useful in tests that, due to circular dependencies, cannot be made part of the package being tested:
package foo_test
import (
"bar/testutil" // also imports "foo"
. "foo"
)
In this case, the test file cannot be in package foo because it uses bar/testutil, which imports foo. So the import . form lets the file pretend to be part of package foo even though it is not.
Except for this one case, do not useimport . in your programs.
// Bad: Dot import hides origin
import . "foo"
var myThing = Bar() // Where does Bar come from?
// Good: Explicit qualification
import "foo"
var myThing = foo.Bar()
Source : Uber Go Style Guide
Avoid init() where possible. When init() is unavoidable, code should:
init() functions// Bad: init() with I/O and environment dependencies
var _config Config
func init() {
cwd, _ := os.Getwd()
raw, _ := os.ReadFile(path.Join(cwd, "config.yaml"))
yaml.Unmarshal(raw, &_config)
}
// Good: Explicit function for loading config
func loadConfig() (Config, error) {
cwd, err := os.Getwd()
if err != nil {
return Config{}, err
}
raw, err := os.ReadFile(path.Join(cwd, "config.yaml"))
if err != nil {
return Config{}, err
}
var config Config
if err := yaml.Unmarshal(raw, &config); err != nil {
return Config{}, err
}
return config, nil
}
Acceptable uses of init():
database/sql dialects, encoding registries)Source : Uber Go Style Guide
Call os.Exit or log.Fatal* only inmain(). All other functions should return errors to signal failure.
Why this matters:
Non-obvious control flow: Any function can exit the program
Difficult to test: Functions that exit also exit the test
Skipped cleanup: defer statements are skipped
// Bad: log.Fatal in helper function func readFile(path string) string { f, err := os.Open(path) if err != nil { log.Fatal(err) // Exits program, skips defers } b, err := io.ReadAll(f) if err != nil { log.Fatal(err) } return string(b) }
// Good: Return errors, let main() decide to exit func main() { body, err := readFile(path) if err != nil { log.Fatal(err) } fmt.Println(body) }
func readFile(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } b, err := io.ReadAll(f) if err != nil { return "", err } return string(b), nil }
Prefer to call os.Exit or log.Fatal at most once in main(). Extract business logic into a separate function that returns errors.
// Good: Single exit point with run() pattern
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
args := os.Args[1:]
if len(args) != 1 {
return errors.New("missing file")
}
f, err := os.Open(args[0])
if err != nil {
return err
}
defer f.Close() // Will always run
b, err := io.ReadAll(f)
if err != nil {
return err
}
// Process b...
return nil
}
Benefits of therun() pattern:
main() function with single exit pointdefer statements always execute| Topic | Rule | Type |
|---|---|---|
| Import organization | std first, groups separated by blank lines | Normative |
| Import grouping | std → other (→ proto → side-effect) | Combined |
| Import renaming | Only when necessary; prefer renaming local/project import | Normative |
| Blank imports | Only in main packages or tests | Normative |
| Dot imports | Only for circular test dependencies | Normative |
| Util packages | Avoid; use descriptive names | Advisory |
| Package size | Balance cohesion vs. distinct concepts | Advisory |
| init() | Avoid; must be deterministic if used | Advisory |
| Exit in main | Only exit from main(); return errors | Advisory |
go-style-corego-naminggo-error-handlinggo-defensivego-lintingWeekly Installs
143
Repository
GitHub Stars
35
First Seen
Jan 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot133
gemini-cli132
codex132
kimi-cli131
amp131
opencode131
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
111,800 周安装
RevOps收入运营指南:优化营销、销售与客户成功统一引擎
18,000 周安装
template-skill 模板技能 | Anthropic 官方技能模板 | 高效开发 AI 助手技能
17,700 周安装
Slack GIF 创建工具 - 优化表情符号和消息动画制作指南与Python代码库
17,900 周安装
网站架构规划指南:SEO优化与用户体验设计,打造高效网站结构
18,800 周安装
Expo网络请求与数据获取指南:React Query、缓存策略、API调试
19,300 周安装
Anthropic内部沟通技能:高效撰写3P更新、公司通讯、FAQ回复等内部文档
19,100 周安装