πŸ”§ Chapter 2 of 20

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.

⏱ β€” πŸ“Š Beginner 🧩 20 sections

🎯 Learning Objectives

βœ… After this chapter, you will be able to:
  • 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 go CLI commands fluently
  • Understand what go run, go build, and go install each 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

🍺 Recommended: Homebrew

Using Homebrew lets you upgrade Go with a single command: brew upgrade go

shell
# 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

shell
# 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

shell
# 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.

VariableDefault ValuePurpose
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).
shell
# 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

πŸ“ GOPATH Layout (~~/go/)
~/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.
⚠️ GOPATH vs Modules

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

shell
# 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

textgo.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.

textgo.sum (excerpt)
# Format:  /go.mod 
#           
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt38=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
πŸ”’ Commit go.sum

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):

text
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
// 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.

shell
# 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
βœ… Output
Hello, Go!

Understanding the Program Structure

gomain.go
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.

shell
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

shell
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

shell
# 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

shell
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

shell
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

shell
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

shell
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

shell
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.

  1. Install VS Code from code.visualstudio.com
  2. Open the Extensions view (Ctrl+Shift+X / Cmd+Shift+X)
  3. Search for "Go" and install the extension by Google (ID: golang.Go)
  4. Open a Go file β€” VS Code will prompt to install tools. Click Install All. This installs: gopls, dlv, staticcheck, golines
  5. Add recommended settings to .vscode/settings.json
json.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
F12F12Go to definition
⌘ ClickCtrl+ClickGo to definition
βŒ₯F12Alt+F12Peek definition
⇧F12Shift+F12Show all references
F2F2Rename symbol
⌘.Ctrl+.Quick fix / code actions
βŒƒβ‡§MCtrl+Shift+MProblems panel

πŸ—‚οΈ Standard Go Project Layout

Go doesn't enforce a project structure, but the community has converged on conventions:

πŸ“ Recommended Go Project Structure
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
πŸ’‘ Simple Projects

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

1
What is the difference between go run, go build, and go install?
β–Ά
Detailed Answer

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.

2
What are Go modules? What problem do they solve?
β–Ά
Detailed Answer

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.

3
What does go mod tidy do?
β–Ά
Detailed Answer

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

🧩
Installation & Setup β€” Quiz
4 questions
1. Which file contains cryptographic checksums of downloaded modules?
2. Where does the Go installer put the Go SDK itself?
3. What does go mod tidy do?
4. Which command should you use to produce a binary for production deployment?
β€”

✏️ Exercises

Exercise 1
Initialise a Module and Add a Dependency
⭐ Beginner
  1. Create a directory called go-playground
  2. Run go mod init go-playground
  3. Add github.com/fatih/color dependency: go get github.com/fatih/color
  4. Write a program that prints "Hello, Go!" in green using the color package
  5. Run go mod tidy and inspect go.mod
gomain.go
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)
Exercise 2
Cross-Compile for Linux
⭐⭐ Intermediate

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.

shell
# 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 install puts binaries
  • Every Go project is a module β€” initialise with go mod init
  • go.mod declares version requirements; go.sum verifies integrity β€” commit both
  • go run = dev only; go build = production binary; go install = system tool
  • Always run go fmt, go vet, and go mod tidy before committing
  • Cross-compile with GOOS and GOARCH environment variables

πŸ“Œ Cheat Sheet

πŸ“Œ Installation & Toolchain β€” Quick Reference
Run (dev)
go run main.go
Build binary
go build -o app .
Init module
go mod init module/path
Add dependency
go get pkg@version
Tidy dependencies
go mod tidy
Run tests
go test -v -race ./...
Format
go fmt ./...
Static analysis
go vet ./...
Cross-compile
GOOS=linux GOARCH=amd64 go build
Shrink binary
go build -ldflags="-s -w"
Show docs
go doc fmt.Println
Env vars
go env GOROOT GOPATH GOBIN