Go初级项目应从main.go单文件起步,先跑通功能再按职责分层;避免过早使用internal/pkg/cmd等目录增加理解成本,配置优先用命令行参数或环境变量,静态资源可用embed.FS打包。

Go 初级项目不需要照搬大型微服务的目录结构,盲目套用 internal、pkg、cmd 会增加理解成本,反而拖慢开发节奏。核心原则是:先跑通功能,再按职责自然分层。
从 main.go 单文件起步最稳妥
新手常因过早设计目录而卡在 import 路径错误或循环引用上。直接把所有逻辑写进 main.go,能快速验证业务流程是否成立,也方便调试和删改。
实操建议:
- HTTP 路由、数据库初始化、配置加载全放在
main()函数里,不拆包 - 用
flag或硬编码模拟配置,避免一上来就引入viper增加依赖复杂度 - 等代码超过 300 行、或某个功能(如用户登录)明显可独立时,再提取为单独的
user/包
cmd/ 和 internal/ 不是必须项
很多教程强调 “标准 Go 工程结构”,但对 CLI 工具或小 API 服务来说,cmd/myapp/main.go 只是多了一层路径跳转;internal/ 的导入限制在初级阶段几乎无意义,还容易导致 “想复用却导不出” 的挫败感。
真实情况:
- 如果你只写一个二进制(比如
go run main.go启动的服务),根本不需要cmd/ -
internal/主要防外部模块误引内部实现,但初级项目通常没有外部引用需求 - 过早加
internal/会让go test找不到测试目标,报错cannot find package "xxx/internal/handler"
何时该拆出 pkg/?看复用意图而非代码量
pkg/ 是唯一值得早期考虑的目录,但它不是为了“看起来规范”,而是当你明确打算把某段逻辑(比如 JWT 签名、日期格式化工具)未来用在另一个项目中时,才把它移进去。
判断信号:
- 同一段代码在两个不同函数里复制粘贴了两次以上
- 你写了注释如
// 这个校验逻辑后续其他接口也要用 - 你开始给函数加
exported首字母大写,且希望别人能import "myproject/pkg/auth"
此时才建 pkg/auth/auth.go,并确保它不依赖 internal/ 或 cmd/ 下的私有类型。
配置与静态资源放哪?别碰 assets/ 或 resources/
Go 编译后是单二进制,静态文件(HTML 模板、SQL 初始化脚本、JSON 配置)默认不在可执行文件内。新手常因此部署失败——本地跑得好,上线就报 open config.yaml: no such file。
务实做法:
- 配置优先用命令行参数或环境变量(
os.Getenv("DB_URL")),避免文件 I/O 失败 - 必须用文件时,约定统一路径如
./config.yaml,并在 README 写明“启动前需手动创建” - 模板文件(
html/template)可用embed.FS打包进二进制,但仅当确定不需热更新时才用——否则每次改 HTML 都要重新编译
package mainimport ( "embed" "html/template" "net/http" )
//go:embed templates/* var tplFS embed.FS
func main() { tpl := template.Must(template.ParseFS(tplFS, "templates/.html")) http.HandleFunc("/", func(w http.ResponseWriter, r http.Request) { tpl.Execute(w, nil) }) http.ListenAndServe(":8080", nil) }
目录结构的复杂度应该跟着实际问题增长,而不是跟着教程模板增长。很多初级项目最终也没用上 internal/,但跑得比强行套结构的项目更稳——因为开发者始终清楚每一行代码在哪、为什么在那里。










