基于 project-layout 的 Go 标准项目布局:目录规范、适用场景与源码级实践指南
本文以本仓库的意大利语文档 README_it.md 为主体,系统讲解 Go 社区通用的标准项目布局(Standard Go Project Layout):每个核心目录(cmd、internal、pkg、api、web、configs、scripts、deployments 等)的职责、命名规则与取舍依据。读完之后,你将能够为自己的 Go 应用挑选合适的目录骨架、理解 internal 包的编译器级强制机制,并基于本仓库模板快速搭建一个结构清晰的 Go 项目。
一、这套布局的定位:社区模式集合,而非官方标准
文档开宗明义地强调:这套布局并不是 Go 核心团队定义的官方标准。它是 Go 生态中长期沉淀下来的一组常见历史与新兴项目布局模式,其中一些模式比其他模式更流行;同时它也附带了一些小改进,以及几乎所有足够大的真实世界应用都会用到的若干支撑目录。
文档还给出三个关键定位:
- 刻意保持通用性:这套结构不试图强加某种特定的 Go 包结构(例如 Clean Architecture 之类的分层方式就不在此讨论范围内);
- 社区协作成果:如果你发现新的模式,或者认为某个现有模式需要更新,应当通过 issue 提出;
- 按需裁剪:目录"存在"不代表"必须使用"——连
vendor模式也不是万能的。
什么时候该用、什么时候不该用
文档用加粗语气给出了一条最重要的建议:
如果你正在学习 Go,或者只是在开发 PoC(概念验证)或个人简单项目,这套布局是不必要的复杂化。请从真正简单的结构开始——一个
main.go文件和go.mod就足够了。
随着项目演进,布局的重要性分阶段上升:
- 项目增长期:要时刻保证代码结构良好,否则会演变成"一堆隐藏依赖 + 全局状态"的混乱代码;
- 多人协作期:需要更有结构的布局,并引入管理包/库的通用方式;
- 开源或被依赖期:当你的仓库是开源项目、或有其他项目会 import 你的代码时,就必须明确私有包与代码(即
internal目录)的边界。
文档给出的落地建议是:克隆本仓库,保留你需要的部分,删除其余一切。克隆命令如下(这是全文唯一涉及仓库地址的场景):
git clone https://gitcode.com/GitHub_Trending/pr/project-layout
Go Modules 前提:从 Go 1.14 起不再被 $GOPATH 束缚
从 Go 1.14 开始,Go Modules 正式达到生产可用。文档建议:除非你有特定理由不用,否则一律使用 Go Modules——这样你就不必再操心 $GOPATH 和项目放置位置的问题。
关于模块路径(module path)有一条容易被忽视的细节:
- 仓库自带的 go.mod 文件内容非常简洁:
// go.mod (仓库根目录)
module github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAME
go 1.19
- 文档说明这份
go.mod默认假设你的项目托管在 GitHub 上,但这并非强制要求——模块路径可以是任意值; - 不过模块路径的第一段应当包含一个点(域名形式)。当前版本的 Go 已不再强制这一点,但如果你使用稍旧版本的 Go,构建失败时不妨先怀疑这里(文档同时引用了 golang/go 的 issue #37554 与 #32819 供进一步阅读,见官方仓库)。
命名、格式与风格:先跑工具,再读指南
文档建议遇到命名、格式、风格问题时,先从运行 gofmt 开始。需要说明的是,意大利语文档中提到的标准 linter 是 golint,而仓库中最新的英文主文档 README.md 已更新为推荐 staticcheck——因为 golint 现已弃停维护;若以当前仓库状态为准,优先使用 staticcheck 这类仍在维护的 linter。
此外文档列出了一批必读的命名与风格指南(此处仅保留条目名称,不再附外部链接):
- "Go Naming Conventions" 演讲(talks.golang.org,2014)
- Effective Go 的 Naming 章节
- 《Package names in Go》官方博客
- CodeReviewComments wiki
- rakyll/JBD 的《Go 包风格指南》(Package-Oriented Style)
以及关于包命名、包组织与代码结构建议的四场经典演讲(GopherCon EU 2018 Peter Bourgon 的工业级编程最佳实践、GopherCon Russia 2018 的 Go best practices、GopherCon 2017 Edward Muller 的 Go 反模式、GopherCon 2018 Kat Zien 的 Go 应用结构),和一篇关于"面向包的设计与架构分层"的中文文章。
二、Go 目录:cmd、internal、pkg、vendor
这是文档中权重最高的四个 Go 专属目录,也是整套布局的核心骨架。
/cmd:本项目的主应用程序
/cmd 存放本项目的主应用(可执行程序)。规则有四点:
- 每个应用的目录名应与期望的可执行文件名一致(例如
/cmd/myapp); - 不要在应用目录里堆大量代码:如果代码可能被其他项目 import 复用,就放进
/pkg;如果不可复用、或你明确不希望别人复用,就放进/internal——"你无法预测别人会怎么 import 你的代码,所以要把意图表达得足够显式"; - 最常见、也最推荐的做法是:写一个很小的
main函数,只做 import 并调用/internal与/pkg中的代码,别无其他; - 子目录说明文档 cmd/README.md 列出了采用该模式的主流项目,包括 velero、moby、prometheus、influxdb、kubernetes、dapr、go-ethereum 等。
本仓库中 cmd/_your_app_/ 就是一个空占位目录,提示你按可执行文件名创建子目录。
/internal:编译器强制的私有代码
/internal 存放"不希望他人 import"的私有应用与库代码。文档强调两个机制性事实:
- 该模式由 Go 编译器本身强制:只要包位于
internal目录下,其他包就无法 import 它(除非共享公共祖先路径)。这是自 Go 1.4 起的行为(见 Go 1.4 发布说明中的 internal packages 一节); internal不限于顶层:你可以在项目树的任意层级放置多个internal目录。
文档还推荐了一个可选的二级结构,用于区分"共享的内部代码"与"非共享的内部代码"(小项目不必做,但能提供更清晰的包用途暗示):
- 实际应用代码放在
/internal/app(例如/internal/app/myapp); - 这些应用共享的内部代码放在
/internal/pkg(例如/internal/pkg/myprivlib)。
这一推荐结构在本仓库中被真实落地为占位目录:
internal/
├── app/
│ └── _your_app_/ # 应用私有代码(不共享)
└── pkg/
└── _your_private_lib_/ # 应用间共享的私有库
子目录文档 internal/README.md 进一步列出了使用 internal 的知名项目:hashicorp/terraform、influxdb、perkeep、jaeger、moby、minio 等,其中 hashicorp/waypoint 展示了 /internal/pkg 的用法。
/pkg:明确"可供外部使用"的公开库
/pkg 存放允许外部应用使用的库代码(例如 /pkg/mypubliclib)。文档对此目录的态度可以概括为三层:
- 谨慎放入:其他项目会 import 这些库并"默认它们能正常工作",因此放任何东西进去之前要想清楚;
internal才是硬保证:真正从机制上阻止 import 的是internal目录(Go 编译器强制),而/pkg的价值在于**显式地对外传达"这里的代码可以被安全使用"**这一契约。社区作者 Travis Jeffery 的博文《I'll take pkg over internal》对pkg与internal的取舍给出了很好的综述(见子目录文档 pkg/README.md 的引用);- 它也是工程组织手段:当项目根目录混杂大量非 Go 组件时,把 Go 代码统一收拢到
/pkg下可以让各种 Go 工具更好用——这一点在 GopherCon EU 2018(Peter Bourgon)、GopherCon 2018(Kat Zien)与 GoLab 2018(Massimiliano Pippi)三场演讲中均有论述。
文档同时坦诚了社区争议:pkg 是一个常见但并非普遍接受的模式,Go 社区中有人并不推荐它;如果你的项目很小、多一层嵌套没有价值,完全可以不用。pkg/README.md 附有一长串使用 pkg 布局的知名仓库清单(containerd、istio、helm、k3s、kubernetes、moby、grafana、cockroach、etcd、linkerd、spire 等),可作为"采用与否"的参考样本。
最后,文档交代了 pkg 目录的历史起源:早期 Go 官方源码树曾用 pkg 目录存放其包,社区项目随之模仿了这一模式(Brad Fitzpatrick 的相关推文提供了更多背景)。
/vendor:应用依赖
/vendor 存放应用依赖——可以手工管理,也可以用你偏好的依赖管理工具,比如内置的 Go Modules:
- 执行
go mod vendor命令即可自动生成/vendor目录; - 注意:如果你没有使用 Go 1.14(该版本起默认启用 vendor 模式),可能需要在
go build命令上追加-mod=vendor标志; - 构建库时不要提交你的应用依赖;
- 自 Go 1.13 起,Go 启用了 module proxy 特性(默认使用官方代理服务器 proxy.golang.org)。如果你的需求与约束能被它满足,就完全不需要
vendor目录。
本仓库当前并未包含 vendor 目录,与"依赖交给模块代理/go mod vendor 生成"的定位一致。
三、服务应用目录:/api
/api 存放 OpenAPI/Swagger 规范、JSON schema 文件与协议定义文件。这是一个面向服务化应用的目录:接口契约与实现代码分离,便于生成客户端或文档。子目录文档 api/README.md 给出的参考项目是 kubernetes 与 moby 的 api 目录。
四、Web 应用目录:/web
/web 存放 Web 应用专属组件:静态 Web 资源、服务端模板与 SPA(单页应用)。从本仓库的实际结构看,该目录已被细分为三个占位子目录,给出了推荐的组织方式:
web/
├── app/ # 前端应用(如 SPA 源码)
├── static/ # 静态资源
└── template/ # 服务端模板
对应的目录说明见 web/README.md。
五、通用应用目录:configs、init、scripts、build、deployments、test
/configs:配置模板与默认配置
存放配置文件模板或默认配置,例如 confd 或 consul-template 的模板文件也应放在这里。说明见 configs/README.md。
/init:系统初始化与进程管理
存放系统初始化(systemd、upstart、sysv)与进程管理器/守护器(runit、supervisord)的配置。说明见 init/README.md。
/scripts:构建与运维脚本
存放执行各类构建、安装、分析等操作的脚本。文档指出这类脚本的核心作用是让根级 Makefile 保持小巧、直接(以 hashicorp/terraform 的 Makefile 为范例)。
这一点在本仓库中被贯彻到了极致——仓库根目录的 Makefile 全文只有一行注释:
# note: call scripts from /scripts
即所有实际逻辑都被推给了 /scripts 下的脚本,根 Makefile 仅作为"入口提示"。这正示范了文档所说的"根 Makefile 小而简单"原则。更多示例见 scripts/README.md。
/build:打包与持续集成
/build 承担打包(Packaging)与 CI 两类职责,文档建议拆成两个子目录:
/build/package:放置云镜像(AMI)、容器(Docker)、操作系统包(deb、rpm、pkg)的打包配置与脚本;/build/ci:放置 CI(travis、circle、drone)的配置与脚本。文档特别提醒:某些 CI 工具(如 Travis CI)对配置文件位置要求非常严格,可以把配置放在/build/ci下,再通过链接(link)指到 CI 工具期望的路径(在可行的情况下)。
/deployments:部署配置与模板
存放 IaaS、PaaS、系统级以及容器编排的部署配置与模板:docker-compose、kubernetes/helm、mesos、terraform、bosh 等。文档注意到一个命名差异:在部分仓库中(尤其是用 kubernetes 部署的应用),这个目录叫 /deploy。本仓库采用了 deployments/ 命名,说明见 deployments/README.md。
/test:外部测试应用与测试数据
/test 存放额外的外部测试应用与测试数据,内部结构可自由组织。文档给出两条与 Go 工具链直接相关的实用规则:
- 大型项目建议设一个数据子目录,如
/test/data;若需要 Go 工具链忽略目录内容,应命名为/test/testdata(Go 的构建/测试工具会自动跳过testdata目录); - 由于 Go 同样会忽略以
.或_开头的目录和文件,测试数据目录的命名还有更大自由度——这也解释了本仓库为何大量使用_your_app_、_your_private_lib_这类下划线前缀占位目录:它们既是模板占位符,又天然被 Go 工具忽略。
示例见 test/README.md。
六、其他目录:docs、tools、examples、third_party、githooks、assets、website
| 目录 | 职责 | 补充说明 |
|---|---|---|
/docs |
用户文档与设计文档(补充在自动生成的 godoc 文档之外) | docs/README.md |
/tools |
项目的支撑工具;注意这些工具可以 import /pkg 与 /internal 中的代码 |
tools/README.md |
/examples |
应用与/或公开库的使用示例 | examples/README.md |
/third_party |
外部辅助工具、fork 的代码、其他第三方工具(如 Swagger UI) | third_party/README.md |
/githooks |
Git hooks | githooks/README.md |
/assets |
仓库附带的其他资源(图片、logo 等) | assets/README.md |
/website |
如果不使用 GitHub pages,项目网站数据放在这里 | website/README.md |
值得注意的是 /tools 的 import 规则:工具既依赖项目代码(可 import pkg/internal),又不应被主程序反向依赖——这为"脚手架/代码生成器/内部 CLI"类工具划定了一个安全的存放位置。
七、不该出现的目录:/src
文档专设一节警告不要在 Go 项目中使用 /src 目录。理由分两层:
- 来源判断:Go 项目出现
src目录,通常是因为开发者来自 Java 世界(Java 中src是常见模式)。文档直接建议:尽量别把 Go 项目做得像 Java 项目; - 避免与 Go workspace 的
/src混淆:$GOPATH环境变量指向(当前的)工作区(非 Windows 系统上默认是$HOME/go),该工作区包含顶层的/pkg、/bin与/src三个目录;你的实际项目会落在工作区的/src之下。如果项目自身再嵌套一个/src,路径就会变成/some/path/to/workspace/src/your_project/src/your_code.go这种双层src。文档最后补充:虽然自 Go 1.11 起项目可以放在GOPATH之外,但这并不意味着/src布局就是好主意。
八、仓库骨架总览:一个可裁剪的纯模板
结合上文对文档各目录的解读,再看本仓库的实际骨架,可以完整对照出这套布局的全貌(仓库内不含任何 .go 源码文件,是一个纯目录模板,配合 LICENSE.md 与上文所示的 go.mod、Makefile):
.
├── api/ # OpenAPI/Swagger/JSON schema/协议定义
├── assets/ # 图片、logo 等仓库资源
├── cmd/
│ └── _your_app_/ # 主应用(目录名=可执行文件名)
├── configs/ # 配置模板/默认配置(含 confd、consul-template)
├── deployments/ # IaaS/PaaS/编排部署(docker-compose、k8s/helm、terraform…)
├── docs/ # 用户与设计文档
├── examples/ # 应用/公开库示例
├── githooks/ # Git hooks
├── init/ # systemd/upstart/sysv、runit、supervisord
├── internal/
│ ├── app/_your_app_/ # 应用私有代码
│ └── pkg/_your_private_lib_/ # 应用间共享私有库
├── pkg/
│ └── _your_public_lib_/ # 可供外部 import 的公开库
├── scripts/ # 构建/安装/分析脚本(根 Makefile 保持精简)
├── test/ # 外部测试应用与测试数据(testdata 可被 Go 忽略)
├── third_party/ # 外部工具、fork 代码
├── tools/ # 支撑工具(可 import pkg 与 internal)
├── web/
│ ├── app/ # SPA/前端应用
│ ├── static/ # 静态资源
│ └── template/ # 服务端模板
├── website/ # 项目网站数据(非 GitHub pages 时)
├── go.mod # module 路径为占位符,需替换为你自己的
├── Makefile # 仅一行:call scripts from /scripts
└── LICENSE.md
使用流程与文档的结论一致:克隆仓库 → 保留所需目录、删除其余 → 替换 go.mod 中的模块路径占位符(github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAME)→ 把 cmd、internal、pkg 下带下划线前缀的占位目录重命名为真实名称。下划线前缀目录同时享受 Go 工具链自动忽略的便利。
九、Badges:仓库文档面板
文档最后给出了 README badge 的推荐用法(此处按当前仓库内容说明用途,不附外链):
- Go Report Card:会用
gofmt、go vet、gocyclo、golint、ineffassign、license、misspell扫描你的代码,把示例中的模块引用替换为你的项目即可; - Pkg.go.dev:Go 包发现与文档的新入口,可用其 badge 生成工具为项目创建 badge;
- Release badge:展示项目的最新 release 版本号,把链接指向你的项目即可。
十、小结
这套布局的完整心法可以压缩为三句话:
cmd薄、internal硬、pkg慎——可执行入口保持极小;私有性交给编译器强制的internal;公开 API 才进pkg并视为对外承诺;- 非 Go 组件各归其位——
api、web、configs、init、deployments、scripts、test、docs、website等目录让根目录始终可读; - 按需裁剪——学习期只用
main.go+go.mod,项目长大、多人协作、对外发布三个阶段逐级加结构;目录在模板中存在,不等于你必须使用它。
文档末尾的 Notes 还提到:一个包含简单可复用配置、脚本与代码的"更有主见的标准项目模板"仍在进行中(WIP),可作为后续关注方向。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00