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
mainpackage withmain()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:
- Imported packages are initialized first
- Package-level variables are initialized
init()functions run in order of declarationmain()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.