Publishing Modules

Purpose and Scope

Publishing a module means making a versioned Go module visible to the Go command and to other developers. In practice, that is not just a Git operation. A published module needs a stable module path, a coherent package layout, clean module metadata, passing tests, and an immutable version tag that Go tools can fetch through a module proxy. The official publishing workflow centers on preparing the module root, tidying dependencies, testing all packages, tagging a semantic version, pushing the tag, and asking a proxy such as proxy.golang.org to discover the new version.

This page focuses on the developer workflow for preparing a module for public use. It uses the official Go module publishing guidance for commands and sequencing, and uses the supplied repository source file to illustrate an important publishing boundary: source layout communicates what is intended to be imported. A package path such as cmd/compile/internal/ssa is organized as an implementation package, whereas a published module should make deliberate choices about which packages are public, which are internal, and which are command packages.

Core Publishing Model

A Go module is published when its version can be resolved by Go tools. Developers who import a package from the module should be able to run commands such as go get, go list -m, go build, or go test and have the module version downloaded, authenticated, and used consistently. The module path in go.mod should match the repository location or the vanity import path that users will import. Package directories beneath the module root become importable packages unless language and layout rules, such as an internal directory boundary, intentionally limit use.

The most important publishing constraint is immutability of tagged versions. After a version tag is visible to users and proxies, do not change the code behind that tag. Go tools authenticate module downloads against the first downloaded copy; if the content for the same version later differs, users can see security errors. The correct way to fix a released version is to make another commit, choose a new version, tag it, and publish that new tag.

A published module can contain library packages, command packages, or both. Library packages are imported by other Go code and therefore form the module’s public surface. Command packages use package main and produce executables. Keeping this distinction clear helps users understand whether they should import a package, run a command, or treat a directory as an implementation detail. The source layout you choose before publishing becomes part of the reader experience, because import paths are derived from directories.

Package Layout and Public Surface

Before publishing, inspect the packages that your module exposes. A simple library module often has its main package at the module root, with files such as modname.go and modname_test.go. Larger modules can split functionality across subdirectories, but every importable directory should have a purpose, a package comment where appropriate, and tests that describe expected behavior. Avoid making incidental helper directories importable if they are not meant to become part of your compatibility story.

The supplied Go repository source path shows how the Go project separates implementation code from public import surfaces. The file is in src/cmd/compile/internal/ssa/layout.go, declares package ssa, and implements compiler SSA block layout functions such as layout, layoutRegallocOrder, and layoutOrder. Its comments describe ordering basic blocks for assembly output and a register-allocation order, which are compiler implementation concerns rather than a public module API. Sources: src/cmd/compile/internal/ssa/layout.go

That distinction matters when publishing your own module. If a directory is part of an implementation subsystem, use layout and naming to communicate that boundary. If a directory is a supported package, document it, test it, and avoid unnecessary churn in exported names. Publishing is therefore a design step as much as a release step: you decide which package paths other modules will import, which commands users will install, and which internal details should remain free to change.

Publishing Flow

Start from the module root, the directory containing go.mod. Run go mod tidy before publishing. Tidying removes requirements that are no longer needed and adds missing requirements needed to build and test packages. A tidy module file is easier for users and tools to reason about because it reflects the actual imports in the module instead of stale local experimentation. If your module contains multiple packages, run tidy from the module root rather than from a nested package directory.

Next, run go test ./... as a final verification step. The ./... pattern asks the Go command to test packages below the current directory, which is the usual release check for a module. For public modules, tests protect both behavior and package layout. They also help catch examples, imports, and dependency edges that may work locally but fail for a fresh user resolving the module from a clean module cache.

After the code and metadata are ready, commit the release changes and tag the commit with a version such as v0.1.0, v1.2.3, or a higher major version that matches the module path rules. Use a version number that communicates the nature of the release. Then push the tag to the origin repository. The tag is the address Go tools use to identify the module version, so it should point at the exact source tree you intend users to consume.

Finally, make the version discoverable by asking a proxy to resolve it. The official workflow uses a command like GOPROXY=proxy.golang.org go list -m example.com/mymodule@v0.1.0. Setting GOPROXY ensures the request reaches the proxy, and go list -m asks for module metadata at the exact version. Once the proxy has indexed the version, other developers can import packages from the module and let the Go command resolve the dependency normally.

Command Checklist

Use this compact sequence for a normal public release. Replace the module path and version with your own values.

go mod tidy
go test ./...
git commit -m "mymodule: changes for v0.1.0"
git tag v0.1.0
git push origin v0.1.0
GOPROXY=proxy.golang.org go list -m example.com/mymodule@v0.1.0

Treat the checklist as a release gate, not as a substitute for reviewing the public surface. The commands can confirm that metadata is tidy, packages test, and the version is reachable, but they cannot decide whether an exported name is well documented or whether a package path is something you want to support. Review package names, exported identifiers, examples, README text, and module path spelling before you create the tag.

System-to-Code Mapping

The module publishing system is mostly exercised through the Go command and the module proxy ecosystem, but repository organization still shapes how modules are consumed. A source file’s package declaration determines the package it belongs to, and its directory contributes to the import path. The supplied compiler file is a concrete example of a tightly scoped implementation package: it lives under a command implementation tree and contains functions that reorder compiler basic blocks before assembly output. Sources: src/cmd/compile/internal/ssa/layout.go

For a module author, the lesson is to align repository layout with user intent. Public library packages should be easy to find and import. Command packages should make it clear that they build programs. Implementation packages should not appear as accidental extension points. When these boundaries are clear, publishing a version is safer because users import the paths you meant to support, and future releases can change private implementation details without surprising downstream modules.

Relevant Source Files

  • src/cmd/compile/internal/ssa/layout.go — Shows a Go source file in an implementation-oriented package, including package declaration, license header, compiler-internal functions, and comments describing how SSA basic blocks are ordered for assembly output. It is useful here as a source-backed example of why package layout and public-vs-internal boundaries matter before publishing a module.

Next Steps

After publishing, verify the version from a clean checkout or temporary directory by importing one of the module’s public packages and running go test or go build. Then continue with the module release workflow: document changes, prepare future version numbers intentionally, and avoid rewriting published tags. For broader context, read the related pages on modules overview, managing dependencies, module versioning, and the module release workflow.