Learn Go← Dashboard

go mod, go fmt/vet/doc, workspaces, build, and ship.

Modules and Workspaces

We met go.mod in chapter 1. Here are the details you need for everyday work on multi-package or multi-module projects.

The module file in detail

module github.com/yourname/myproject

go 1.23

require (
    github.com/google/uuid v1.6.0
    github.com/spf13/cobra v1.8.0
)

require (
    github.com/inconshreveable/mousetrap v1.1.0 // indirect
)

replace github.com/old/pkg => github.com/new/pkg v1.0.0
  • module — the import path of this module.
  • go — minimum Go language version.
  • require — direct (and indirect) dependencies.
  • replace — override a dep with a local path or a fork. Useful for development: replace github.com/me/lib => ../lib.

Versions

Go modules use semantic versioning strictly:

  • v1.x.y — stable; backwards-compatible.
  • v2+ — must change the import path to end in /v2, /v3, etc. Yes, this is unusual; it's also how Go forces upgrades to be deliberate.
  • Pre-release: v1.0.0-rc.1, etc.
  • Untagged commits: a "pseudo-version" like v0.0.0-20240315123456-abcdef.
go get example.com/lib@v1.2.3      # specific version
go get example.com/lib@latest       # latest stable
go get example.com/lib@main         # a branch (becomes pseudo-version)
go get -u ./...                     # upgrade everything (minor versions)

Workspaces (go.work)

A workspace lets you point at multiple local modules at once — perfect for working on a library and an app together without replace directives.

mkdir myws && cd myws
go work init ./app ./lib

That creates go.work:

go 1.23
use (
    ./app
    ./lib
)

Inside the workspace, the app always sees the local ./lib, no matter what version its go.mod requests.

Vendoring

go mod vendor copies every dependency into a vendor/ folder inside your project. Builds then use that folder instead of the module cache. Two reasons to do this:

  • Reproducible builds in restricted/offline environments.
  • Easy code review of exactly what's shipped.

Most teams skip vendoring (the module cache + go.sum already gives reproducibility), but it's still supported and required by some enterprises.

When would you use `replace` in `go.mod`?