Go包名须全小写、无下划线,且与目录名严格一致;main包仅用于可执行程序,应置于cmd/目录下;包名需语义清晰、避免泛化缩写,一经发布不可随意更改。

包名必须全小写且不含下划线
Go 语言的包名在 import 时直接用作标识符,而 Go 规定标识符不能含 _,也不区分大小写(MyUtils 和 myutils 在 import 路径中会被视为同一包),所以实际使用中必须强制小写、无下划线、无空格。
常见错误现象:import "github.com/user/json_parser" 导致编译报错 invalid package name: json_parser;或本地测试时 json_parser 包被识别为 jsonparser,但引用处仍写 json_parser.Do(),引发 undefined 错误。
- 所有包名统一用纯小写英文单词,如
http、sql、yaml、cli - 多词组合用连写(驼峰不推荐),如
multipart而非multiPart或multi_part - 避免与标准库包名冲突,比如不要起名
log、io、net—— 即使路径不同,IDE 和 linter 容易误判 - 模块根目录的包名不必和模块名一致,但应语义清晰;例如模块是
github.com/org/kit,内部可有kit/http、kit/db,对应包名分别是http和db
main 包只能出现在可执行程序中
main 是 Go 的特殊包名,仅当文件里有 func main() 且包声明为 package main 时才合法。它不是“主模块”或“入口模块”的代称,而是编译器识别可执行文件的硬性标记。
使用场景:你写一个 CLI 工具,想生成二进制;或者跑集成测试前需要临时启动一个 HTTP server 验证行为。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑:package main 出现在非命令行项目里(比如某个内部库子目录),导致 go build 报 cannot build a program outside of GOPATH or module path(尤其在 Go 1.16+ 模块模式下);或者误把 main 包当成“公共工具包”,结果其他代码 import 它时报 can't load package: package ... is not in GOROOT。
- 只在
cmd/xxx/目录下用package main,这是社区约定,也方便go install定位 - 测试辅助代码(比如 testmain)不要用
package main,改用package xxxtest+//go:build ignore注释控制 - 同一个模块里允许存在多个
main包,但每个必须在独立目录,且不能互相 import —— 否则构建失败
包名与目录名必须严格一致
Go 不支持 alias-style 的包导入(如 Python 的 import x as y),import "path/to/pkg" 中的 pkg 就是该目录下 package 声明的名称。两者不一致会直接导致编译失败。
典型错误:mkdir utils; echo "package helpers" > utils/utils.go,然后在别处 import "myproj/utils",编译提示 cannot load myproj/utils: cannot find module providing package myproj/utils —— 实际是因为 Go 查找的是 package utils,而非 helpers。
- 创建目录后,第一件事是确认
package xxx和目录名完全相同(包括拼写、大小写) - 重命名目录时,务必同步修改所有
package声明和所有 import 引用;编辑器重命名功能常漏掉 import 行,需手动检查 - CI 流程中加一条检查:遍历所有
*.go文件,比对package <name>和其所在路径最后一级目录名是否相等(可用grep -r "^package " --include="*.go" . | awk '{print $2, $0}'辅助)
避免包名暴露实现细节或过度泛化
包名是 API 的第一层契约。叫 v1、impl、internal 或 common 这类名字,要么过早锁定版本,要么失去语义,要么让人不敢用(internal 确实该用,但不该作为包名公开 export)。
性能影响不大,但可维护性直线下降:别人看到 import "myapp/common" 根本不知道这包干啥;看到 "myapp/v2" 会怀疑 v1 是否废弃、接口是否兼容。
- 用动词或名词表达职责,如
auth(处理认证)、cache(封装缓存操作)、queue(消息队列客户端抽象) - 拒绝缩写,除非是广泛接受的(如
http、sql),cfg不如config,srv不如service - 如果一个包只导出 1–2 个函数,且用途极其明确,可以更细粒度,比如
retry(专做重试策略)、sem(信号量封装),而不是塞进util
最常被忽略的一点:包名一旦发布到公开模块(比如打 tag 推送到 GitHub),就基本没法安全改了——下游 import 路径全断,而且 Go 的 import 路径即包身份。起名时多花三十秒想清楚,比半年后做 breaking change 省力得多。










