当前位置:首页 > 文章列表 > Golang > Go教程 > 用 go.work 同时开发两个模块并保持各自发布独立

用 go.work 同时开发两个模块并保持各自发布独立

来源:17golang原创 2026-10-07 13:01:22 0浏览 收藏

两个 Go 模块可以放在同一仓库或相邻目录中,用 go.work 做本地联合开发,同时继续各自发布。关键是把工作区看成本地源码选择层:它让服务模块在开发时直接读取 API 模块源码,但不改写任何模块路径,也不要求把本地 replace 写进 go.mod。发布前再用 GOWORK=off 分别测试,才能确认每个模块只依赖真实可发布的版本。

官方教程:https://go.dev/doc/tutorial/workspaces

最小可用方案
  • 两个目录分别保留自己的 go.mod、go.sum 和版本标签。
  • 仓库根目录运行 go work init ./api ./service,不要向模块文件加入本地 replace。
  • 日常联调用工作区源码;发布前在每个模块内使用 GOWORK=off 测试。
  • API 先发布新版本,服务再关闭工作区升级 require,最后独立发布服务。

规模变大后,问题通常出在本地 replace

只有一个模块时,依赖边界很直观。拆出 API SDK 或公共库后,开发者往往在服务模块的 go.mod 中临时加入 replace corp.example/api => ../api。它确实能联调,却把个人目录结构写进了发布元数据:忘记删除会影响 CI,删除与恢复又会制造无意义差异,多人并行时还容易反复冲突。

go.work 把这类本地组合提升到模块之外。工作区的 use 指令声明哪些磁盘目录作为主模块参与当前命令;每个模块内部仍然保留自己的真实 require。这样,本地研发速度与发布契约不再由同一份文件承担。

工作区只负责本地组合,不合并模块身份

假设仓库中有 api 和 service 两个模块,服务线上依赖 API 的 v0.3.0,目录可以保持下面的结构:

# 两个模块各自保留发布文件,根目录只放工作区配置
repo/
├── go.work
├── api/
│   ├── go.mod
│   └── client/
└── service/
    ├── go.mod
    └── cmd/server/

service/go.mod 应记录消费者真正能够获取的模块版本,而不是本地相对路径:

// 服务模块的发布契约:依赖一个已经存在的 API 版本
module corp.example/service

go 1.23.0

require corp.example/api v0.3.0

然后在仓库根目录创建工作区:

# 创建工作区,并一次纳入两个已有模块
go work init ./api ./service

# 确认当前目录实际使用的工作区文件
go env GOWORK

生成的 go.work 本质上只是本地组合清单:

// 工作区配置:只选择本地模块,不改变它们的发布身份
go 1.23.0

use (
    ./api
    ./service
)

此时服务导入 corp.example/api/client,Go 命令会优先使用工作区中的 ./api 源码。即使 API 的未发布改动尚未打标签,服务也可以立刻编译联调;而 service/go.mod 仍明确表示它对外发布时依赖 v0.3.0。

go.work 本地工作区与两个独立 Go 模块之间的静态边界图
图1:多模块工作区边界图。go.work 只组合本地源码,API 与服务仍由各自的 go.mod 定义发布身份。

联合开发时如何运行和测试

在工作区根目录,命令可以显式覆盖两个模块的包模式。不要直接假设根目录的 go test ./... 会遍历所有模块,因为根目录本身通常不是一个模块。

# 在工作区上下文中联合测试两个模块
go test ./api/... ./service/...

# 新增第三个模块时,把它加入现有工作区
go work use ./worker

# 查看工作区里当前纳入的模块目录
go work edit -json

联合测试的价值是尽早发现跨模块接口变化。例如 API 修改了参数类型,服务会直接基于本地源码编译,不必先发布一个临时版本。但这只证明“两个当前目录一起工作”,还没有证明任何一个模块能单独发布。

用 GOWORK=off 验证真正的独立发布

go env GOWORK 会显示当前生效的工作区文件;设置 GOWORK=off 可以明确关闭工作区模式。发布门禁应该在每个模块目录分别关闭工作区,防止本地源码掩盖缺失版本或漏提交内容。

# 先验证 API 模块可以脱离工作区独立构建
cd api
GOWORK=off go mod tidy
GOWORK=off go test ./...

# 再验证服务只依赖 go.mod 中已发布的 API 版本
cd ../service
GOWORK=off go mod tidy
GOWORK=off go test ./...

如果工作区测试成功而服务的独立测试失败,通常说明服务使用了 API 的未发布接口。这个失败不是工作区的缺陷,恰恰是它揭示出的发布顺序问题:先让 API 形成可获取的新版本,再让服务显式升级。

