首页
/ Go 标准项目布局深度解析:基于 project-layout 仓库的目录组织实战指南

Go 标准项目布局深度解析:基于 project-layout 仓库的目录组织实战指南

2026-09-05 18:12:48作者:俞予舒Fleming

本文基于 project-layout 仓库的法语文档 README_fr.md(Standard Go Project Layout)完整展开,系统讲解标准 Go 项目布局中 cmdinternalpkgvendorapiweb 等各级目录的定位与取舍原则,并结合本仓库真实存在的骨架目录、go.modMakefile.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 参与完善。

文档还给出了风格与命名的入门建议:遇到命名、格式、风格问题时,先跑一遍 gofmtgolint,再研读 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

其中几个配置文件直接印证了文档的原则:

  1. go.mod:仅两行——module github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAMEgo 1.19。这正是文档中“模块路径默认按 GitHub 托管书写、使用时替换为你自己的用户/组织与仓库名”的活例子。
  2. Makefile:只有一行注释 # note: call scripts from /scripts——刻意保持 Makefile 极简,把构建、安装、分析等操作全部委托给 /scripts 目录下的脚本,这与下文 /scripts 一节的设计意图完全一致。
  3. .gitignore:忽略 .DS_Store、二进制产物(*.exe*.dll*.so*.dylib)、测试二进制(*.test)、覆盖率输出(*.out)、项目级 glide 缓存(.glide/);其中 # vendor/ 一行被注释掉——默认不忽略 vendor,需要时取消注释即可,对应了“库项目不要提交依赖”的告诫。
  4. .editorconfig:统一了 charset = utf-8end_of_line = lf、文件末尾换行与行尾去空格;并对不同文件类型规定了缩进策略(.goMakefilego.modgo.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 存放配置文件模板或默认配置confdconsul-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.mdtest/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;
  • 不要把根级 /srcGOPATH 工作区中的 /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:用 gofmtgo vetgocyclogolintineffassignlicensemisspell 等命令扫描代码并出具评分徽章——这份工具清单本身就是 Go 项目质量检查的常用基线;
  • Pkg.go.dev:Go 文档发现平台,可通过其徽章生成工具为模块创建徽章(原 GoDoc 在线文档服务已被其取代);
  • Release 徽章:展示项目最新版本号。

配套的命名与风格自查流程,如前所述,是 gofmt + golint 先行,再对照官方与社区命名指引。.editorconfig 则从编辑器层面固化了这套约定。

九、实操要点:克隆与裁剪模板

README_fr.md 全文收敛为可执行的落地步骤:

  1. 克隆仓库后先做减法:以 go.mod 的模块路径占位为起点,替换为你的 用户/组织/仓库名;按项目类型删除用不到的目录——非 Web 项目删 /web,纯库项目删 /cmd 并确认不提交 vendor/(参考 .gitignore 中被注释的 # vendor/ 行),无外部依赖管理诉求的项目删 /third_party
  2. 代码放置三问:能被外部复用的公共库 → pkg/;不想被复用的内部逻辑 → internal/(可用 internal/appinternal/pkg 二级结构,参照本仓库 internal/app/_your_app_internal/pkg/_your_private_lib_ 占位);应用入口 → cmd/,且 main 保持极小;
  3. 构建逻辑下沉:根 Makefile 只留入口注释(参照本仓库 Makefile),构建/安装/分析脚本放 /scripts,打包与 CI 配置分别进 /build/package/build/ci
  4. 依赖管理默认走 Go Modules(Go 1.14+ 生产可用);确需离线/受限环境时 go mod vendor 生成 /vendor,旧版本工具链配合 -mod=vendor 构建;
  5. 部署与初始化资产归位:IaaS/PaaS/K8s/Helm/Terraform 配置进 /deployments(或 /deploy),systemd/supervisord 单元进 /init,confd/consul-template 模板进 /configs
  6. 始终避免根级 /src;测试数据用 testdata 或以 ./_ 前缀命名以被 Go 工具链忽略。

最后,README_fr.md 的 Notes 一节说明:一个包含可复用代码、脚本与配置、不那么通用的项目模板正在社区制作中,关注该仓库动态可以获取后续演进。

十、附录:多语言文档索引

该布局文档维护了 19 个语言版本,仓库内均可直接查阅:英文한국어简体中文正體中文Français日本語PortuguêsEspañolRomânăРусскийTürkçeItalianoTiếng ViệtУкраїнськаBahasa Indonesiaहिन्दीБеларуская,另有 README_fa.mdREADME.md 等版本与 LICENSE.md 协议文件。本文为法语版的中文展开,各节表述与 README_fr.md 原文一一对应,实现细节则以仓库内的目录骨架与各目录 README 为准。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384