Packages and Modules

Go organizes code into packages (directories) and modules (dependency management).

Packages

Package Basics

Every Go file starts with a package declaration:

package main        // Executable program
package http        // Library package
package internal    // Internal package (special meaning)
  • Package name = directory name (by convention)
  • All files in a directory must have the same package name
  • main package with main() function = executable

Visibility (Exported vs Unexported)

package user

// Exported (starts with uppercase) - accessible from other packages
type User struct {
    Name  string  // Exported field
    Email string  // Exported field
    age   int     // Unexported field
}

func NewUser(name string) *User {  // Exported function
    return &User{Name: name}
}

func validateEmail(email string) bool {  // Unexported function
    // Only accessible within this package
}

Importing Packages

// Single import
import "fmt"

// Multiple imports
import (
    "fmt"
    "strings"
    "time"
)

// With alias
import (
    f "fmt"
    str "strings"
)
f.Println("Hello")

// Blank import (for side effects only)
import _ "github.com/lib/pq"  // Registers database driver

// Dot import (avoid - pollutes namespace)
import . "fmt"
Println("Hello")  // Works without fmt prefix

Package Initialization

package config

var settings map[string]string

func init() {
    // Runs before main()
    settings = loadSettings()
}

// Multiple init functions are allowed
func init() {
    // Runs in order of declaration
}

Initialization order:

  1. Imported packages are initialized first
  2. Package-level variables are initialized
  3. init() functions run in order of declaration
  4. main() runs

Modules

Modules are Go's dependency management system (Go 1.11+).

Creating a Module

mkdir myproject
cd myproject
go mod init github.com/username/myproject

This creates go.mod:

module github.com/username/myproject

go 1.21

Adding Dependencies

# Add dependency (automatically updates go.mod)
go get github.com/gin-gonic/gin

# Add specific version
go get github.com/gin-gonic/gin@v1.9.1

# Add latest version
go get github.com/gin-gonic/gin@latest

# Update all dependencies
go get -u ./...

# Clean up unused dependencies
go mod tidy

go.mod File

module github.com/username/myproject

go 1.21

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

require (
    // Indirect dependencies (transitive)
    github.com/some/transitive v1.0.0 // indirect
)

go.sum File

Contains cryptographic checksums for dependencies. Commit this file!

github.com/gin-gonic/gin v1.9.1 h1:4+fr...
github.com/gin-gonic/gin v1.9.1/go.mod h1:...

Useful Module Commands

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

# Add missing / remove unused dependencies
go mod tidy

# Download dependencies to local cache
go mod download

# Verify dependencies
go mod verify

# Show dependency graph
go mod graph

# Explain why a package is needed
go mod why github.com/some/package

# Create vendor directory
go mod vendor

Project Structure

Simple Project

myproject/
├── go.mod
├── go.sum
├── main.go
└── utils.go

Medium Project

myproject/
├── go.mod
├── go.sum
├── main.go
├── config/
│   └── config.go
├── handlers/
│   ├── user.go
│   └── product.go
├── models/
│   ├── user.go
│   └── product.go
└── internal/
    └── database/
        └── db.go

Large Project (Common Layout)

myproject/
├── go.mod
├── go.sum
├── cmd/
│   ├── server/
│   │   └── main.go        # go run ./cmd/server
│   └── cli/
│       └── main.go        # go run ./cmd/cli
├── internal/              # Private packages
│   ├── config/
│   ├── database/
│   └── service/
├── pkg/                   # Public packages
│   └── api/
├── api/                   # API definitions (OpenAPI, protobuf)
├── web/                   # Web assets
├── scripts/               # Build/CI scripts
├── docs/                  # Documentation
└── test/                  # Integration tests

Internal Packages

The internal directory has special meaning:

myproject/
├── internal/
│   └── secret/
│       └── secret.go      # Only importable by myproject
└── cmd/
    └── app/
        └── main.go        # Can import internal/secret

Packages in internal can only be imported by:

  • Code in the same module
  • Code in the same directory tree

Building and Running

# Run directly
go run main.go
go run ./cmd/server

# Build executable
go build                    # Creates executable named after module
go build -o myapp           # Custom name
go build ./cmd/server       # Build specific package

# Install to $GOPATH/bin
go install

# Cross-compile
GOOS=linux GOARCH=amd64 go build
GOOS=windows GOARCH=amd64 go build
GOOS=darwin GOARCH=arm64 go build

Build Tags

Conditional compilation:

//go:build linux
// +build linux

package main

// This file is only compiled on Linux
//go:build !windows

package main

// Compiled on all platforms except Windows

Multiple tags:

//go:build linux && amd64

//go:build debug || test

Build Flags

# Strip debug info (smaller binary)
go build -ldflags="-s -w"

# Inject version info
go build -ldflags="-X main.version=1.0.0"

# In code:
var version string  // Set by ldflags

# Build for debugging
go build -gcflags="all=-N -l"

Working with Multiple Packages

Importing Your Own Packages

myproject/
├── go.mod                  # module github.com/user/myproject
├── main.go
└── utils/
    └── helpers.go
// main.go
package main

import "github.com/user/myproject/utils"

func main() {
    utils.DoSomething()
}
// utils/helpers.go
package utils

func DoSomething() {
    // ...
}

Local Module Replace

For developing multiple modules locally:

// go.mod
module github.com/user/myproject

require github.com/user/mylib v1.0.0

// Use local version instead
replace github.com/user/mylib => ../mylib

Workspace Mode (Go 1.18+)

For working with multiple modules:

# Create workspace
go work init ./module1 ./module2

# Add module to workspace
go work use ./module3

Creates go.work:

go 1.21

use (
    ./module1
    ./module2
)

Documentation

Package Documentation

// Package strings provides functions to manipulate strings.
//
// This is additional documentation that appears in the package overview.
package strings

// Split slices s into all substrings separated by sep.
// It returns a slice of the substrings between those separators.
func Split(s, sep string) []string {
    // ...
}

Viewing Documentation

# View docs in terminal
go doc fmt
go doc fmt.Println

# Start local documentation server
go doc -http=:6060
# Then visit http://localhost:6060

# Generate HTML docs
go doc -all fmt > fmt.txt

Best Practices

Package Naming

// Good: short, lowercase, no underscores
package http
package json
package user

// Bad
package HTTPServer
package json_parser
package userService

Import Organization

import (
    // Standard library
    "fmt"
    "strings"

    // Third-party
    "github.com/gin-gonic/gin"

    // Local packages
    "github.com/user/project/internal/config"
)

Use goimports to automatically organize imports.

Avoid Cyclic Imports

// BAD: package A imports B, B imports A
a/ -> b/
b/ -> a/  // Error!

// Solution 1: Merge packages
// Solution 2: Extract shared code to third package
// Solution 3: Use interfaces

Keep Main Minimal

// cmd/server/main.go
package main

import (
    "log"
    "github.com/user/project/internal/app"
)

func main() {
    if err := app.Run(); err != nil {
        log.Fatal(err)
    }
}

Next Steps

Continue to 11-testing.md to learn about testing in Go.