Overview
Workspaces solve one problem: developing against a local, unpublished copy of another
module. Before 1.18 that meant adding replace directives to go.mod and remembering to
strip them before commit; go.work moves that wiring into a separate, personal file the
go command reads automatically, leaving go.mod clean.
Key points
go work init ./app ./lib: createsgo.worklisting module directories viauseentries; everygocommand in scope now resolves those modules from disk instead of the module cache/proxy.- Replaces the replace dance: no more temporary
replace example.com/lib => ../liblines pollutinggo.modand leaking into commits;go.workcan also hold its ownreplacedirectives for the rare remaining cases. - Almost never commit it:
go.workdescribes your checkout layout, not the module — gitignore it by default. The exception is a monorepo whose canonical layout is fixed for every clone; committing ago.workat the repo root is then a reasonable convention. go work use -r .scans recursively and adds every module found — the quick setup for a monorepo checkout.go work syncpushes workspace-resolved versions back into each module'sgo.mod, keeping the modules buildable standalone.- Releases ignore workspaces: published module versions must build without
go.work(GOWORK=off go build ./...is the honest pre-release check); CI should generally run with workspaces off. - Monorepos: workspaces make multi-module monorepos ergonomic (each service/library its own module, one workspace over all), but a single module per repo remains the simpler default when versioning independently isn't needed.
Examples
go work init ./service ./shared
go work use ./newmod
GOWORK=off go test ./... # verify the module stands alone
Related
- The Go toolchain — parent catalog.
- go mod — the per-module interface workspaces layer over.
- Go modules — the resolution rules go.work locally overrides.