Taming go get: Installing Packages in Go for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why "go get" broke and how to fix it?

\n

If you've ever wondered "How can I install a package with go get?" — you're not alone. This question has thousands of views on Stack Overflow, and for good reason. It used to be simple: go get github.com/some/package — and the library would be in your $GOPATH/src. But after Go 1.16, everything changed. Now go get no longer installs packages in the traditional sense. Instead, it only updates go.mod. And for installing binaries, the go install command was introduced.

\n

In this article, we'll figure out what happened, how to properly install packages and dependencies now, and why beginners get so confused. Let's go!

\n\n

A Brief History: From GOPATH to Modules

\n

Before Go 1.11, all code lived in GOPATH — a special folder where all your projects and third-party packages were stored. The go get command downloaded sources directly there. It was convenient, but it caused versioning problems: if two projects required different versions of the same library — chaos ensued.

\n

With the arrival of Go modules (starting from Go 1.11 and stable since 1.16), each project got its own go.mod — a file specifying dependencies and their versions. Now go get works differently:

\n
    \n
  • Adds a dependency to go.mod
  • \n
  • Downloads it to the cache (usually $GOPATH/pkg/mod)
  • \n
  • Does not create a binary in $GOPATH/bin
  • \n
\n

If you need to install a ready-made program (e.g., a command-line utility) — use go install.

\n\n

How go get works now: Step-by-step breakdown

\n

Open a terminal and create a new module:

\n
mkdir myproject\ncd myproject\ngo mod init myproject
\n

Now let's add, say, the popular gorilla/mux library for routing. Previously we would write:

\n
go get github.com/gorilla/mux
\n

And it will work — the library will be downloaded. But look at what happened:

\n
    \n
  1. A line appeared in go.mod: require github.com/gorilla/mux v1.8.0
  2. \n
  3. A go.sum file appeared in the project folder — a file with checksums for security
  4. \n
  5. The library sources are in the cache, not next to the project
  6. \n
\n

Now in your code you can import the package:

\n
package main\n\nimport (\n    "fmt"\n    "github.com/gorilla/mux"\n)\n\nfunc main() {\n    r := mux.NewRouter()\n    fmt.Println("Router created!")\n}
\n

Run go run main.go — everything works. But if you wanted to get a binary (a compiled program) — go get won't give you one.

\n\n

Installing binaries: go install to the rescue

\n

Let's say you found a cool Go utility, for example golangci-lint — a linter for code. To install it as a system command, you used to write:

\n
go get github.com/golangci/golangci-lint/cmd/golangci-lint
\n

Now this won't work (it will give a warning). The correct way is:

\n
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
\n

What will happen:

\n
    \n
  • Go will download the utility's sources
  • \n
  • Compile them
  • \n
  • Place the resulting binary in $GOPATH/bin (or $GOBIN, if the variable is set)
  • \n
\n

Make sure $GOPATH/bin is added to your PATH — otherwise the command won't be available. You can check it like this:

\n
echo $GOPATH\n# usually /home/user/go\nls ~/go/bin/
\n

If your binary is there — everything is fine.

\n\n

Common beginner mistakes (and how to avoid them)

\n

On Stack Overflow, people most often ask about these pitfalls:

\n