2.5 go get and go install: Adding and Installing Packages
Right, so you want to add a third-party package to your project. Welcome to the fun part. You’ve got two main tools for the job: go get and go install. They seem similar, but they serve two very different masters, and confusing them is a rite of passage for every Go developer. Let’s demystify them.
The Old Way vs. The New Way
First, a quick history lesson because context is everything. Before Go 1.11 and the advent of modules, go get was the Swiss Army knife for fetching and building and installing packages. It was a bit of a mess. The designers, in their infinite wisdom (and I mean that mostly sincerely), decided to untangle this with the introduction of modules.
Now, in the glorious present, the roles are much clearer:
go getis primarily for adding or updating dependencies in yourgo.modfile.go installis primarily for compiling and installing executable programs to yourGOPATH/bin(orGOBIN).
Think of it as go get for libraries you import and go install for tools you run. It’s not a perfect dichotomy, but it’s a solid starting point.
go get: Your Dependency Manager
You use go get when you’re inside a module and you need to pull in a new library or update an existing one. It’s how you manipulate your go.mod file without manually typing in version numbers like some kind of medieval scribe.
# From within your module directory
go get github.com/fatih/color@latest
This command does a few brilliant things: it downloads the code, figures out the latest version, and adds that dependency to your go.mod and go.sum files. The @latest suffix is a module-aware thing; you can also specify @v1.2.3 for a specific version or @master for living on the edge.
Why is this a big deal? Because it means your project explicitly declares every version of every dependency it uses. Reproducible builds aren’t just a nice idea anymore; they’re the default. The old method of just cloning into GOPATH and hoping for the best is now officially deprecated, and good riddance.
A common pitfall? Forgetting that go get by itself, without an @version suffix, still works but its behavior can change based on context. It might upgrade other dependencies to satisfy requirements. My advice: always be explicit. Use @latest if you genuinely want the latest stable release.
go install: Your Tool Installer
Now, let’s talk about go install. You use this when you want to download, compile, and install an executable command-line tool. This is for things like golangci-lint, staticcheck, or hey—tools that aren’t library dependencies for your project but are useful for you, the developer.
The critical difference? You run it from anywhere, not necessarily from within a module directory.
# Install a tool at a specific version. Note the @version at the END.
go install github.com/go-delve/delve/cmd/dlv@latest
This command fetches the Delve debugger at its latest version, compiles it, and plops the binary right into your $GOBIN directory (which defaults to $GOPATH/bin). Make sure that directory is in your system’s PATH, or you’ll be wondering why the shell can’t find your shiny new tool.
The best practice here is to use go install with an explicit version tag for your tools. This is far cleaner than the old go get -u tool/url method, which would pollute your global workspace. Now, each tool version is managed by the go command, isolated and versioned cleanly. It’s a huge improvement, even if the syntax feels a bit backward at first (@latest goes on the end, not after go install).
When Things Get Weird: The Overlap
Of course, it wouldn’t be software if there werenn’t a weird edge case. You can use go install to install an executable that’s part of your current module or a local directory. This is useful for building your own project’s commands.
# From within your module, install the cmd/myapp binary to GOBIN
go install ./cmd/myapp
Notice the ./ prefix? That tells the go tool to use the package from the local file system instead of trying to download it. Forget that ./, and it will try to find the package online, fail spectacularly, and you’ll feel a brief but profound sense of confusion. We’ve all been there.
So, to recap: use go get to manage dependencies inside your module’s go.mod. Use go install @version from anywhere to manage your global developer tools. It’s a separation of concerns that, once you get used to it, you’ll genuinely appreciate. The designers got this one right, even if the path to get here was a bit convoluted.