Go 工作区入门:用 go work 同时开发多个本地模块

本文讲解 go work 的基本使用场景,包括多个本地模块联调、替代临时 replace、工作区文件和团队协作注意事项。

多模块本地联调不一定要改 go.mod

很多团队会把通用库和业务服务放在不同仓库或不同模块里。比如你正在开发 user-api,它依赖本地的 kit 模块。以前常见做法是在 user-api/go.mod 里写 replace

replace example.com/kit => ../kit

这能工作,但如果你不小心把个人本地路径提交了,其他同事可能无法构建。go work 提供了另一种方式:在本地创建工作区,把多个模块临时组合起来开发,而不必修改每个模块的 go.mod

这篇文章讲 go work 的入门用法和边界。它不是每个项目都必须用,但在多模块联调时非常舒服。

一个多模块目录

假设目录:

workspace/
├── kit/
│   ├── go.mod
│   └── logx/
│       └── logx.go
└── user-api/
    ├── go.mod
    └── main.go

kit/go.mod

module example.com/kit

user-api/go.mod

module example.com/user-api

require example.com/kit v0.1.0

user-api/main.go 导入:

import "example.com/kit/logx"

如果没有工作区,Go 会按版本去找 example.com/kit v0.1.0。但你现在想使用本地 ../kit 的最新修改。

创建 go.work

workspace/ 目录:

go work init ./kit ./user-api

生成 go.work

go 1.22

use (
	./kit
	./user-api
)

现在进入 user-api 运行:

go run .

Go 会优先使用工作区里的本地 kit 模块。你修改 kit/logx 后,user-api 能立刻使用新代码,不需要发布版本,也不需要改 user-api/go.mod

添加模块:

go work use ./another-module

同步工作区:

go work sync

入门阶段最常用的就是 inituse

go.work 要不要提交

这取决于团队约定。如果仓库本身就是一个 monorepo,包含多个模块,并且大家都应该用同一个工作区,提交 go.work 是合理的。

如果只是你个人本地同时开发两个独立仓库,go.work 更像本地开发辅助文件,不一定适合提交。否则别人拉到后,路径结构不一致就可能困惑。

一个简单判断:go.work 描述的是项目标准结构,还是你个人机器上的临时组合?前者可以提交,后者不要提交。

go work 和 replace 的区别

replace 写在模块自己的 go.mod 里,会影响所有使用这个模块构建的人,除非它没有被提交。go work 写在工作区文件里,更适合本地多模块联调。

replace 仍然有用途。比如你要临时替换某个依赖到 fork 版本,并且这个替换就是项目当前需要的构建规则,可以写进 go.mod。但如果只是本地开发两个模块,go work 通常更干净。

不要同时在多个地方写复杂替换规则。依赖解析越绕,排查越难。团队应该明确当前推荐方式。

一个真实联调流程

假设你发现 user-api 调用 kit/logx 时缺少一个字段。你可以先在 workspace 里创建工作区:

go work init ./kit ./user-api

然后修改 kit/logx

package logx

func Fields(service string, requestID string) []interface{} {
	return []interface{}{
		"service", service,
		"request_id", requestID,
	}
}

user-api 里直接使用:

logger.Info("request finished", logx.Fields("user-api", requestID)...)

运行:

cd user-api
go test ./...
go run .

如果测试通过,你可以分别提交两个模块的改动。发布 kit 新版本后,再让 user-apigo.mod 升级到正式版本。这个流程比在 user-api/go.mod 里留下本地 replace 更干净。

需要注意的是,工作区只解决本地联调,不等于版本发布。CI 环境如果没有使用同一个 go.work,仍然会按 go.mod 里的版本解析依赖。因此合并前要确认依赖模块已经发布,或者 CI 也明确使用工作区结构。

常见问题:为什么我改了本地模块却没生效

遇到这种情况,先执行:

go env GOWORK

如果输出为空,说明当前命令没有处在工作区中。你可能不在 go.work 所在目录或其子目录下,也可能显式关闭了工作区。确认目录后再运行:

go list -m all

看依赖模块是否指向工作区版本。还要检查 go.work 里是否真的 use 了你修改的模块:

go work use ./kit

另一个常见原因是编辑器或终端打开了不同目录。Go 工具根据当前工作目录向上查找 go.work,目录不对时就会按普通模块模式工作。多模块项目里,保持终端、编辑器和测试命令的工作目录一致,会少很多困惑。

小结

go work 适合多个本地 Go 模块一起开发。它通过 go.work 文件声明工作区里的模块,让 Go 工具优先使用本地代码,减少临时 replace ../pathgo.mod 的污染。

它不是每个项目的必需品。单模块项目不需要;个人临时工作区不一定提交;monorepo 多模块项目可以把它作为标准开发方式。理解它解决的问题,比机械使用命令更重要。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页