What problem do Go workspaces (go.work) solve? How do they differ from replace?
Question 414MediumGo 1.22 to 1.25
Workspaces (Go 1.18) let you build several local modules together as if they were all main modules, without editing any go.mod. They are the standard answer for "I'm changing a library and a service at the same time".
go work init ./service ./lib
go work use ./tools # add another module
# go.work
go 1.23
use (
./lib
./service
./tools
)
Differences from replace:
- No go.mod changes, so there is nothing to forget to revert before committing.
- All
used modules act as main modules. MVS runs over the combined graph, andgo test ./...from the root covers all of them. - go.work can hold its own
replacedirectives, which override those in module go.mod files.
Gotchas: usually don't commit go.work for libraries, because CI would then test against local code rather than published versions. Disable it with GOWORK=off. go mod tidy ignores the workspace, so a module may build inside the workspace yet fail on its own. Check go env GOWORK when builds behave strangely. go work sync writes the workspace's selected versions back into each module's go.mod.
More on Modules, Packages & Tooling
- Q412Explain exclude and retract. Who writes each one, and how do they affect version selection?
- Q413What is a pseudo-version, and when does the go command generate one?
- Q415How does vendoring work in module mode, and when is it still worth using?
- Q416How do internal packages work? Give an example of an allowed and a forbidden import.
- Q417Your CI can't fetch a private module from github.com/acme/secret. Walk through GOPROXY, GOPRIVATE, GONOPROXY, GONOSUMDB and GOINSECURE.
- Q418How does the Go checksum database protect the supply chain? What does go mod verify actually check?