Go 工作区联合开发与 GOWORK off 独立发布验证矩阵
图2:独立发布验证矩阵。联合开发使用工作区,发布前则关闭工作区,分别核对模块依赖、测试与标签。

两个模块各自发布的稳妥顺序

当 API 的本地改动已经被服务验证,可以先在 API 模块完成兼容性检查、测试和版本发布。假设新版本为 v0.4.0,服务随后关闭工作区升级真实依赖:

# 在服务模块中关闭工作区,并升级到刚发布的 API 版本
cd service
GOWORK=off go get corp.example/api@v0.4.0

# 整理服务模块自己的依赖账本并验证
GOWORK=off go mod tidy
GOWORK=off go test ./...

确认 service/go.mod 和 service/go.sum 的差异后,再给服务模块打自己的标签。API 和服务的版本号无需相同:API 可以发布 v0.4.0,服务仍按自己的语义版本发布。go.work 不参与消费者的模块下载,也不替两个模块决定标签。

为什么不建议日常直接运行 go work sync

go work sync 会根据工作区构建列表,把选中的依赖版本同步回各工作区模块的 go.mod。它适合团队明确要统一一批共享依赖版本的场景,但不是“让本地 API 生效”的必要步骤。仅仅进行本地跨模块开发时,use 已经足够。

如果无意中运行 sync,两个模块可能同时出现依赖版本变化,扩大审查范围,甚至让原本能够独立维护的模块产生不必要的版本耦合。需要使用时,应先确认目标版本,再逐个审查每份 go.mod 差异;不要把它当成 tidy 的工作区版本。

go.work 是否应该提交到仓库

Go 官方参考通常不建议提交 go.work:它可能覆盖开发者在父目录设置的工作区,也可能让 CI 测到与模块 go.mod 不同的依赖版本。对于发布边界清晰、可独立消费的模块,把 go.work 加入忽略规则往往更稳妥。

如果这是一个所有模块都强绑定、所有命令都以仓库根目录为入口的统一仓库,团队也可以约定提交工作区文件。无论选择哪种策略,发布 CI 都应增加 GOWORK=off 的逐模块测试;这比争论文件是否入库更能守住独立发布能力。工作区还可能生成 go.work.sum,它补充记录工作区所需但未被各模块 go.sum 集体覆盖的校验和,也不应被误认为某个模块的发布文件。

上线后的运行信号与后续改进

信号说明处理方式
工作区测试通过,GOWORK=off 失败使用了尚未发布的跨模块改动先发布被依赖模块,再升级依赖方
两个 go.mod 同时大量变化可能误用了 go work sync确认是否真的要统一版本并审查差异
CI 与本机选中版本不同工作区或工具链环境不一致记录 go env GOWORK,并在发布任务关闭工作区
服务 go.mod 出现本地 replace本地路径泄漏到发布契约移除 replace,改由 go.work 的 use 管理

随着模块数量增加,可以把“工作区联合测试”和“关闭工作区的逐模块测试”拆成两个 CI 任务。前者保护跨模块集成,后者保护每个模块对外可消费。两类结果同时稳定,才说明本地开发效率和独立发布边界都没有被牺牲。

常见问题

go.work 会覆盖 service/go.mod 中的 require 吗?

不会改写文件,但在工作区模式下,本地 use 模块会作为主模块参与版本选择和包加载,所以命令使用本地源码。关闭工作区后,service/go.mod 中的正式版本重新成为独立构建依据。

可以在 go.work 中写 replace 吗?

可以,工作区级 replace 会覆盖工作区内模块的替换关系;当多个主模块存在冲突的 replace 时,也必须在 go.work 中解决。不过本例只需 use 两个本地模块,不需要额外 replace。

为什么不让两个模块共用一个 go.mod?

共用 go.mod 意味着它们成为同一个模块,通常共享模块版本边界。若 API 需要被其他项目独立引用,或者服务与 API 有不同发布节奏,保留两个 go.mod 更符合目标。

如何临时确认命令没有偷偷使用工作区?

先运行 go env GOWORK 查看路径,再在关键命令前显式加 GOWORK=off。不要只依赖当前目录位置推断工作区是否生效,因为 Go 会向父目录查找 go.work。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
雾林月光下白鹿剪影的电影感手机壁纸提示词雾林月光下白鹿剪影的电影感手机壁纸提示词
上一篇
雾林月光下白鹿剪影的电影感手机壁纸提示词
死锁日志怎么看:还原事务交叉加锁的最短路径
下一篇
死锁日志怎么看:还原事务交叉加锁的最短路径
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    363次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    419次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    433次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    386次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    213次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码