Using Go Modules for Golang Dependency Management
Table of Contents
The Go (also known as Golang) programming language has made significant strides in the dependency management space. From version 1.11, the Go team introduced a feature called Go Modules, which makes package management in the language efficient and seamless.
Go Modules is now the default way of managing dependencies in Golang—just like npm in Node.js and pip in Python.
In this article, we’re going to introduce the Golang dependency management system. We’ll talk about the basics of using it for simplifying the way you create your code, as well as enhancing the process of downloading, updating, and removing dependencies in your Go applications.
Getting started with Modules
In Golang, Modules refer to a grouping of related packages that are versioned to each other as one unit. With Go Modules, you can implement precise dependency requirements and design reproducible builds for multiple environments. It makes dependency management in Golang a lot easier.
To get started using Modules in your project, you can simply enter the following command (this will be applicable if your project is already using version control):
go mod init
Or, you can initiate Go Modules by specifying the module import path manually. The path can be a module name, project name, or URL hosting the repository’s code.
go mod init <module_path>
For example, here is how you can start using modules in your project:
go mod init github.com/Alfrick/Go-Test
After running the above command, it’ll generate a go.mod module config file in your project’s root directory. The file will contain the module and Go version information.
module github.com/Alfrick/Go-Test
go 1.14
The file stipulates the project’s requirements and lists all the needed dependencies—it’s like the package.json file used in Node.js dependency management.
Installing dependencies
Once you’ve initialized your project to start using Go Modules, you can now introduce new dependencies into your codebase.
To install dependencies, use the go get command, which will also update the go.mod file automatically.
Here is an example:
go get github.com/lib/pq
You may also target a specific branch of the dependency:
go get github.com/lib/pq@master
Or, a precise version:
go get github.com/lib/pq@v1.8.0
After running the command, your go.mod file should look something like this:
module github.com/Alfrick/Go-Test
go 1.14
require github.com/lib/pq v1.8.0 // indirect
Since the package is not currently used anywhere in the project, it’s marked as indirect. This comment may also appear on an indirect dependency package; that is, a dependency of another dependency.
Then, to import and use the newly installed package, you can specify its import path and implement one of its methods.
Here is an example:
// main.go
package main
import (
“github.com/lib/pq”
)
func main() {
pq.Load()
}
Authenticating dependencies
Furthermore, installing a dependency will also generate a go.sum file in your project’s root. While it’s not a lock file, like package-lock.json in Node.js, the file comes with the expected cryptographic hashes of the content of particular module versions.
The go.sum file acts as a dependency checker that authenticates your modules against unexpected or malicious changes that may break your entire codebase. Also, if you stop using a module, the recorded checksum information will allow you to resume using it as-is in the future.
Removing unused dependencies
To tidy things up, you can execute the following command on the terminal:
go mod tidy
After running the above command, it’ll remove any unused dependencies in your project and update the go.mod file.
Remember that the go.sum file will still contain the cryptographic hash of the package’s content even after you’ve removed it.
Also, it’ll add any missing dependencies, such as if you imported an external package and failed to fetch it first with the go get command.
Note that since the go.mod file contains all the necessary information for reproducible builds, it’s important to run the go mod tidy command after making any changes to your code. It’ll ensure your module file is accurate and clean, especially before every commit.
Installing missing dependencies
You may also run go build or go test command to install all the missing dependencies automatically.
For example, let’s say you imported and used a third-party package in your project this way:
// main.go
package main
import (
“github.com/lib/pq”
“github.com/subosito/gotenv”
)
func main() {
pq.Load()
log.Println(os.Getenv(“APP_ID”))
}
If you run go build, the command will fetch the missing package automatically and include it to your go.mod file before your project is compiled.
Here is the updated go.mod file:
module github.com/Alfrick/Go-Test
go 1.14
require (
github.com/lib/pq v1.8.0
github.com/subosito/gotenv v1.2.0
)
Upgrading dependency versions
Go Modules versions follow the Semantic Versioning (semver) format, which has three sections: major, minor, and patch. For example, if a package is in version 1.8.0, it means that 1 is a major version, 8 a minor version, and 0 a patch version.
In Golang dependency management, there are several ways of changing the version of a dependency. To start with, if you know the version of a dependency you want, you can simply navigate to the go.mod file in your project and change to your desired dependency version by hand. Then, run go get command to upgrade the go.sum file as well.
However, if you intend to move to a major version, you’ll need to edit the import path, as well. For example, to upgrade from github.com/lib/pq at v1.8.0 to v.2.2.3, you’ll need to change the path from require github.com/lib/pq v1.8.0 to require github.com/lib/pq/v2 v2.2.3.
In Go Modules, the convention for code opting is to utilize a different module path entirely for every new major version referenced. Beginning from the second version of a dependency, the path must end with the number of the major version.
Alternatively, you may automatically update a dependency to its latest version by running the go get command. Using this option will upgrade the go.mod file automatically; so, you’ll not need to edit it manually.
For example, as we’ve seen earlier, you can download and update to the latest dependency version by running the following command (“latest” refers to the latest dependency version with a semver tag):
go get github.com/lib/pq
Or, download and update to a specific version:
go get github.com/lib/pq@v1.8.0
Furthermore, you may update a dependency and all its transitive dependencies to the latest version by running the following command (the -u flag implies that the latest minor or patch releases are to be used):
go get -u github.com/lib/pq
If you want to use the latest patch release, you may run the following:
go get -u=patch github.com/lib/pq
Additionally, other commands, such as go build or go test, will add new dependencies automatically based on the stipulated requirements. For example, as earlier illustrated, they can do this to satisfy import requirements, including upgrading go.mod file and installing the new dependencies.
Listing dependencies
To see a list of the current module (also known as main module) and all its dependencies, run the following command:
go list -m all
To see a list of all the available versions of a package, run:
o list -m -versions github.com/lib/pq
Conclusion
The introduction of Go Modules has reshaped the Golang dependency management landscape. The package management system solves the issues of dependency versioning, ensures dependency information is explicit, and simplifies various dependency management tasks. It’s what you need to make the most of your Go code.
Importantly, if you want to automate dependency updates in your Go projects, Mend Renovate is the way to go. With the open source tool, you can comfortably automate dependency updates using pull requests and branches, and save yourself the hassle of keeping tabs on every released package upgrade.
Click here to get the free Mend Renovate tool.