工程化
工程化解决的是"代码之外"的问题:代码放在哪里、质量由谁把关、提交后如何验证、最终如何交付。本章给出一套被广泛验证的最小工程实践。
标准项目布局
Go 社区没有强制的目录规范,但存在事实标准。中型服务的典型布局:
两条核心原则:
internal/是编译器强制的私有边界:internal下的包只能被其父目录内的代码导入,外部项目无法引用,公共 API 与内部实现天然隔离。cmd/<名称>/main.go只做装配:解析配置、连接依赖、注入构造函数后启动服务;业务逻辑一律下沉到internal。
静态检查:golangci-lint
go vet 与 gofmt 是底线;生产项目普遍使用 golangci-lint 聚合数十个检查器。仓库根目录放置 .golangci.yml(v2 配置格式):
Tip
配置原则:从 default: standard 起步,按团队痛点逐个启用额外检查器。一次性开满上百个 linter 只会产生海量告警,让所有人关掉检查。CI 与本地必须使用同一份配置。
Makefile:统一命令入口
把团队约定固化为命令,降低"怎么跑测试/怎么构建"的沟通成本:
CI:GitHub Actions
推送与 PR 时自动执行"检查 + 测试 + 构建",这是质量的最后一道闸门:
要点:go-version-file: go.mod 让 CI 的 Go 版本跟随模块声明,避免环境漂移;测试永远带 -race。
交付:Docker 多阶段构建
Go 编译出的是静态二进制,镜像只需极小的运行时层:
:::tip 多阶段构建的关键点:
CGO_ENABLED=0生成纯静态二进制,可在 distroless/alpine 等无 libc 镜像中运行。- 先
COPY go.mod go.sum再go mod download,源码变更时不重装依赖,构建缓存命中率大幅提升。 -ldflags="-s -w"去除调试符号,镜像体积减小约 30%。- 用
nonroot用户运行,收窄容器被攻破后的权限;distroless 不含 shell,进一步减少攻击面。 .dockerignore中排除.git、bin、doc_build等目录,加快构建上下文上传。 :::
小结
- 布局记两条就够:入口在
cmd/,实现沉到internal/。 .golangci.yml(v2 格式)统一本地与 CI 的质量标准,从 standard 起步。- CI 最小闭环:vet → test -race → lint → build。
- 多阶段 Docker 构建 + distroless,交付产物小而安全。
下一章把这些实践全部落地:实现一个带认证、测试与容器化部署的短链接服务。