A module is a directory tree of Go packages with a go.mod file at its root. go.mod names the module and lists the versions of every other module it depends on. You create one with go mod init:
mkdir myapp
cd myapp
go mod init example.com/myapp
go: creating new go.mod: module example.com/myapp
The resulting go.mod:
module example.com/myapp
go 1.24.5
That is enough to build. Every Go project since Go 1.16 is a module, and go build, go run . and go test refuse to work without one:
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
Choosing a Module Path
The module path is the prefix of every import path inside the module. With module example.com/myapp, a package in the internal/store directory is imported as example.com/myapp/internal/store.
| Situation | Module path |
|---|---|
| Code hosted on GitHub that others may import | github.com/yourname/project |
| Your company's code | yourcompany.com/project or the repository URL |
| A private program or exercise | example.com/myapp or just myapp |
| Version 2 or later of a published module | github.com/yourname/project/v2 |
For a library, the path must match where the code lives, because go get uses it to find the repository. For a program only you run, the path is just a name. Avoid a single word that matches a standard library package, such as go mod init fmt or go mod init strings: the build then fails with ambiguous import: found package fmt in multiple modules.
Running go mod init with no argument fails outside the old GOPATH layout:
go: cannot determine module path for source directory /home/ana/myapp (outside GOPATH, module path must be specified)
Give it a path.
Adding Dependencies
Write the import, then let Go fetch it. Suppose main.go imports github.com/google/uuid. Building before the module knows about it gives a clear instruction:
main.go:6:2: no required module provides package github.com/google/uuid; to add it:
go get github.com/google/uuid
Either command fixes it:
go get github.com/google/uuid
# or, to sync go.mod with every import in the module:
go mod tidy
go get reports what it changed:
go: downloading github.com/google/uuid v1.6.0
go: added github.com/google/uuid v1.6.0
go mod tidy reports how it resolved the import:
go: finding module for package github.com/google/uuid
go: downloading github.com/google/uuid v1.6.0
go: found github.com/google/uuid in github.com/google/uuid v1.6.0
go.mod now has a require line:
module example.com/myapp
go 1.24.5
require github.com/google/uuid v1.6.0
go get with versions
| Command | Effect |
|---|---|
go get pkg | add pkg at its latest release, or keep the current version if already required |
go get pkg@v1.5.0 | use exactly v1.5.0 (upgrade or downgrade) |
go get pkg@latest | move to the latest release |
go get pkg@abc1234 | use a specific commit (recorded as a pseudo-version) |
go get -u ./... | upgrade every dependency to its latest minor or patch release |
go get -u=patch ./... | upgrade to the latest patch releases only |
go get pkg@none | remove the requirement |
go get go@1.24 | raise the module's minimum Go version |
go get changes go.mod. It no longer builds or installs programs; since Go 1.18 that is the job of go install pkg@version.
Indirect dependencies
Requirements marked // indirect are modules your code does not import directly but that the build needs. Since Go 1.17, go.mod lists every module that provides a package to the build, so the dependencies of your dependencies show up here with that marker. go mod tidy manages these markers; you do not add them yourself.
go mod tidy
Run go mod tidy whenever you add or remove imports. It:
- adds requirements for imported packages that are missing,
- removes requirements nothing imports anymore,
- adds the
go.sumentries the build needs and drops stale ones.
It looks at every package in the module, including tests and every build tag combination, so it sometimes keeps a dependency you do not see used on your platform. Running go mod tidy before each commit, and checking in CI that it produces no diff, keeps go.mod honest.
go.sum
go.sum records a hash for every module version the build uses:
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
The first line hashes the module's files, the second only its go.mod. When the go command downloads a module it checks the hash against go.sum and against the public checksum database (sum.golang.org), so a tampered or silently changed release fails the build. Commit go.sum next to go.mod, and never edit it by hand.
How Go Picks Versions
Go uses minimal version selection. Each module lists the minimum version of each dependency it needs, and the build uses the highest of those minimums, never something newer. If your module requires uuid v1.5.0 and a dependency requires uuid v1.6.0, the build uses v1.6.0, even if v1.7.0 exists. Nothing upgrades unless someone asks with go get.
The consequence is that builds are reproducible without a lock file: go.mod plus the dependency graph fully determines every version. go list -m all prints the result:
go list -m all
example.com/myapp
github.com/google/uuid v1.6.0
Major versions
A module at v2 or above must include the major version in its path: github.com/yourname/project/v2. The import path changes with it, so project and project/v2 are different modules and can both be in one build. This is Go's rule for breaking changes, and it is why you see imports like github.com/jackc/pgx/v5.
replace: Working on a Dependency Locally
To test changes to a dependency before publishing them, point its path at a local directory:
module example.com/myapp
go 1.24.5
require example.com/mylib v1.2.0
replace example.com/mylib => ../mylib
Or from the command line:
go mod edit -replace example.com/mylib=../mylib
go mod tidy
The directory must contain its own go.mod. replace only applies when building this module directly, not when someone else depends on it, so remember to remove it before you tag a release.
For editing several modules at once without touching their go.mod files, Go 1.18 added workspaces:
go work init . ../mylib
That writes a go.work file that makes the local mylib take precedence. Keep go.work out of version control unless the whole team uses the same layout.
Tool Dependencies (Go 1.24)
Go 1.24 added a tool directive, so code generators and linters can be versioned in go.mod instead of installed globally:
go get -tool golang.org/x/tools/cmd/stringer
go tool stringer -type=Color
func init Is Something Else
Searches for "golang init" often mean the init function, which is unrelated to go mod init. Any package may declare func init(). It takes no arguments, cannot be called by your code, and runs once, automatically, after the package-level variables are set and before main:
A file may have several init functions, and they run in the order they appear. Imported packages finish their own initialization first. Keep init small: work that can fail is easier to handle and test as an ordinary function called from main.
Private Modules and Proxies
By default go downloads modules through proxy.golang.org. That proxy cannot see private repositories, so tell Go which paths are private:
go env -w GOPRIVATE=github.com/yourcompany/*
Modules matching GOPRIVATE are fetched directly from the repository with your git credentials and skip the checksum database.
Frequently Asked Questions
What does go mod init do?
It creates a go.mod file in the current directory, which makes that directory the root of a module. go mod init example.com/myapp writes the module path and the Go version:
module example.com/myapp
go 1.24.5
Run it once per project, before go build or go get.
What should I use as the module path in go mod init?
The address the code will be fetched from if others import it, usually the repository path: go mod init github.com/yourname/project. For a program nobody will import, any name works (go mod init myapp), but a dotted domain-style path such as example.com/myapp avoids clashing with standard library package names.
What is the difference between go get and go mod tidy?
go get pkg@version adds a dependency or changes its version. go mod tidy reads your source files, adds any module your imports need but go.mod lacks, removes requirements nothing imports, and updates go.sum. A common flow is to write the import, then run go mod tidy.
Should I commit go.sum?
Yes. go.sum holds cryptographic hashes of every module version your build uses. Committing it lets the go command verify that everyone downloads byte-identical dependencies. Never edit it by hand; go mod tidy maintains it.
Is "golang init" the same as go mod init?
No, they are different things that share a word. go mod init is a terminal command that creates go.mod. func init() is a function you write in Go code; it runs automatically once, before main, after the package's variables are initialized.