Go 标准项目布局深度解析:基于 project-layout 仓库的目录组织实战指南
本文基于 project-layout 仓库的法语文档 README_fr.md(Standard Go Project Layout)完整展开,系统讲解标准 Go 项目布局中 cmd、internal、pkg、vendor、api、web 等各级目录的定位与取舍原则,并结合本仓库真实存在的骨架目录、go.mod、Makefile、.gitignore 与 .editorconfig 逐一印证。读完本文,你将能够为 Go 项目搭建一套职责清晰、对编译器语义(如 internal 包强制)有明确依据的目录结构,并知道哪些模式该保留、哪些应当删掉。
一、标准布局的定位与适用边界
README_fr.md 的开篇给出了一条必须牢记的定性说明:该仓库代表的布局并不是 Go 官方开发团队定义的标准,而是从 Go 生态中历史悠久或较新的项目中沉淀出的一组架构模式(pattern)。部分模式比其他模式更流行,文档中同时包含若干小幅改进以及许多大型应用中常见的目录。
文档针对读者群体划出了清晰的适用边界:
- Go 初学者、或只做个人小 side-project 的场景,该布局完全不适用——从一个单独的
main.go文件开始就足够了; - 随着项目演进,必须保持代码结构良好,否则很快就会陷入难以维护的代码、大量隐藏的依赖和全局状态(global state)的泥潭;
- 项目参与人数越多,稳健的结构就越重要。因此需要为“如何组织库和包”建立一种所有人一致的约定;
- 维护开源项目、或明确知道其他项目会 import 你的仓库代码时,就需要公开包与私有代码(即
internal)的明确区分; - 使用方式上,文档的原话是:克隆仓库,保留你需要的部分,删除其余的(Clonez le dépôt, gardez ce dont vous avez besoin et supprimez tout le reste !)。目录存在并不意味着你必须全部使用——包括
vendor在内,没有任何模式是普适的。
关于依赖管理,文档给出了明确的时间线结论:
- Go 1.14 起,Go Modules 已可用于生产环境。除非有非常具体的理由,否则应默认使用 Go Modules;使用模块后,无需再关心
$GOPATH,也无需预先规划项目放在哪个目录; - 仓库内的 go.mod 就体现了模块约定:
module github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAME默认假设仓库托管在 GitHub 上,但这不是强制要求。模块路径可以是任意值,只是路径的第一段(第一个组件)应当包含一个点——当前版本 Go 已不强制这一点,但使用较旧版本 Go 时,没有点可能导致构建失败; - 整个布局是刻意保持通用的,不试图强加某种特定的 Go 包结构;它是一项社区协作工程,发现新 pattern 或认为某个 pattern 需要更新时,应通过提交 issue 参与完善。
文档还给出了风格与命名的入门建议:遇到命名、格式、风格问题时,先跑一遍 gofmt 与 golint,再研读 Go 官方与社区的一系列命名指引(如 Effective Go 的 Names 一节、Go Wiki 的 CodeReviewComments、rakyll 的《Style guideline for Go packages》),以及 GopherCon 系列演讲(Peter Bourgon 的工业级编程最佳实践、Kat Zien 的《How Do You Structure Your Go Apps》、Edward Muller 的《Go Anti-Patterns》等)。
二、仓库真实骨架:一个可直接克隆的目录模板
README_fr.md 所描述的布局,在仓库本身中以“占位骨架”的形式完整落地。仓库根目录的真实结构如下(各占位目录内以 .keep 文件保持空目录存在):
api/ assets/ build/
├── ci/ (.keep)
└── package/ (.keep)
cmd/
└── _your_app_/ (.keep)
configs/ deployments/ docs/
examples/ githooks/ init/
internal/
├── app/_your_app_/ (.keep)
└── pkg/_your_private_lib_/(.keep)
pkg/
└── _your_public_lib_/ (.keep)
scripts/ test/ third_party/
tools/ vendor/ website/
web/
├── app/ (.keep)
├── static/ (.keep)
└── template/ (.keep)
go.mod Makefile .gitignore .editorconfig LICENSE.md README*.md
其中几个配置文件直接印证了文档的原则:
- go.mod:仅两行——
module github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAME与go 1.19。这正是文档中“模块路径默认按 GitHub 托管书写、使用时替换为你自己的用户/组织与仓库名”的活例子。 - Makefile:只有一行注释
# note: call scripts from /scripts——刻意保持 Makefile 极简,把构建、安装、分析等操作全部委托给/scripts目录下的脚本,这与下文/scripts一节的设计意图完全一致。 - .gitignore:忽略
.DS_Store、二进制产物(*.exe、*.dll、*.so、*.dylib)、测试二进制(*.test)、覆盖率输出(*.out)、项目级 glide 缓存(.glide/);其中# vendor/一行被注释掉——默认不忽略 vendor,需要时取消注释即可,对应了“库项目不要提交依赖”的告诫。 - .editorconfig:统一了
charset = utf-8、end_of_line = lf、文件末尾换行与行尾去空格;并对不同文件类型规定了缩进策略(.go、Makefile、go.mod、go.sum及 Markdown 用 Tab,yml/yaml/json用 2 空格,JS/TS/Python 系用 4 空格),保证多人协作时格式一致。
三、Go 核心代码目录
/cmd:应用入口
README_fr.md 对 /cmd 的定义是:本项目的全部主应用。三条实操规则:
- 每个应用的目录名应当与你期望生成的可执行文件名一致(例如
/cmd/myapp对应可执行文件myapp); - 不要把大量代码放在应用目录里。如果代码可以被其他项目 import 和复用,移入
/pkg;如果代码不可复用、或你不想让别人复用,放入/internal。文档特别提醒:要对自己的意图保持显式,否则你会惊讶于其他开发者如何“使用”你的代码; - 常见做法是一个小小的
main函数,只负责 import 并调用/internal与/pkg中的代码,别无其他。
仓库中 cmd/README.md 补充了业界参照:velero、moby、prometheus、influxdb、kubernetes、dapr、go-ethereum 等项目的 cmd 目录都遵循“极小的 main 函数 + 其余逻辑在包中”的模式。本仓库的占位目录 cmd/_your_app_ 正是这一约定的落点。
/internal:编译器强制的私有代码
/internal 存放私有应用与库代码——即你不想被其他应用或库 import 的代码。文档强调一个关键实现事实:这个模式是由 Go 编译器本身强制执行的(可回溯至 Go 1.4 的 release notes),而不是靠约定。
两条常被忽略的细节:
internal不局限于顶层。你可以在项目树的任意层级放置多个internal目录;- 可以额外增加一层结构来区分享用与非分享的内部代码:应用自身代码放
/internal/app(如/internal/app/myapp),各应用间共享的代码放/internal/pkg(如/internal/pkg/myprivlib)。这并非强制(小项目尤其不必),但它提供了关于包用途的视觉线索。
本仓库的骨架正是后一种组织方式的模板:internal/app/_your_app_/ 与 internal/pkg/_your_private_lib_/(见 internal/README.md,其中还列举了 terraform、influxdb、jaeger、moby、minio 等采用 internal 的项目,以及 hashicorp/waypoint 风格的 internal/pkg 实例)。
/pkg:可声明为对外公开的库
/pkg 存放可被外部应用安全复用的代码(例如 /pkg/mypubliclib)。文档给出了三条判断依据:
- 其他项目会 import 这些库并默认它们持续可用,因此把代码放进
/pkg前要三思; - 真正保证包私有且不可 import 的方式是
internal(编译器强制);/pkg的价值在于显式沟通“这里的代码可以放心被他人使用”。Travis Jeffery 的博客《I'll take pkg over internal》对两者的边界有更细致的讨论; - 另一个收益是:当根目录混杂了大量非 Go 组件时,把 Go 代码集中到
pkg便于运行各类 Go 工具(GopherCon EU 2018《Best Practices for Industrial Programming》、Kat Zien 与 Massimiliano Pippi 的演讲都提到这一点)。
文档对 pkg 的态度非常诚实:这不是一个被普遍接受的 pattern,社区里有人不推荐它;小型项目多一层嵌套未必有收益,不必强用(除非你真心想要)。当项目变大、根目录开始杂乱(尤其有大量非 Go 组件)时,再考虑引入。本仓库的 pkg/README.md 进一步交代了 pkg 目录的渊源——早期 Go 源码树自身用 pkg 组织包,社区项目随之一路沿袭,并给出了 containerd、istio、helm、kubernetes、moby、grafana、cockroach、etcd、datadog-agent、cilium 等一大批采用者的清单,作为“社区常见但非共识”的注脚。对应占位目录为 pkg/_your_public_lib_/。
/vendor:依赖目录与模块代理
/vendor 存放应用依赖,可手工管理,也可用你偏好的依赖管理工具,或 Go 内置的 Modules 功能。文档给出的操作要点:
go mod vendor命令会为你生成/vendor目录;- 若未使用 Go 1.14+(1.14 起默认启用 vendor 模式),执行
go build时可能需要显式加上-mod=vendor标志; - 如果你开发的是库(library),不要提交你的依赖。
文档还交代了一个演进事实:自 Go 1.13 起,Go 启用了模块代理(module proxy)功能,默认使用 proxy.golang.org 作为代理服务器。如果该机制满足你的需求与合规约束,就完全不需要 vendor 目录。仓库的 .gitignore 中 # vendor/ 处于注释状态,vendor/ 目录当前只包含 vendor/README.md 的说明性内容——这正示范了“库项目不提交依赖”的默认姿态。
四、服务与 Web 应用目录
/api:API 规格与协议定义
/api 存放 OpenAPI/Swagger 规格、JSON Schema 文件、协议定义文件。本仓库的 api/README.md 以 kubernetes 与 moby 的 api 目录为参照实例。这一目录面向的是“服务”型 Go 项目:接口契约独立于实现存放,便于多方按同一规格对接。
/web:Web 前端组件
/web 存放 Web 应用专属组件:静态资源、服务端模板与 SPA。本仓库的骨架进一步细分了三个占位子目录:
web/app/:单页应用(SPA)入口;web/static/:静态资源(JS/CSS/图片等);web/template/:服务端渲染模板。
见 web/README.md。对于非 Web 的纯后端项目,整个 /web 目录可以直接删除——这正是“保留需要的、删除其余”原则的典型应用场景。
五、应用通用目录
/configs:配置模板与默认配置
/configs 存放配置文件模板或默认配置,confd 与 consul-template 的模板文件也放在这里(见 configs/README.md)。它与 Go 代码目录的关系是:模板化的配置在部署时由配置管理系统渲染,而默认值随仓库版本化。
/init:系统初始化与进程监管
/init 存放系统初始化单元(systemd、upstart、sysvinit)以及进程管理器/监管器(runit、supervisord)的配置。服务化部署的 Go 应用通常需要一个 systemd unit 或 supervisord 配置来管理进程生命周期,集中放在此目录可避免它们散落在仓库各处。
/scripts:把 Makefile 保持简单
/scripts 存放执行构建、安装、分析等各类操作的脚本。scripts/README.md 给出的核心理由是:这些脚本让根目录的 Makefile 保持精简(terraform 的 Makefile 是典型范例)。这一设计在本仓库中得到了最直接的印证:根 Makefile 全部正文只有一行注释 # note: call scripts from /scripts——即 Makefile 只做入口,具体逻辑全部下沉到 scripts/。
/build:打包与持续集成
/build 面向打包(Packaging)与持续集成(CI),仓库内已落地两个子目录(均含 .keep 占位):
/build/package:云端(AMI)、容器(Docker)、操作系统(deb、rpm、pkg)等打包的脚本与配置;/build/ci:CI(travis、circle、drone)的脚本与配置。文档特别提醒:某些 CI 工具(如 Travis CI)对配置文件的存放位置要求非常严格,尽量把配置放在/build/ci并链接(或复制)到工具期望的位置;如果做不到,放在根目录也无妨。
/deployments:基础设施与编排
/deployments 存放 IaaS、PaaS、系统与容器编排的部署模板和配置:docker-compose、kubernetes/helm、mesos、terraform、bosh 等。文档补充了一个命名变体:在部分项目(主要是经 Kubernetes 部署的应用)中,这个目录叫 /deploy。
/test:外部测试应用与测试数据
/test 存放额外的外部测试应用与测试数据,内部结构可自由组织。文档给出的两条与 Go 工具链直接相关的事实:
- 较大的项目建议设置数据子目录,例如
/test/data,或者使用/test/testdata——testdata是 Go 工具链会自动忽略的目录名,适合放置不参与构建的测试数据; - Go 同时忽略以
.或_开头的目录和文件,这为测试数据目录命名提供了更大灵活性。
build/README.md 与 test/README.md 分别以 cockroach 与 openshift/origin(测试数据位于 /testdata 子目录)作为参照实例。
六、其他通用目录
| 目录 | 用途(据 README_fr.md) | 仓库内补充证据 |
|---|---|---|
/docs |
用户与设计文档(在 GoDoc 生成文档之外) | docs/README.md 列举 hugo、openshift、dapr 实例 |
/tools |
项目支撑工具;这些脚本可以 import /pkg 与 /internal 的代码 |
tools/README.md 列举 istio、openshift、dapr 实例 |
/examples |
应用与/或公共库的使用示例 | examples/README.md 列举 nats.go、docker-slim、packer 实例 |
/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 列举 vault、perkeep 实例 |
其中 /tools 值得单独强调:文档明确指出 tools 中的脚本可以 import /pkg 和 /internal 的代码——即工具链代码与业务代码共享同一模块边界,这是它与 /cmd(对外部使用者而言只是入口)在职责上的关键差别。
七、/src:一个应当避免的目录
README_fr.md 专门辟出一节告诫:Go 项目中不应出现根级 /src 目录。其理由与常见误解:
- 出现
src的 Go 项目,通常源于开发者来自 Java 世界——这是 Java 的惯例,文档直言你并不希望自己的 Go 代码看起来像 Java; - 不要把根级
/src与 GOPATH 工作区中的/src混为一谈:环境变量$GOPATH指向当前工作区(非 Windows 系统默认为$HOME/go),该工作区包含/pkg、/bin、/src三个目录;你的项目本身位于工作区的/src子目录下。若项目里再建一个/src,代码文件的完整路径会变成/some/path/to/workspace/src/your_project/src/your_code.go这样的双重嵌套; - 文档补充了一个版本事实:自 Go 1.11 起,项目可以放在 GOPATH 之外——但这仍然不构成使用
/src目录的理由(本仓库的 go.mod 也表明当前模块模式下项目位置完全自由)。
八、命名、风格与质量徽章
在“命名与组织”之外,README_fr.md 的 Badges 一节推荐了面向开源仓库的三组质量标识(引用时把徽章指向的仓库地址替换为你自己的项目地址即可):
- Go Report Card:用
gofmt、go vet、gocyclo、golint、ineffassign、license、misspell等命令扫描代码并出具评分徽章——这份工具清单本身就是 Go 项目质量检查的常用基线; - Pkg.go.dev:Go 文档发现平台,可通过其徽章生成工具为模块创建徽章(原 GoDoc 在线文档服务已被其取代);
- Release 徽章:展示项目最新版本号。
配套的命名与风格自查流程,如前所述,是 gofmt + golint 先行,再对照官方与社区命名指引。.editorconfig 则从编辑器层面固化了这套约定。
九、实操要点:克隆与裁剪模板
把 README_fr.md 全文收敛为可执行的落地步骤:
- 克隆仓库后先做减法:以 go.mod 的模块路径占位为起点,替换为你的
用户/组织/仓库名;按项目类型删除用不到的目录——非 Web 项目删/web,纯库项目删/cmd并确认不提交vendor/(参考 .gitignore 中被注释的# vendor/行),无外部依赖管理诉求的项目删/third_party; - 代码放置三问:能被外部复用的公共库 →
pkg/;不想被复用的内部逻辑 →internal/(可用internal/app与internal/pkg二级结构,参照本仓库internal/app/_your_app_、internal/pkg/_your_private_lib_占位);应用入口 →cmd/,且main保持极小; - 构建逻辑下沉:根 Makefile 只留入口注释(参照本仓库 Makefile),构建/安装/分析脚本放
/scripts,打包与 CI 配置分别进/build/package与/build/ci; - 依赖管理默认走 Go Modules(Go 1.14+ 生产可用);确需离线/受限环境时
go mod vendor生成/vendor,旧版本工具链配合-mod=vendor构建; - 部署与初始化资产归位:IaaS/PaaS/K8s/Helm/Terraform 配置进
/deployments(或/deploy),systemd/supervisord 单元进/init,confd/consul-template 模板进/configs; - 始终避免根级
/src;测试数据用testdata或以./_前缀命名以被 Go 工具链忽略。
最后,README_fr.md 的 Notes 一节说明:一个包含可复用代码、脚本与配置、不那么通用的项目模板正在社区制作中,关注该仓库动态可以获取后续演进。
十、附录:多语言文档索引
该布局文档维护了 19 个语言版本,仓库内均可直接查阅:英文、한국어、简体中文、正體中文、Français、日本語、Português、Español、Română、Русский、Türkçe、Italiano、Tiếng Việt、Українська、Bahasa Indonesia、हिन्दी、Беларуская,另有 README_fa.md、README.md 等版本与 LICENSE.md 协议文件。本文为法语版的中文展开,各节表述与 README_fr.md 原文一一对应,实现细节则以仓库内的目录骨架与各目录 README 为准。
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 StartedRust0623
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