Using a virtual environment and package manager helps us manage dependencies and compatibility more easily, for example, by separating modules used on a per-project basis and managing packages and language versions. Unlike what we typically do in other languages, such as Python’s venv, Node’s nvm, and Ruby’s rbenv, we rarely use a Virtual environment in Go. The main reason is that we seldom need to be concerned about compatibility in Go 1, most of the time.


Go Module

In short:

  • Go module is the main stream to manage dependency in Golang.
  • It is introduced since Go 1.11 and officially recommended since Go 1.13.

Go introduced Go module for dependency management since Go 1.11 (go envGO111MODULE=on) that performs similar functionalities to package managers in other languages, such as Python’s pip, Node.js’s npm, and Ruby’s bundler, while simultaneously avoiding environment management issues.

When we run go mod init, a go.mod file will be created, which describes the module’s properties, including its dependencies on other modules and on versions of Go.

Example of go.mod

// module path
module github.com/gin-gonic/gin
 
// The minimum version of Go required by the current module.
go 1.18
 
// List of minimum versions of other modules required by the current module.
require (
  github.com/bytedance/sonic v1.4.0
  github.com/gin-contrib/sse v0.1.0
  github.com/go-playground/validator/v10 v10.11.1
  github.com/goccy/go-json v0.9.11
  github.com/json-iterator/go v1.1.12
  github.com/mattn/go-isatty v0.0.16
  github.com/pelletier/go-toml/v2 v2.0.2
  github.com/stretchr/testify v1.8.1
  github.com/ugorji/go/codec v1.2.7
  golang.org/x/net v0.0.0-20221004154528-8021a29435af
  google.golang.org/protobuf v1.28.1
  gopkg.in/yaml.v2 v2.4.0
)
 
// //indirect comment indicates that no package from the required module
// is directly imported by any package in the main module.
require (
  github.com/chenzhuoyu/base64x v0.0.0-20220526154910-8bf9453eb81a // indirect
  github.com/davecgh/go-spew v1.1.1 // indirect
  github.com/go-playground/locales v0.14.0 // indirect
  github.com/go-playground/universal-translator v0.18.0 // indirect
  github.com/klauspost/cpuid/v2 v2.0.14 // indirect
  github.com/leodido/go-urn v1.2.1 // indirect
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
  github.com/modern-go/reflect2 v1.0.2 // indirect
  github.com/pmezard/go-difflib v1.0.0 // indirect
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
  golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect
  golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
  golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
  golang.org/x/text v0.3.7 // indirect
  gopkg.in/yaml.v3 v3.0.1 // indirect
)

if you need to install new Go package, use go install.

But go get work as well, because it implicitly apply -d flag when you use it.

go install, with or without a version suffix (as described above), is now the recommended way to build and install packages in module mode. go get should be used with the -d flag to adjust the current module’s dependencies without building packages, and use of go get to build and install packages is deprecated. In a future release, the -d flag will always be enabled.

https://tip.golang.org/doc/go1.16#modules

Go makes it easy to install different versions locally. These different versions contain their own standard libraries that won’t interfere with each other.

In practice, we unlikely need to use Virtual environment for development. Using Go with the newer version when we start a new project. For existing project using older Go, we can install multiple Go with different version.

When we need to use older package as dependency, we simply install that package with older version tag, e.g. go get github.com/wilk/uuid@0.0.1(make sure GO111MODULE=on) and import the package. Simply running go build or go mod tidy will tidy up the go.mod file.

For editing the go.mod file with package:

go mod init .
go mod edit -require github.com/wilk/uuid@0.0.1
go get -v -t ./...
go build
go install
# https://stackoverflow.com/a/51979879

Snippet of .envrc file for Go environment variable management per project:

# .envrc
export GO111MODULE=on
export GOPRIVATE=github.com/[mycompany]/.
export GOFLAGS=-mod=vendor # see vendor

Directive

Using replace directive like following allows local package loading:

# go.mod
# ...

replace (
    golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
    golang.org/x/net => example.com/fork/net v1.4.5 // to other package
    golang.org/x/net v1.2.3 => ./fork/net
    golang.org/x/net => ./fork/net // to local package
)

References