3.1 The Standard Go Workspace Layout
Right, let’s talk about where you put your stuff. This isn’t just about being tidy; it’s about speaking Go’s language so its tools can actually help you instead of throwing their virtual hands up in frustration. The good news is, after years of community bickering, we’ve mostly settled on a standard. The bad news is, it’s a standard with a few… idiosyncrasies. We’ll get to those.
The core idea is that all your Go code, for all your projects, should live inside one single directory on your machine. This is your workspace, and by convention, it’s called go. Not very creative, but effective. Under this directory, you’ll find three key folders: bin, pkg, and src. Forget src for a moment, because since Go 1.11, it’s been on life support, but we need to understand its ghost.
The bin, pkg, and src Triad (R.I.P. src)
$HOME/
└── go/ # Your $GOPATH, your entire universe.
├── bin/ # Compiled executables get dumped here.
├── pkg/ # Pre-compiled package archives (.a files).
└── src/ # Where we USED to put all our source code.
bin: This is where thego installcommand plops the binary when you build a command (anmainpackage). It’s fantastic because it means your Go tools are automatically in yourPATHif you add$GOPATH/binto it. Do that, by the way.export PATH=$PATH:$(go env GOPATH)/bin.pkg: The Go compiler is smart. When yougo builda dependency, it doesn’t recompile it from scratch every time. It stores the compiled version here, which makes subsequent builds blazingly fast. You can mostly ignore this folder; it’s the compiler’s messy workshop.src: This is where we used to put everything. Your projectgithub.com/yourname/yourprojectwould live at$GOPATH/src/github.com/yourname/yourproject. This was… fine, until it wasn’t. It forced your project’s location on disk to mirror its import path, which felt clever but was incredibly rigid. Thank the ancients we’ve moved on.
The Modern Way: Go Modules in Any Directory
Here’s the part where we break the chains. You no longer need to shackle your projects inside $GOPATH/src. With Go modules, your project can live anywhere—/Users/you/dev/chaos-project, C:\my-weird-folder, whatever. The module, defined by a go.mod file at its root, is what gives it its identity.
Let’s create a new project the right way, completely outside the old src hierarchy.
# Navigate anywhere you like!
cd ~/projects/my-awesome-go-app
# Initialize a new module. The module name is its canonical import path.
# Even if you haven't pushed it to GitHub yet, name it as if you will.
go mod init github.com/your-github-username/my-awesome-go-app
This command creates a go.mod file. This file is the boss. It tracks your module’s name and its dependencies. Let’s say you write a simple main.go:
package main
import (
"fmt"
"github.com/rs/zerolog" // A popular logging library
)
func main() {
logger := zerolog.New(os.Stdout)
fmt.Println("Hello, Module!")
logger.Info().Msg("This is a structured log!")
}
Now, run go mod tidy. This is your best friend. It intelligently adds only the dependencies you actually use to the go.mod file and creates the go.sum file for security. It also removes cruft you don’t need. Always run go mod tidy.
The vendor Directory and When to Use It
Sometimes you want to bring the party indoors. The go mod vendor command creates a vendor directory in your project root and copies all your dependencies into it. Why would you do this?
- Reproducible Builds: You’ve frozen your dependencies literally on disk. The Go toolchain will prefer these copies over those in the module cache, guaranteeing the same code is used every time.
- Offline Development: You can build your project without needing to hit the network to fetch dependencies.
- Internal Codebases: If you have internal private modules that the public Go proxy can’t access, vendoring can simplify things.
It’s a bit old-school and adds weight to your repo, but it’s a powerful tool for when you absolutely need to pin things down. To use it, just run go mod vendor. The toolchain does the rest. To build with the vendor directory, use go build -mod=vendor.
The Module Cache: Where Go Hoards Everything
Run go env GOMODCACHE. You’ll see a path, typically $GOPATH/pkg/mod. This directory is where Go stores every single version of every single dependency you’ve ever downloaded. It’s a glorious, organized hoard of code. You’ll see directories with names like github.com/rs/zerolog@v1.29.0.
This cache is what makes Go so fast. When you build a project, it doesn’t redownload the internet; it checks the cache first. You can safely delete this folder if it gets too big (go clean -modcache); Go will just redownload what it needs next time. It’s not a “project” directory; it’s a global system directory managed by the go command. Don’t touch the files inside, but feel free to admire the sheer scale of it.