go项目无强制目录规范,但混乱结构会导致go build失败、go test不识别、import报错;cmd/须按可执行名建子目录(如cmd/api),main.go仅加载配置、初始化依赖、调用app.run();internal/是编译器级私有边界,仅本项目可导入;测试文件必须与源码同目录同包且以_test.go结尾。

Go 项目没有强制目录结构,但随意组织会在 go build、go test 和多人协作时立刻暴露问题:二进制名错乱、测试不运行、import 失败、重构时循环依赖爆炸——这不是风格偏好,是编译器和工具链的硬性反馈。
cmd/ 下必须按可执行名建子目录,否则 go build 输出不可控
Go 工具链通过 cmd/ 子目录名推导最终二进制名,不是靠 main.go 里的内容。比如 cmd/api/main.go 执行 go build cmd/api 会生成 ./api;若写成 cmd/main.go,输出就是 ./cmd,完全偏离预期。
- 多个命令(如 CLI + Web + Worker)必须隔离在不同子目录,否则
go build ./cmd会同时编译所有main包,触发multiple main packages错误 -
cmd/xxx/main.go中只做三件事:加载配置、初始化依赖、调用app.Run();所有业务逻辑必须移出,不能塞任何非main包代码 - IDE 或 CI 脚本里写
go run cmd/api比go run main.go更稳定,避免因根目录下存在多个main.go导致歧义
internal/ 是编译器级访问控制,不是“私有代码垃圾桶”
internal/ 不是约定,是 Go 编译器内置的导入限制机制:位于 github.com/user/project/internal/foo 的包,仅允许被 github.com/user/project/ 下的包导入;外部模块引用会直接报错 use of internal package not allowed。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 适合放真正不该被复用的实现细节,例如
internal/tradebot/state(含私有状态机)、internal/dbmigration(仅本项目用的迁移逻辑) - 避免
internal/util/或internal/common/这类命名——语义模糊,很快变成函数堆砌场,且违背“小而专注”的包设计原则 - 如果某个包未来可能开源或跨项目复用,就别放
internal/,改放pkg/或直接顶层路径(如github.com/user/project/storage)
测试文件必须与源码同目录同包,否则 go test 不识别
Go 不支持集中式 test/ 目录。运行 go test ./ 时,工具链只扫描当前模块下所有 *_test.go 文件,并要求它们与同目录的非 _test.go 文件属于同一 package,否则编译失败。
-
internal/user/service.go(package service)的测试必须是internal/user/service_test.go(同样package service),才能测未导出函数 - 若想黑盒测试导出接口,可用
package service_test,但此时无法访问未导出字段或方法 - 把测试文件放在其他目录(如
test/service_test.go)会导致go test完全忽略它,且go list ./无法解析包路径
pkg/ 和 config/ 的边界在于“是否跨项目复用”
pkg/ 存的是有稳定 API、带文档和测试、能被其他项目安全 import 的组件;config/ 存的是当前项目的加载逻辑,紧贴运行时需求,不具备通用性。
-
config/通常包含:读取config.yaml、绑定环境变量(如CONFIG_ENV=prod)、校验必填字段、返回结构体实例 -
pkg/示例:一个带完整单元测试和 godoc 的pkg/uuid,导出NewV4()和Validate(),供其他项目直接import "github.com/user/project/pkg/uuid" - 把数据库模型(
model.User)放在internal/model,而不是pkg/model——除非你明确打算把它作为公共 SDK 对外发布
最常被忽略的点是:目录结构不是写完就一劳永逸的事。一旦项目超过 3k 行、引入第二个 cmd/ 入口、或开始写集成测试,internal/ 边界松动、pkg/ 里混入项目专用逻辑、测试文件放错位置,就会立刻让 go mod tidy、go test 和 CI 流水线开始报错——这些不是警告,是编译器拒绝配合的明确信号。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










