From mise-toolkit
Go modules deep dive — workspace mode, replace directives, private modules with GOPRIVATE, vendoring vs the module cache, and when to use `go install` vs mise's go: backend. Use when working with Go module layouts or troubleshooting dependency resolution.
npx claudepluginhub ray-manaloto/claude-code-marketplace --plugin mise-toolkitThis skill uses the workspace's default tool permissions.
Go's module system is simpler than most, but has a few corners that reliably trip people up: workspaces, replace directives, private modules, and vendoring. This skill covers them and how they interact with mise.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Share bugs, ideas, or general feedback.
Go's module system is simpler than most, but has a few corners that reliably trip people up: workspaces, replace directives, private modules, and vendoring. This skill covers them and how they interact with mise.
go.mod — single-module projects. The standard case.go.work — workspaces. Multiple modules edited together.GOPRIVATE — private modules (internal corp repos).vendor/ — vendoring. Rare now, still relevant for some compliance stories.All four are Go's domain, not mise's. mise just pins the Go toolchain that runs them.
go.work)Workspaces let you edit multiple interdependent modules together without juggling replace directives.
myorg/
├── go.work
├── api/
│ ├── go.mod # module myorg/api
│ └── main.go
├── client/
│ ├── go.mod # module myorg/client
│ └── client.go
└── shared/
├── go.mod # module myorg/shared
└── types.go
// go.work
go 1.23
use (
./api
./client
./shared
)
Now go build ./... from the root builds all three modules with local-first resolution. myorg/api importing myorg/shared uses the local copy, not a published version.
[tools]
go = "1.23"
[tasks."work:sync"]
run = "go work sync"
[tasks.build]
depends = ["work:sync"]
run = "go build ./..."
[tasks.test]
depends = ["work:sync"]
run = "go test ./..."
Commit go.work to git for monorepos where the workspace is the shared layout. Don't commit it for individual-dev workspaces that mix unrelated repos (add to .gitignore).
replace directivesThe older pattern for "use a local copy of this module":
// go.mod
replace myorg/shared => ../shared
Replace directives are painful across teams — the relative path only works if everyone has the same checkout layout. Workspaces are strictly better for the "I'm editing multiple modules at once" use case.
Keep replace for:
// TODO: remove after upstream PR #123 comment).Migrate to workspaces for:
GOPRIVATE[env]
GOPRIVATE = "github.com/my-org/*,gitlab.internal.corp/*"
GONOSUMCHECK = "github.com/my-org/*"
GOPRIVATE tells Go:
proxy.golang.org) for these modules.sum.golang.org) for them.Without GOPRIVATE, Go phones home to the public infra for every private dep, which leaks module paths (and fails for truly private ones).
By default, go get uses HTTPS. For private GitHub orgs with SSH keys, tell git to rewrite:
git config --global url."ssh://git@github.com/".insteadOf "https://github.com/"
Now go get github.com/my-org/… uses your SSH key instead of asking for an HTTPS token.
Alternative: use a ~/.netrc with an HTTPS token. Less common; larger surface area for leaks.
go mod vendor copies all module deps into ./vendor/. Build with -mod=vendor or GOFLAGS=-mod=vendor.
Pros:
Cons:
Use vendoring for:
Skip vendoring for:
~/go/pkg/mod) is fine.In mise.toml with vendoring:
[tools]
go = "1.23"
[env]
GOFLAGS = "-mod=vendor"
[tasks."deps:vendor"]
run = "go mod vendor"
sources = ["go.mod", "go.sum"]
outputs = ["vendor/"]
go install vs mise's go: backendThree ways to get a Go CLI installed:
go install into ~/go/bingo install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
$GOBIN (defaults to ~/go/bin).go: backend[tools]
"go:github.com/golangci/golangci-lint/cmd/golangci-lint" = "1.62"
aqua: backend (preferred when available)[tools]
"aqua:golangci/golangci-lint" = "1.62"
Decision: try aqua: first. Fall back to go: only for tools that aren't published as GitHub releases.
mise registry golangci-lint # see what backends are available
The shared module cache lives at $GOPATH/pkg/mod/. It can grow large (multi-GB). Clean occasionally:
go clean -modcache
Don't rm -rf the dir — it has read-only permissions that confuse some tools.
For CI caching, cache ~/go/pkg/mod between runs:
- uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ hashFiles('**/go.sum') }}
go get in a CI workflow (instead of the implicit download during go build) — unnecessary.vendor/ AND using the module proxy — pick one.replace with an absolute path — breaks for every other contributor.go.sum — run go mod tidy instead.go install for team-shared tools and expecting everyone to have the same version — use mise instead.mise-lang-go-overview — version resolution, GOPATH, toolchain directive.mise-backends-overview — aqua vs go vs other backends.mise-ci-github-actions — mise-action and the Go module cache.go.dev/ref/mod.go.work guide: go.dev/doc/tutorial/workspaces.