Module release and versioning workflow
When you develop modules for use by other developers, you can follow a workflow that helps ensure a reliable, consistent experience for developers using the module. This topic describes the high-level steps in that workflow.
For an overview of module development, see Developing and publishing modules.
See also
- If you’re merely wanting to use external packages in your code, be sure to see Managing dependencies.
- With each new version, you signal the changes to your module with its version number. For more, see Module version numbering.
Common workflow steps
The following sequence illustrates release and versioning workflow steps for an example new module. For more about each step, see the sections in this topic.
-
Begin a module and organize its sources to make it easier for developers to use and for you to maintain.
If you’re brand new to developing modules, check out Tutorial: Create a Go module.
In Go’s decentralized module publishing system, how you organize your code matters. For more, see Managing module source.
-
Set up to write local client code that calls functions in the unpublished module.
Before you publish a module, it’s unavailable for the typical dependency management workflow using commands such as
go get
. A good way to test your module code at this stage is to try it while it is in a directory local to your calling code.See Coding against an unpublished module for more about local development.
-
When the module’s code is ready for other developers to try it out, begin publishing v0 pre-releases such as alphas and betas. See Publishing pre-release versions for more.
-
Release a v0 that’s not guaranteed to be stable, but which users can try out. For more, see Publishing the first (unstable) version.
-
After your v0 version is published, you can (and should!) continue to release new versions of it.
These new versions might include bug fixes (patch releases), additions to the module’s public API (minor releases), and even breaking changes. Because a v0 release makes no guarantees of stability or backward compatibility, you can make breaking changes in its versions.
For more, see Publishing bug fixes and Publishing non-breaking API changes.
-
When you’re getting a stable version ready for release, you publish pre-releases as alphas and betas. For more, see Publishing pre-release versions.
-
Release a v1 as the first stable release.
This is the first release that makes commitments about the module’s stability. For more, see Publishing the first stable version.
-
In the v1 version, continue to fix bugs and, where necessary, make additions to the module’s public API.
For more, see Publishing bug fixes and Publishing non-breaking API changes.
-
When it can’t be avoided, publish breaking changes in a new major version.
A major version update – such as from v1.x.x to v2.x.x – can be a very disruptive upgrade for your module’s users. It should be a last resort. For more, see Publishing breaking API changes.
Coding against an unpublished module
When you begin developing a module or a new version of a module, you won’t yet have published it. Before you publish a module, you won’t be able to use Go commands to add the module as a dependency. Instead, at first, when writing client code in a different module that calls functions in the unpublished module, you’ll need to reference a copy of the module on the local file system.
You can reference a module locally from the client module’s go.mod file by using
the replace
directive in the client module’s go.mod file. For more
information, see in Requiring module code in a local
directory.
Publishing pre-release versions
You can publish pre-release versions to make a module available for others to try it out and give you feedback. A pre-release version includes no guarantee of stability.
Pre-release version numbers are appended with a pre-release identifier. For more on version numbers, see Module version numbering.
Here are two examples:
v0.2.1-beta.1
v1.2.3-alpha
When making a pre-release available, keep in mind that developers using the
pre-release will need to explicitly specify it by version with the go get
command. That’s because, by default, the go
command prefers release versions
over pre-release versions when locating the module you’re asking for. So
developers must get the pre-release by specifying it explicitly, as in the
following example:
go get example.com/theirmodule@v1.2.3-alpha
You publish a pre-release by tagging the module code in your repository, specifying the pre-release identifier in the tag. For more, see Publishing a module.
Publishing the first (unstable) version
As when you publish a pre-release version, you can publish release versions that don’t guarantee stability or backward compatibility, but give your users an opportunity to try out the module and give you feedback.
Unstable releases are those whose version numbers are in the v0.x.x range. A v0 version makes no stability or backward compatibility guarantees. But it gives you a way to get feedback and refine your API before making stability commitments with v1 and later. For more see, Module version numbering.
As with other published versions, you can increment the minor and patch parts of the v0 version number as you make changes toward releasing a stable v1 version. For example, after releasing a v.0.0.0, you might release a v0.0.1 with the first set of bug fixes.
Here’s an example version number:
v0.1.3
You publish an unstable release by tagging the module code in your repository, specifying a v0 version number in the tag. For more, see Publishing a module.
Publishing the first stable version
Your first stable release will have a v1.x.x version number. The first stable release follows pre-release and v0 releases through which you got feedback, fixed bugs, and stabilized the module for users.
With a v1 release, you’re making the following commitments to developers using your module:
- They can upgrade to the major version’s subsequent minor and patch releases without breaking their own code.
- You won’t be making further changes to the module’s public API – including its function and method signatures – that break backward compatibility.
- You won’t be removing any exported types, which would break backward compatibility.
- Future changes to your API (such as adding a new field to a struct) will be backward compatible and will be included in a new minor release.
- Bug fixes (such as a security fix) will be included in a patch release or as part of a minor release.
Note: While your first major version might be a v0 release, a v0 version does not signal stability or backward compatibility guarantees. As a result, when you increment from v0 to v1, you needn’t be mindful of breaking backward compatibility because the v0 release was not considered stable.
For more about version numbers, see Module version numbering.
Here’s an example of a stable version number:
v1.0.0
You publish a first stable release by tagging the module code in your repository, specifying a v1 version number in the tag. For more, see Publishing a module.
Publishing bug fixes
You can publish a release in which the changes are limited to bug fixes. This is known as a patch release.
A patch release includes only minor changes. In particular, it includes no changes to the module’s public API. Developers of consuming code can upgrade to this version safely and without needing to change their code.
Note: Your patch release should try not to upgrade any of that module’s own transitive dependencies by more than a patch release. Otherwise, someone upgrading to the patch of your module could wind up accidentally pulling in a more invasive change to a transitive dependency that they use.
A patch release increments the patch part of the module’s version number. For more see, Module version numbering.
In the following example, v1.0.1 is a patch release.
Old version: v1.0.0
New version: v1.0.1
You publish a patch release by tagging the module code in your repository, incrementing the patch version number in the tag. For more, see Publishing a module.
Publishing non-breaking API changes
You can make non-breaking changes to your module’s public API and publish those changes in a minor version release.
This version changes the API, but not in a way that breaks calling code. This might include changes to a module’s own dependencies or the addition of new functions, methods, struct fields, or types. Even with the changes it includes, this kind of release guarantees backward compatibility and stability for existing code that calls the module’s functions.
A minor release increments the minor part of the module’s version number. For more, see Module version numbering.
In the following example, v1.1.0 is a minor release.
Old version: v1.0.1
New version: v1.1.0
You publish a minor release by tagging the module code in your repository, incrementing the minor version number in the tag. For more, see Publishing a module.
Publishing breaking API changes
You can publish a version that breaks backward compatibility by publishing a major version release.
A major version release doesn’t guarantee backward compatibility, typically because it includes changes to the module’s public API that would break code using the module’s previous versions.
Given the disruptive effect a major version upgrade can have on code relying on the module, you should avoid a major version update if you can. For more about major version updates, see Developing a major version update. For strategies to avoid making breaking changes, see the blog post Keeping your modules compatible.
Where publishing other kinds of versions requires essentially tagging the module code with the version number, publishing a major version update requires more steps.
-
Before beginning development of the new major version, in your repository create a place for the new version’s source.
One way to do this is to create a new branch in your repository that is specifically for the new major version and its subsequent minor and patch versions. For more, see Managing module source.
-
In the module’s go.mod file, revise the module path to append the new major version number, as in the following example:
example.com/mymodule/v2
Given that the module path is the module’s identifier, this change effectively creates a new module. It also changes the package path, ensuring that developers won’t unintentionally import a version that breaks their code. Instead, those wanting to upgrade will explicitly replace occurrences of the old path with the new one.
-
In your code, change any package paths where you’re importing packages in the module you’re updating, including packages in the module you’re updating. You need to do this because you changed your module path.
-
As with any new release, you should publish pre-release versions to get feedback and bug reports before publishing an official release.
-
Publish the new major version by tagging the module code in your repository, incrementing the major version number in the tag – such as from v1.5.2 to v2.0.0.
For more, see Publishing a module.