Packages and Modules in Go: From Basics to Advanced
Go's package and module system is designed for dependency management and code organization. Let's explore it comprehensively.
Table of Contents
Basic Package Concepts
-
Every Go file belongs to a package
-
mainpackage is special (creates executable) -
Other packages are reusable libraries
-
Packages are organized in directories
// File: greeter/greet.go
package greeter
func Hello() string {
return "Hello, world!"
}
Creating Packages
-
Create a directory for your package
-
Add Go files with same package declaration
-
Export functionality using capitalized names
Example structure:
mylib/
├── mathutil/
│ ├── math.go
│ └── stats.go
└── stringutil/
└── strings.go
Importing Packages
Basic imports:
import (
"fmt"
"math/rand"
"github.com/user/mylib/mathutil"
)
Aliased imports:
import (
m "math"
"github.com/user/mylib/mathutil"
)
Dot imports (avoid in production):
import . "fmt" // Now can use Println directly
Blank imports (for side effects):
import _ "image/png" // registers PNG decoder
Visibility Rules
-
Uppercase names: exported (visible outside package)
-
Lowercase names: unexported (package-private)
package mylib
var privateVar int // package-private
var PublicVar int // exported
func privateFunc() {} // package-private
func PublicFunc() {} // exported
Init Functions
-
Special function that runs when package is imported
-
Can have multiple init() per package
-
Used for initialization tasks
package mylib
var config map[string]string
func init() {
config = loadConfig()
}
func loadConfig() map[string]string {
return map[string]string{"key": "value"}
}
Go Modules
Modules are Go's dependency management system:
- Initialize a module:
go mod init github.com/user/mymodule
- Resulting
go.modfile:
module github.com/user/mymodule
go 1.21
require (
github.com/some/dependency v1.2.3
)
- Key commands:
go mod tidy # Add missing and remove unused modules
go get -u # Update dependencies
go list -m all # List all dependencies
Version Management
-
Semantic versioning (vMAJOR.MINOR.PATCH)
-
Version suffixes:
-
v1.2.3- exact version -
v1.2- latest patch -
v1- latest minor -
master- branch name
-
Example go get commands:
go get github.com/user/repo@v1.2.3
go get github.com/user/repo@latest
go get github.com/user/repo@master
Vendor Directory
Local copy of dependencies:
go mod vendor
This creates a vendor directory with all dependencies.
To use vendor:
go build -mod=vendor
Advanced Patterns
Internal Packages
Packages in internal/ are only importable by parent directory:
project/
├── internal/
│ └── utils/ # Only importable by project/ code
└── main.go
Submodules
Create nested modules:
project/
├── go.mod # root module
├── submodule/
│ └── go.mod # submodule
Plugin Architecture
// main.go
package main
import "plugin"
func loadPlugin(path string) {
p, err := plugin.Open(path)
if err != nil {
panic(err)
}
sym, err := p.Lookup("Handler")
if err != nil {
panic(err)
}
handler := sym.(func(string))
handler("Hello from plugin")
}
Build Constraints
Control compilation with file suffixes:
// File: app_linux.go
//go:build linux
package main
func init() {
// Linux-specific initialization
}
Best Practices
-
Keep packages focused - single responsibility
-
Avoid circular dependencies
-
Use semantic versioning properly
-
Document exported elements with doc comments
-
Minimize package-level variables
-
Use internal/ for private packages
-
Pin important dependencies to exact versions
-
Regularly update dependencies (
go get -u) -
Vendor dependencies for production deployments
-
Use go.sum for reproducible builds
Complete Example
Module Structure
myapp/
├── go.mod
├── go.sum
├── main.go
└── internal/
└── calculator/
├── add.go
└── subtract.go
go.mod
module github.com/user/myapp
go 1.21
require (
github.com/sirupsen/logrus v1.9.3
)
calculator/add.go
package calculator
// Add returns the sum of two integers
func Add(a, b int) int {
return a + b
}
main.go
package main
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/user/myapp/internal/calculator"
)
func main() {
logrus.Info("Starting application")
sum := calculator.Add(5, 3)
fmt.Printf("5 + 3 = %d\\n", sum)
}