Modules Overview
Purpose and Scope
This page explains how Go modules fit into the Go command workflow. A module is the unit of dependency management used by modern Go projects: it groups one or more related packages, identifies them with a module path, records requirements in a module file, and lets the go command select versions for a build. The goal is to help you understand the shape of a module project before you move into task-specific pages such as creating a module, adding dependencies, publishing versions, or using multi-module workspaces.
Sources: src/cmd/go/alldocs.go, src/cmd/go/internal/modload/init.go, src/cmd/go/internal/modload/build.go
The repository implements modules primarily inside the cmd/go tree. The public behavior is documented for users in the generated command help source, while the module loader implements discovery, initialization, version selection, and build metadata. That split matters: when you run commands such as go mod init, go build, go test, or go list -m, you are using one command-line surface backed by internal module-loading state. The same loader decisions determine whether the command is operating in module-aware mode, which module is the main module, and which dependency version supplies an imported package.
Modules are different from packages. A package is the compilation unit imported by Go source files, usually from one directory. A module is the versioned boundary that contains packages. A module may contain a single package, a command plus supporting packages, or many packages intended to be released together. The module root is the directory containing the module metadata file, and package import paths inside that root are interpreted relative to the module path. Understanding that distinction makes errors about import paths, replacements, and selected versions easier to diagnose.
Relevant Source Files
src/cmd/go/alldocs.go- contains the generated user-facing documentation forgosubcommands and help topics, including module-aware command behavior exposed throughgo help,go mod, and related command documentation.src/cmd/go/internal/modload/init.go- implements module-loader initialization: discovering module roots, establishing the main module context, interpreting environment and command mode, and preparing the loader state used by later package and module operations.src/cmd/go/internal/modload/build.go- connects loaded packages and selected modules to build-facing metadata, including module information for packages, module roots, standard-library exclusion, selected versions, and update information.
These files describe two sides of the same system. alldocs.go is useful when you want to know what contract the Go command presents to users. The modload files are useful when you want to understand why the command behaves that way for a particular directory, import path, or dependency graph. For example, module information shown by build tools must not be invented separately from loading: build.go asks the loader for the module that provides a package, checks whether module mode is enabled, excludes standard-library import paths, and then reports module metadata only when the package was actually loaded in a module-aware build.
Sources: src/cmd/go/alldocs.go, src/cmd/go/internal/modload/init.go, src/cmd/go/internal/modload/build.go
Core Concepts
The main module is the module containing the packages you are directly working on for a command invocation. In the simplest case, you create a directory, run go mod init example.com/hello, and the resulting module file makes that directory the module root. Packages below that root can import each other using paths under example.com/hello. When a command runs from within that tree, the module loader can establish the main module and use its requirements as the starting point for dependency selection.
A module requirement names another module path and a version. Requirements form a dependency graph because required modules can themselves require other modules. The Go command does not simply use every version mentioned by every module; it computes a selected version for each module path in the build. The ModuleInfo path in build.go reflects that model: when a version is not explicitly embedded in the query, module information is derived from the current loaded module state, including selected versions from roots or the module graph. If a module is not selected for the current build, the public metadata can report that it is not in the current build rather than pretending it is available.
Sources: src/cmd/go/internal/modload/build.go
The module root is important because it is where the command looks for module metadata and from which package import paths are interpreted. Internal initialization code in modload is responsible for setting up this context before later operations load packages, resolve imports, or compute module information. That means the same project can behave differently depending on the working directory, environment settings, workspace use, or explicit command options. For day-to-day use, the practical rule is simple: run module-aware commands from inside the module you mean to work on, and keep the module file at the root of the source tree you intend to version together.
Standard-library packages are intentionally outside normal module reporting. The build.go snippet checks whether an import path is a standard import path and whether the package is part of the standard library tree. If it is, package module metadata and module roots are not reported as external module information. This distinction is visible in tools that inspect builds: fmt, net/http, or database/sql come from the Go distribution, while example.com/project/pkg or third-party module paths are associated with module versions and roots when loaded under module mode.
Module-Aware Command Behavior
The go command is module-aware when it resolves imports and dependencies using module metadata rather than only searching legacy source locations. The public command documentation in alldocs.go describes the user-facing subcommands, while modload/init.go prepares the state those subcommands rely on. In practice, commands such as go run, go build, and go test load packages, and package loading may require selecting modules, fetching source, or consulting the existing module graph. Commands under go mod focus directly on module metadata: initializing a module, editing requirements, tidying unused dependencies, downloading modules, and explaining why a module is needed.
Sources: src/cmd/go/alldocs.go, src/cmd/go/internal/modload/init.go
A common first workflow is to create a library module, then create a separate caller module that imports it. The caller module has its own module path and its own module file. When the caller imports the library package, the Go command must decide which module version supplies that import path. During local development, you may use replacement or workspace mechanisms on more advanced pages, but the conceptual model remains the same: imports name packages, packages are provided by modules, and the build uses a selected module version for each module path.
The module loader also feeds build metadata. PackageModuleInfo in build.go returns public module information for the module that provides a package, but only when module mode is enabled, the package is not from the standard library, and the package was successfully loaded. PackageModRoot similarly returns the root directory for the module that provides a package, but it avoids returning a module root in cases where module reporting is not meaningful, such as standard-library packages or vendor-mode builds. These checks prevent diagnostics and tools from confusing local source, vendored code, downloaded module cache content, and GOROOT packages.
Sources: src/cmd/go/internal/modload/build.go
System-to-Code Mapping
| Concept | User-facing behavior | Repository implementation signal |
|---|---|---|
| Module help and commands | go help, go mod, go build, go test, and related commands describe module-aware behavior | src/cmd/go/alldocs.go |
| Module initialization | The command establishes whether a module is active and which module is the main module | src/cmd/go/internal/modload/init.go |
| Module graph selection | Builds use selected versions from roots or the full module graph | src/cmd/go/internal/modload/build.go |
| Package-to-module metadata | Tools can ask which module provides a loaded package | src/cmd/go/internal/modload/build.go |
| Standard library boundary | Standard packages are not reported as ordinary module dependencies | src/cmd/go/internal/modload/build.go |
The mapping is useful because modules are not implemented as a separate tool bolted onto the side of Go. They are part of package loading, build planning, testing, and metadata reporting. A go test ./... command, for example, begins with packages and patterns, but those packages may import dependencies outside the main module. The loader must understand the module context before it can resolve the import graph. Later, build metadata functions can report which modules participated in that loaded graph. The source layout mirrors this progression: initialize module state, load packages and module files, compute graph selections, then expose selected module information to callers that need it.
The distinction between a requirement and a selected version is especially important when reading diagnostics. A module file can mention direct requirements, and dependencies can mention their own requirements, but the build uses one selected version for each module path. ModuleInfo handles this by consulting the loaded module state. In pruned-module situations it may use selected root information; otherwise it can ask the module graph for the selected version. If the selected version is none, the module is reported as not being in the current build. That behavior matches the user expectation that command output should describe the actual build, not every possible dependency ever mentioned by a file.
Sources: src/cmd/go/internal/modload/build.go
Practical Workflow
Start a new module by choosing a stable module path and running the initialization command from the directory that should become the root. The path should be the import prefix users will write for packages in the module. For a private experiment, an example path is enough; for a published module, use the repository-based path that consumers will import. After initialization, add packages and commands under the root. Each package has its own directory and package name, while the module file remains the dependency-management anchor for all of them.
mkdir hello
cd hello
go mod init example.com/hello
go test ./...
go build ./...As you import packages from other modules, the go command updates and uses dependency metadata so the build can be repeated. You generally do not need to compute the graph yourself. Instead, use normal package commands while letting module-aware loading resolve imports. When a dependency is no longer imported, go mod tidy is the command-oriented way to reconcile the module file with the package graph. When you need to inspect what the build selected, go list -m all and related module queries expose the selected module versions rather than merely echoing direct requirements.
When something looks wrong, ask which layer is involved. If the command cannot find the module root, check where you are running the command and whether the module file is in the expected directory. If an import cannot be resolved, check whether the import path names a package path under the intended module path. If metadata omits a module for a package, remember that standard-library packages and disabled module mode deliberately return no module information. If vendor mode is active, module roots may also be hidden from package metadata because the build is using the vendor tree as its source boundary.
Sources: src/cmd/go/internal/modload/init.go, src/cmd/go/internal/modload/build.go
Next Steps
After this overview, read the task page that matches what you are doing next. Use Create a Module when starting from an empty directory, Managing Dependencies when adding or upgrading imports, go.mod Reference when you need directive-level detail, and Multi-Module Workspaces when you are developing several modules together locally. If you are publishing a module, continue with module versioning and release workflow material so that your module path, tags, and compatibility expectations line up with Go's decentralized module system.
The most important habit is to treat the module file, package imports, and command working directory as one system. Modules are not just a metadata format; they are the way the Go command connects source directories, import paths, selected versions, and build metadata. The implementation in cmd/go/internal/modload makes that connection explicit, and the public command documentation in cmd/go/alldocs.go is the contract you should rely on when teaching, scripting, or debugging module-aware workflows.
Sources: src/cmd/go/alldocs.go, src/cmd/go/internal/modload/init.go, src/cmd/go/internal/modload/build.go