/cmd目录不是Go编译器硬性要求,而是社区标准约定,用于支持多二进制构建、强化关注点分离及保障internal包的编译器级隔离;扁平化会触发“multiple main packages”错误,且破坏模块边界与可维护性。
/cmd 目录必须存在,且每个可执行文件对应一个子目录,这是 Go 编译器识别入口的硬性约定,不是风格偏好。
为什么 /cmd 不能扁平化或合并?
go 的 go build 命令依赖 /cmd 下的独立子目录结构来区分不同二进制产物。如果你把两个 main.go 放进同一目录,go build 会报错:multiple main packages;如果直接放在根目录,又违反了标准布局的模块边界原则,导致 internal 包无法被正确隔离。
-
/cmd/myapp/main.go→ 构建出myapp二进制 -
/cmd/myworker/main.go→ 构建出myworker二进制 - 每个
main.go只 importinternal/app/xxx和pkg/xxx,绝不写业务逻辑
/internal 不是“内部用”,而是“编译器强制禁止外部导入”
Go 从 1.4 起就通过路径规则硬编码限制:任何以 /internal/ 开头的导入路径,若调用方不在其父目录树内,编译直接失败。这不是文档建议,是语言级保护机制。
- 错误示例:
github.com/you/project/internal/config被github.com/others/lib导入 → 编译报错use of internal package not allowed - 正确组织:
internal/app/myapp(主应用逻辑)、internal/pkg/database(仅本项目复用的工具) - 别在
/internal里放接口定义——除非你确定这些接口绝不会被外部实现或 mock
/pkg 不是“放工具函数的地方”,而是“对外提供稳定 API 的公共库”
/pkg 是唯一允许被其他项目 go get 直接引用的目录,它的内容必须满足语义化版本(SemVer)兼容性要求。随便往里扔一个 utils 包,等于主动承担长期 API 兼容责任。
- 适合放:
pkg/httpclient(带 retry、timeout、metric 的 HTTP 客户端封装) - 不适合放:
pkg/utils(含StrToBool、NowStr这类泛用函数——应归入internal/pkg/utils) - 一旦发布 v1.0.0,
pkg/mylib的导出函数签名就不能破坏性变更,否则下游项目会构建失败
go.mod 的位置和 replace 的使用时机:它必须位于项目根目录,且仅在开发调试阶段临时指向本地 pkg 修改,上线前必须删掉 replace 行——否则 CI 构建时会因路径解析失败而中断。











