Go项目应将main.go放在cmd/子目录下,如cmd/userapi/main.go;internal/为访问边界,仅父级可导入;pkg/仅放明确复用的通用组件;测试文件与被测文件同目录。

main.go 放哪?别塞进根目录
新手常把 main.go 直接丢在项目最外层,结果随着功能增加,根目录迅速堆满配置、工具脚本和测试文件。Go 官方推荐将入口文件放在 cmd/ 下,每个可执行程序一个子目录。比如项目叫 userapi,就建 cmd/userapi/main.go。这样既支持多命令(如 cmd/userapi 和 cmd/migrate),也避免 go build 时误编译非主包。
internal/ 不是摆设,是访问边界的硬约束
internal/ 目录下的包,只能被其父目录或祖先目录的代码导入——这是 Go 编译器强制的可见性规则。初级项目容易把所有业务逻辑塞进 pkg/ 或平铺在根下,导致后期模块耦合严重、无法安全重构。正确做法是:把核心业务逻辑(如用户认证、订单处理)放进 internal/user、internal/order;对外暴露的接口(如 HTTP handler、gRPC service)则放在 pkg/ 或直接由 cmd/ 调用。这样即使未来要抽成独立库,也能快速识别哪些代码能拆、哪些必须保留。
config/ 和 pkg/ 怎么分?看是否会被外部复用
pkg/ 应只放**明确设计为被其他项目复用**的通用能力,比如自定义日志封装 pkg/logger、统一错误码 pkg/errno。而项目专用的配置加载逻辑(如从 YAML 解析数据库地址、自动 fallback 环境变量)必须放在 internal/config 或 cmd/userapi/config.go。常见错误是把 pkg/config 做成万能加载器,结果每次加个新字段都要改签名、发版本。实际中,80% 的配置解析只需在启动时调一次 viper.Unmarshal(),根本不需要抽象成包。
test 相关文件不要单独建 test/ 目录
Go 的测试惯例是:单元测试文件与被测文件同目录,文件名以 _test.go 结尾,比如 internal/user/service.go 对应 internal/user/service_test.go。不建议建顶层 test/ 目录集中放测试——那样会导致导入路径混乱(例如要写 import "myproj/test/mocks"),且 IDE 跳转、覆盖率统计都容易出错。集成测试若需独立数据或服务,可放在 internal/user/e2e_test.go,用 //go:build e2e 标签隔离。
立即学习“go语言免费学习笔记(深入)”;
myproj/ ├── cmd/ │ └── userapi/ │ └── main.go ├── internal/ │ ├── config/ # 项目专用配置解析 │ ├── user/ # 用户领域逻辑(含 service_test.go) │ └── order/ ├── pkg/ # 可被其他项目 import 的通用组件 │ └── logger/ ├── go.mod └── Dockerfile
真正难的是坚持——比如新增一个“通知”功能,本能想建 pkg/notify,但得先问:这个通知逻辑会不会被另一个项目(比如后台任务系统)直接 import?如果答案是否定的,它就该进 internal/notify。目录结构不是一开始画完就结束的事,而是每次加代码时都要做的判断。










