Chapter Takeaways


Congratulations! You've completed the installation and setup chapter. You now have:

In the next chapter, we'll dive deep into Go's syntax and language features.


# Create project directory
mkdir my-project
cd my-project

# Initialize a module
go mod init my-project

This creates a go.mod file:

module my-project

go 1.23

Module path naming

The module path should typically match your repository URL:

# For a GitHub project
go mod init github.com/username/my-project

# For a local project
go mod init my-project

Managing Dependencies

Adding dependencies

When you import a package in your code, Go automatically adds it to your dependencies:

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"  // External dependency
)

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello"})
    })
    r.Run()
}

Run go mod tidy to download dependencies:

go mod tidy

This will:

go.mod file example

module my-project

go 1.23

require (
    github.com/gin-gonic/gin v1.9.1
)

require (
    github.com/bytedance/sonic v1.8.0 // indirect
    github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
    // ... more dependencies
)

Adding a specific version

go get github.com/gin-gonic/[email protected]

Updating dependencies

# Update all dependencies to latest versions
go get -u ./...

# Update a specific dependency
go get -u github.com/gin-gonic/gin

# Update to a specific version
go get github.com/gin-gonic/[email protected]

Removing unused dependencies

go mod tidy

This removes dependencies that are no longer imported.

Understanding go.mod and go.sum

go.mod file

The go.mod file is the source of truth for your module:

module github.com/user/project

go 1.23

require (
    example.com/other v1.2.3
    example.com/another v0.1.0
)

replace example.com/other => ../local/other

exclude example.com/bad v1.0.0

go.sum file

The go.sum file contains cryptographic checksums of dependencies:

github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1S0lVI9J+0mg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:jD2toBW3GJUr7m1owhpKJk7vMnXvXzH1+3U3uBx9r5c=

Important points

Version Resolution and Multiple Versions

When working with Go modules, you may encounter situations where different dependencies require different versions of the same module. Understanding how Go resolves these conflicts is crucial for effective dependency management.

Version conflicts occur because in a complex dependency tree, different packages might require different versions of the same module. For example:

Go must resolve this to a single version that satisfies all requirements.

Go resolves versions using an algorithm called Minimal Version Selection (MVS). This algorithm ensures reproducible builds by selecting the minimum version that satisfies all requirements.

MVS operates in the following steps:

  1. Collect all requirements: Go examines all go.mod files in the dependency tree
  2. Find minimum satisfying version: For each module, it selects the highest version that satisfies all constraints
  3. Single version per module: Only one version of each module is used in the build
  4. Reproducible: The same go.mod and go.sum files always produce the same dependency tree

Example of version resolution

// Your go.mod
module myproject
go 1.23
require (
    github.com/example/lib v1.2.0
    github.com/other/pkg v1.0.0
)

// github.com/example/lib v1.2.0's go.mod requires:
//   github.com/common/util v1.1.0

// github.com/other/pkg v1.0.0's go.mod requires:
//   github.com/common/util v1.0.0

// Result: Go selects v1.1.0 (the minimum that satisfies both)

Viewing the resolved dependency tree

You can see which versions are actually used:

# List all dependencies with versions
go list -m all

# Show why a specific module is included
go mod why -m github.com/example/lib

# Show the dependency graph
go mod graph

Example output of go list -m all

myproject
github.com/example/lib v1.2.0
github.com/other/pkg v1.0.0
github.com/common/util v1.1.0  # Resolved to satisfy both dependencies

Handling version conflicts

If dependencies require incompatible versions (e.g., v1 and v2), Go treats them as different modules:

// These are treated as DIFFERENT modules:
github.com/example/lib v1.2.0
github.com/example/lib/v2 v2.0.0  // Different import path required

Major version changes

When a module releases a major version (v2, v3, etc.), the import path must change:

// v1 module
import "github.com/example/lib"

// v2 module (different import path)
import "github.com/example/lib/v2"

This allows both versions to coexist in the same project.

Practical example

# Initialize a project
go mod init example.com/myapp

# Add a dependency
go get github.com/gin-gonic/[email protected]

# Check what versions are actually used
go list -m all

# See the dependency graph
go mod graph | grep gin

Understanding indirect dependencies

Dependencies can be marked as indirect in go.mod:

require (
    github.com/gin-gonic/gin v1.9.1  // Direct dependency
)

require (
    github.com/bytedance/sonic v1.8.0 // indirect
    // This is a dependency of gin, not directly imported by your code
)

Updating to resolve conflicts

If you need a newer version to satisfy all dependencies:

# Update all dependencies to latest compatible versions
go get -u ./...

# Update a specific dependency
go get -u github.com/example/lib

# Update to a specific version
go get github.com/example/[email protected]

Key points about version resolution

Troubleshooting version issues

# If you get version conflicts, check:
go mod why -m <module-name>  # Why is this module included?
go list -m -versions <module-name>  # What versions are available?
go mod tidy  # Clean up and resolve dependencies

Working with Modules

Vendoring dependencies

You can vendor dependencies (copy them into your project):

go mod vendor

This creates a vendor/ directory with all dependencies. Useful for:

Building with vendor

go build -mod=vendor

Module proxy

Go uses a module proxy by default (proxy.golang.org). You can configure it:

# Check proxy settings
go env GOPROXY

# Use direct mode (no proxy)
export GOPROXY=direct

# Use private proxy
export GOPROXY=https://proxy.company.com,direct

Private modules

For private repositories, configure authentication:

# Configure Git to use credentials
git config --global url."https://user:[email protected]/".insteadOf "https://github.com/"

# Or use GOPRIVATE
go env -w GOPRIVATE=github.com/company/*

Complete Example: A Module-Based Project

Let's create a complete example:

# Create project
mkdir calculator
cd calculator

# Initialize module
go mod init github.com/user/calculator

# Create main.go
cat > main.go << 'EOF'
package main

import (
    "fmt"
    "github.com/user/calculator/math"
)

func main() {
    result := math.Add(10, 5)
    fmt.Printf("10 + 5 = %d\n", result)
}
EOF

# Create math package
mkdir math
cat > math/add.go << 'EOF'
package math

func Add(a, b int) int {
    return a + b
}
EOF

# Build and run
go build
./calculator

Project structure

calculator/
  go.mod
  go.sum
  main.go
  math/
    add.go

This demonstrates:


Congratulations! You've completed the installation and setup chapter. You now have:

In the next chapter, we'll dive deep into Go's syntax and language features.