Installation & Setup
Install Go on any OS, understand the workspace model, configure your editor,
and master every go command you'll use daily as a professional developer.
π― Learning Objectives
- Install Go on macOS, Linux, and Windows
- Understand and configure GOROOT, GOPATH, and GOBIN
- Initialise and work with Go modules (
go.mod,go.sum) - Configure VS Code with the official Go extension for a perfect dev environment
- Use all essential
goCLI commands fluently - Understand what
go run,go build, andgo installeach do differently - Write, run, format, vet, and test a Go program from the command line
π₯ Installing Go
Go supports macOS, Linux, Windows, and FreeBSD. The official installer handles everything.
macOS Installation
Using Homebrew lets you upgrade Go with a single command: brew upgrade go
# Method 1: Homebrew (recommended)
brew install go
# Method 2: Official installer
# Download from https://go.dev/dl/
# Run the .pkg installer β Go installs to /usr/local/go
# Verify installation
go version
# Output: go version go1.22.0 darwin/arm64
Linux Installation
# Download the latest tarball (check go.dev/dl for the latest version)
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
# Remove any previous Go installation & extract
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
# Add Go to PATH β add this to ~/.bashrc or ~/.zshrc
export PATH=$PATH:/usr/local/go/bin
# Reload your shell
source ~/.bashrc # or source ~/.zshrc
# Verify
go version
# go version go1.22.0 linux/amd64
Windows Installation
# Method 1: winget (Windows Package Manager)
winget install GoLang.Go
# Method 2: Official installer
# Download go1.22.0.windows-amd64.msi from go.dev/dl
# Run the installer β it automatically sets PATH
# Method 3: Chocolatey
choco install golang
# Verify in PowerShell or Command Prompt
go version
# go version go1.22.0 windows/amd64
π GOROOT, GOPATH & GOBIN
These three environment variables define where Go lives and where your code goes.
| Variable | Default Value | Purpose |
|---|---|---|
| GOROOT | /usr/local/go (Mac/Linux) or C:\Go (Windows) |
Where the Go SDK is installed. Contains the compiler, stdlib, and tools. Don't change this. |
| GOPATH | ~/go |
Your workspace. Contains src/, pkg/, bin/. Stores downloaded module caches at ~/go/pkg/mod/. |
| GOBIN | $GOPATH/bin |
Where go install puts compiled binaries. Add to PATH to run installed tools. |
| GOPROXY | https://proxy.golang.org,direct |
Module download proxy. The proxy caches modules and validates them with a checksum database. |
| GONOSUMCHECK | empty | Comma-separated list of module paths to skip the checksum database for (useful for private modules). |
# Inspect all Go environment variables
go env
# Check specific variables
go env GOROOT # /usr/local/go
go env GOPATH # /Users/you/go
go env GOBIN # /Users/you/go/bin
go env GOPROXY # https://proxy.golang.org,direct
go env GOMAXPROCS # number of CPU cores (default for concurrency)
# Add $GOPATH/bin to PATH so installed tools are executable
# Add this to ~/.zshrc or ~/.bashrc:
export PATH=$PATH:$(go env GOPATH)/bin
GOPATH Directory Structure
~/go/ βββ bin/ β Installed binaries (go install puts them here) β βββ gopls β Go language server β βββ dlv β Delve debugger β βββ pkg/ β βββ mod/ β Downloaded module cache (go get populates this) β β βββ github.com/ β β βββ golang.org/ β βββ cache/ β Build cache for faster rebuilds β βββ src/ β Legacy (pre-modules). Not used with go mod.
Before Go 1.11, all Go code had to live inside $GOPATH/src. This was painful.
Since Go 1.16, modules are the default. You can put your project anywhere
on your filesystem β just run go mod init in the project directory.
π¦ Go Modules
Go modules are the official dependency management system (introduced in Go 1.11, default in Go 1.16).
Every Go project is a module. A module is defined by a go.mod file.
Creating Your First Module
# Create a new project directory
mkdir myapp && cd myapp
# Initialise the module
go mod init github.com/yourname/myapp
# Creates go.mod with module path and Go version
# go.mod contents:
# module github.com/yourname/myapp
#
# go 1.22.0
# Add a dependency
go get github.com/pkg/errors@v0.9.1
# Updates go.mod and creates/updates go.sum
# Download all dependencies (if cloning a project)
go mod download
# Remove unused dependencies, add missing ones
go mod tidy
Anatomy of go.mod
module github.com/yourname/myapp // Module path β globally unique identifier
go 1.22.0 // Minimum Go version required
require (
github.com/pkg/errors v0.9.1 // Direct dependency
github.com/stretchr/testify v1.9.0 // Direct dependency (for tests)
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect β pulled by testify
github.com/pmezard/go-difflib v1.0.0 // indirect
)
Anatomy of go.sum
go.sum contains cryptographic hashes of each dependency's source code.
This ensures nobody can silently swap out a dependency to inject malicious code.
# Format: /go.mod
#
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt38=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Always commit go.sum to version control. Never modify it manually. It's your reproducible build guarantee.
Semantic Versioning in Go Modules
Go modules use Semantic Versioning (SemVer):
v1.2.3
β β βββ Patch: backward-compatible bug fixes
β βββββ Minor: backward-compatible new features
βββββββ Major: breaking changes (v2+ requires a module path change)
For major version 2+, the module path itself changes:
// go.mod for v2
module github.com/yourname/myapp/v2
// Import path in code also includes /v2
import "github.com/yourname/myapp/v2/pkg/something"
π Your First Go Program
Let's create and run a complete Go program step by step.
# Step 1: Create project
mkdir hello-go && cd hello-go
go mod init hello
# Step 2: Create main.go
cat > main.go << 'EOF'
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
EOF
# Step 3: Run it
go run main.go
Understanding the Program Structure
package main // Every Go file belongs to a package
// "main" is special: it defines an executable program
import "fmt" // Import the fmt package from the standard library
// Unused imports are compile errors
func main() { // Entry point β Go programs always start here
// No arguments, no return value
fmt.Println("Hello, Go!")
// Println: prints with a newline
// fmt.Printf: formatted printing (like C's printf)
// fmt.Fprintf: write to an io.Writer
}
β‘ Go Commands β Deep Dive
go run β Development Mode
go run compiles and immediately executes a Go program.
It creates a temporary binary in a temp directory, runs it, then deletes it.
Never use go run in production.
go run main.go # Run a single file
go run . # Run the package in the current directory
go run ./cmd/server/ # Run a specific package by directory path
go run -race main.go # Run with the race detector enabled
# go run compiles every time β not cached for execution
# Use for development and quick experiments only
go build β Produce a Binary
go build # Build current package, put binary here
go build -o myserver . # Name the output binary
go build ./... # Build all packages in all subdirectories
# Cross-compilation β build for a different OS/arch
GOOS=linux GOARCH=amd64 go build -o myapp-linux .
GOOS=windows GOARCH=amd64 go build -o myapp.exe .
GOOS=darwin GOARCH=arm64 go build -o myapp-mac-arm .
# Build flags for production
go build \
-ldflags="-s -w" \ # Strip debug symbols (smaller binary)
-trimpath \ # Remove local file paths from binary
-o myserver \
.
# -s: omit symbol table
# -w: omit DWARF debug info
# Result: ~30% smaller binary
go install β Install a Tool
# Install a tool from a remote module
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Binary goes to $GOPATH/bin/golangci-lint
# Install a specific version
go install golang.org/x/tools/gopls@v0.15.0
# Install the current module's binary
go install . # compiles and puts binary in $GOPATH/bin
go test β Testing
go test ./... # Run all tests in all packages
go test -v ./... # Verbose: show each test name
go test -run TestMyFunc ./... # Run only tests matching a pattern
go test -race ./... # Run with race detector
go test -count=1 ./... # Disable test caching (always re-run)
go test -cover ./... # Show code coverage percentage
go test -coverprofile=c.out ./... # Write coverage profile to file
go tool cover -html=c.out # Open coverage in browser
# Benchmarks
go test -bench=. ./... # Run all benchmarks
go test -bench=BenchmarkFoo -benchmem ./... # With memory stats
# Run a specific test file
go test -v -run TestSpecific ./pkg/mypackage/
go fmt β Format Code
go fmt ./... # Format all .go files in project (modifies in place)
gofmt -d main.go # Show diff of what would change (don't modify)
gofmt -l ./... # List files that need formatting
gofmt -w main.go # Write changes to file
# goimports: also manages imports (adds/removes) β install separately
go install golang.org/x/tools/cmd/goimports@latest
goimports -w main.go
go vet β Static Analysis
go vet ./... # Run all built-in static analysis checks
// go vet catches bugs like:
// - Wrong Printf format strings: Printf("%d", "hello") // β wrong type
// - Unreachable code
// - Mutex copied by value
// - Calls to sync.Mutex in wrong order
// - Struct tags with invalid syntax
// - Incorrect use of sync.Locker
// Example of what go vet catches:
package main
import "fmt"
func main() {
name := "Alice"
fmt.Printf("Hello %d\n", name) // go vet: Printf format %d has arg name of wrong type string
}
go mod β Module Management
go mod init github.com/user/project # Create go.mod
go mod tidy # Add missing, remove unused deps
go mod download # Download all deps to module cache
go mod verify # Verify checksums in go.sum
go mod graph # Print dependency graph
go mod why -m github.com/pkg/errors # Why is this dependency needed?
go mod vendor # Copy deps to ./vendor/ directory
# Upgrading dependencies
go get github.com/some/package@latest # Latest version
go get github.com/some/package@v1.2.3 # Specific version
go get -u ./... # Upgrade all deps to latest minor/patch
go get -u=patch ./... # Upgrade all deps to latest patch only
go doc β Documentation
go doc fmt # Show package-level docs for fmt
go doc fmt.Printf # Show docs for a specific function
go doc -all fmt # Show all exported symbols with docs
go doc net/http.Server # Show docs for a type
go doc -src fmt.Println # Show the actual source code
# Start local doc server (browse stdlib like godoc.org)
go install golang.org/x/tools/cmd/godoc@latest
godoc -http=:6060
# Open http://localhost:6060 in browser
π» VS Code Setup
VS Code with the official Go extension provides the best Go development experience.
- Install VS Code from code.visualstudio.com
- Open the Extensions view (Ctrl+Shift+X / Cmd+Shift+X)
- Search for "Go" and install the extension by Google (ID:
golang.Go) - Open a Go file β VS Code will prompt to install tools. Click Install All. This installs:
gopls,dlv,staticcheck,golines - Add recommended settings to
.vscode/settings.json
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"[go]": {
"editor.defaultFormatter": "golang.go",
"editor.tabSize": 4,
"editor.insertSpaces": false // Go uses tabs, not spaces!
},
"go.useLanguageServer": true,
"go.lintTool": "golangci-lint",
"go.lintOnSave": "workspace",
"go.testFlags": ["-v", "-race"],
"go.coverOnSave": false,
"go.toolsManagement.autoUpdate": true
}
Essential VS Code Shortcuts for Go
| Shortcut (Mac) | Shortcut (Win/Linux) | Action |
|---|---|---|
| F12 | F12 | Go to definition |
| β Click | Ctrl+Click | Go to definition |
| β₯F12 | Alt+F12 | Peek definition |
| β§F12 | Shift+F12 | Show all references |
| F2 | F2 | Rename symbol |
| β. | Ctrl+. | Quick fix / code actions |
| ββ§M | Ctrl+Shift+M | Problems panel |
ποΈ Standard Go Project Layout
Go doesn't enforce a project structure, but the community has converged on conventions:
myproject/ βββ go.mod β Module definition (required) βββ go.sum β Dependency checksums (commit this) βββ README.md β βββ cmd/ β Entry points (main packages) β βββ server/ β βββ main.go β package main, func main() β βββ internal/ β Private packages (not importable externally) β βββ auth/ β β βββ auth.go β β βββ auth_test.go β βββ database/ β βββ db.go β βββ pkg/ β Public packages (importable by external code) β βββ models/ β βββ user.go β βββ api/ β API definitions (OpenAPI/protobuf specs) β βββ openapi.yaml β βββ configs/ β Configuration files β βββ config.yaml β βββ scripts/ β Build, deploy, maintenance scripts β βββ build.sh β βββ docs/ β Documentation
For small programs or learning projects, a flat structure is perfectly fine: just main.go, go.mod, and any helpers in the same directory. Don't over-engineer the structure for a toy project.
π€ Interview Questions
go run, go build, and go install?go run: Compiles and executes in one step. The binary is temporary β created in a temp directory and deleted after execution. Use for development and experiments. Never use in production (it re-compiles every time).
go build: Compiles to a binary file at the specified path (or current directory if no -o flag). This binary can be copied anywhere and run without Go installed. Use for producing deployment artifacts.
go install: Compiles to a binary and places it in $GOPATH/bin (or $GOBIN). Used primarily for installing CLI tools you want available system-wide. Also used to install remote tools: go install tool@latest.
Common mistake: Using go run in Docker containers or CI pipelines to run the actual server. Always go build first, then run the binary.
Go modules are the official dependency management system. Before modules (pre-Go 1.11), all Go code had to live inside $GOPATH/src, and there was no standard way to version dependencies. If two projects needed different versions of the same library, you were stuck.
Modules solve this by: (1) allowing projects to live anywhere; (2) tracking exact versions in go.mod; (3) verifying integrity with cryptographic checksums in go.sum; (4) supporting semantic versioning.
A module is defined by a go.mod file at the root. The module path is globally unique (by convention it's the VCS hosting URL). go mod tidy keeps go.mod in sync with what your code actually imports.
go mod tidy do?go mod tidy ensures your go.mod and go.sum files are consistent with the source code. It does two things:
1. Adds any missing module requirements that are imported in your code but not in go.mod
2. Removes any module requirements from go.mod that are no longer imported anywhere in your code
Run it: (a) after adding new imports; (b) after removing imports; (c) before committing to version control; (d) as part of CI to verify the module graph is correct.
Production tip: Add go mod tidy && git diff --exit-code to your CI pipeline to enforce that go.mod is always tidied.
π§ͺ Chapter Quiz
go mod tidy do?βοΈ Exercises
- Create a directory called
go-playground - Run
go mod init go-playground - Add
github.com/fatih/colordependency:go get github.com/fatih/color - Write a program that prints "Hello, Go!" in green using the color package
- Run
go mod tidyand inspectgo.mod
package main
import "github.com/fatih/color"
func main() {
green := color.New(color.FgGreen, color.Bold)
green.Println("Hello, Go!")
}
// Run: go run main.go
// Output: Hello, Go! (in bold green)
Cross-compile the "Hello, Go!" program for Linux amd64, even if you're on macOS or Windows. Check the file size of the resulting binary.
# Cross-compile for Linux amd64
GOOS=linux GOARCH=amd64 go build -o hello-linux .
# Check binary info
file hello-linux
# hello-linux: ELF 64-bit LSB executable, x86-64
# Check size
ls -lh hello-linux
# -rwxr-xr-x 1.9M hello-linux
# Reduce binary size with linker flags
GOOS=linux GOARCH=amd64 go build \
-ldflags="-s -w" \
-o hello-linux-small .
ls -lh hello-linux-small
# -rwxr-xr-x 1.3M hello-linux-small (30% smaller!)
π Summary
- Install Go via Homebrew (Mac), apt/tar (Linux), or the official installer (Windows)
- GOROOT = where Go is installed. GOPATH = your workspace (~/go). GOBIN = where
go installputs binaries - Every Go project is a module β initialise with
go mod init go.moddeclares version requirements;go.sumverifies integrity β commit bothgo run= dev only;go build= production binary;go install= system tool- Always run
go fmt,go vet, andgo mod tidybefore committing - Cross-compile with
GOOSandGOARCHenvironment variables
π Cheat Sheet
go run main.gogo build -o app .go mod init module/pathgo get pkg@versiongo mod tidygo test -v -race ./...go fmt ./...go vet ./...GOOS=linux GOARCH=amd64 go buildgo build -ldflags="-s -w"go doc fmt.Printlngo env GOROOT GOPATH GOBIN