"Go 项目该怎么组织目录?"------这是 Go 开发者社区反复被问到的问题。网上充斥着过度设计的模板,反而让初学者无所适从。本文基于 Go 语言"less is more"的核心哲学,结合实际开发经验,为你梳理一套简单、清晰、可维护的 Go 项目布局方案。
一、Go 项目布局的核心原则
在动手建目录前,请牢记三句话:
- ✅ 先做出能跑的东西,再考虑拆包重构
- ✅ 不要为了"看起来专业"而堆砌目录
- ✅
internal/
、pkg/
、cmd/
是工具,不是模式
Go 官方文档也强调:只有当项目足够大时,才考虑拆分目录 (Go Modules Layout Guide)。盲目套用"标准结构"往往是过度工程的开始。
二、常见误区与正确做法
❌ 误区 1:一上来就建 cmd/
、pkg/
、internal/
很多教程一开头就推荐:
csharp
project/
├── cmd/
├── pkg/
├── internal/
└── go.mod
但对一个只有几百行代码的小项目来说,这纯属无意义的复杂度。
✅ 正确做法:
- 如果是可执行程序(CLI 或 Web 服务) ,直接把
main.go
放在项目根目录。 - 如果是纯库(library) ,根本不需要
main.go
。
优势:
- 安装命令最简:
go install github.com/you/project@latest
- 导入路径更短
- 结构一目了然
示例(推荐):
go
my-api/
├── go.mod
├── main.go # 程序入口
├── config/
├── storage/
├── api/
└── README.md
❌ 误区 2:滥用 pkg/
目录
pkg/
是早期 Go 社区的历史惯例,现代 Go 项目已不再需要它。
反例(不推荐):
go
import "github.com/you/project/pkg/storage"
✅ 正确做法 :
将功能包直接放在顶层,用语义化命名:
go
import "github.com/you/project/storage"
import "github.com/you/project/auth"
这样导入路径更短,意图更清晰。
❌ 误区 3:乱建 util/
、common/
、helper/
这些"万能包"最终会变成代码垃圾场------任何不知道放哪的函数都被塞进去。
反例:
go
// util/strings.go
package util
func Reverse(s string) string { ... }
✅ 正确做法:
- 把函数放在语义相关的包 中,如
text/
、crypto/
; - 或直接放在使用它的包内部 (如
handler/text_helpers.go
)。
包名应体现用途,
text.Reverse
比util.Reverse
更有意义。
❌ 误区 4:过度拆分包和文件
- 每个函数一个文件?
- 每个接口一个包?
这会让代码像"碎片",阅读和维护成本剧增。
✅ 正确做法:
- 一个包可包含多个文件,只要它们语义相关;
- 包的大小建议在 200--1000 行之间(非硬性,仅作参考);
- 按功能/用途拆包,而不是按文件大小。
例如:storage
包可包含 mysql.go
、redis.go
、interface.go
,共同实现存储抽象。
三、internal/
什么时候用?
internal/
是 Go 的访问控制机制 :只有父目录及其子目录能导入 internal
下的代码。
✅ 适用场景:
- 你的项目会被大量第三方依赖;
- 你有不想暴露但内部复用的代码(如密钥处理、私有协议)。
❌ 不适用场景:
- 个人项目、内部服务、小型工具------不用
internal/
更简单。
示例(合理使用):
bash
/project
├── main.go
├── internal/
│ └── secrets/ # 外部无法 import
└── storage/
四、推荐的极简项目模板
以下结构适用于大多数中小型 Go 项目(Web 服务、CLI 工具等):
perl
my-project/
├── go.mod
├── main.go # 可执行程序入口(如是库则无)
├── README.md
├── config/ # 配置加载、解析
├── storage/ # 数据库、缓存等存储逻辑
├── api/ # HTTP/gRPC 处理器(handlers)
├── tools/ # 开发工具脚本(非构建依赖)
└── docs/ # 设计文档、API 说明(可选)
💡 如果未来项目变大,再按需拆分,比如增加
domain/
、service/
等。
五、关于版本管理的建议
- 初期建议使用
v0.x
版本(语义化版本); - 不要过早发布 v1/v2,避免因小改动导致用户分叉;
- 通过
CHANGELOG.md
或 release notes 明确 API 变更。
Go 的哲学是:先发布,再演进,而不是"设计完美再开源"。
六、总结
Go 项目布局的终极目标不是"看起来像大厂",而是:
清晰、简单、可维护、低认知负担。
记住:
- 没有"标准结构",只有"合适结构";
- 目录是演进出来的,不是设计出来的;
- 少即是多(less is more) 是 Go 的灵魂。
把精力放在写好代码、写好文档、理清楚功能概念、定义清晰的接口上,远比纠结目录名重要得多。