Golang Packages: Comprehensive Notes
Package Fundamentals
What is a Package in Go?
A package is a collection of related Go source files that are compiled together. Packages provide:
- Code organization
- Namespace management
- Code reusability
- Access control
Basic Package Structure
myproject/
├── go.mod # Module definition
├── main.go # Main package
└── utils/
├── math.go # Utility package
└── strings.go # Utility package
Creating and Using Packages
Package Declaration
// Every file must start with package declaration
package utils
// Exported function (capitalized)
func Add(a, b int) int {
return a + b
}
// Unexported function (lowercase)
func internalHelper() {
// ...
}Importing Packages
import (
"fmt" // Standard library
"math/rand" // Subpackage
"github.com/user/repo/pkg" // Third-party
"./utils" // Local package (relative path)
myalias "some/long/package/path" // Aliased import
)Package Initialization
init() Function
package db
var connection string
func init() {
// Runs when package is first imported
connection = os.Getenv("DB_CONN")
fmt.Println("Database package initialized")
}⚠️ Avoid complex logic in init() functions as initialization order can be tricky to debug.
Initialization Order
- Package-level variable initialization
- init() functions in order of declaration
- Main package execution
Visibility Rules
| Naming Convention | Visibility |
|---|---|
UpperCase | Exported |
lowerCase | Unexported |
package models
type User struct { // Exported
Name string // Exported
age int // Unexported
}
func NewUser() User { // Exported
return User{}
}
func validate(u User) bool { // Unexported
return u.age > 0
}Advanced Package Concepts
Internal Packages
// Inside project/internal/auth/authenticator.go
package auth
// Only accessible within the parent module
func Authenticate() bool {
// ...
}Vendor Directory
# Create vendor directory with dependencies
go mod vendor
# Build using vendor directory
go build -mod=vendorPackage Documentation
// Calculator provides basic arithmetic operations
package calculator
// Add returns the sum of two integers
//
// Example:
//
// sum := Add(3, 4) // returns 7
func Add(a, b int) int {
return a + b
}Common Patterns
Factory Pattern
package shapes
type Shape interface {
Area() float64
}
func NewShape(shapeType string) Shape {
switch shapeType {
case "circle":
return &Circle{}
case "rectangle":
return &Rectangle{}
default:
return nil
}
}Configuration Packages
package config
type Config struct {
Port int
Database string
}
var AppConfig Config
func init() {
AppConfig = loadConfig()
}
func loadConfig() Config {
// Load from file/env
}Package Management
go.mod File
module github.com/user/myapp
go 1.21
require (
github.com/lib/pq v1.10.9
golang.org/x/text v0.14.0
)
replace github.com/old/package => github.com/new/package v1.2.3Common Commands ⭐
# Initialize new module
go mod init github.com/user/repo
# Add dependencies
go get github.com/pkg/errors@latest
# Tidy dependencies
go mod tidy
# Verify dependencies
go mod verifyBest Practices
- Keep packages focused: Single responsibility principle
- Avoid circular dependencies: Refactor into common package if needed
- Minimal exported API: Only expose what’s necessary
- Clear documentation: Every exported element should be documented
- Consistent naming: Follow standard library conventions
💡 The Go standard library is an excellent resource for learning good package design patterns.
FAQ
Q1: What’s the difference between a package and a module?
A: A package is a collection of Go source files in the same directory. A module is a collection of related Go packages with versioned dependencies defined in a go.mod file.
Q2: How does Go handle package versioning?
A: Go uses semantic versioning (v1.2.3) and records specific versions in go.mod. It supports minimal version selection (MVS) for dependency resolution.
Q3: What is the purpose of the internal directory?
A: Packages inside internal are only importable by other packages within the same parent module, providing a way to create private shared code.
Q4: How would you structure a large Go project?
A: Typical structure:
project/
├── cmd/ # Main applications
│ └── app1/
│ └── main.go
├── internal/ # Private code
├── pkg/ # Library code
├── api/ # API contracts
├── configs/ # Configuration
├── scripts/ # Build scripts
└── go.modQ5: What’s the difference between go install and go get?
A:
go getdownloads and installs packages, updating go.modgo installcompiles and installs binaries to $GOPATH/bin
Q6: How do you handle package initialization with dependencies?
A: Use init() functions sparingly. For complex initialization, consider explicit initialization functions that are called by the main package.
Q7: What are blank imports (_) used for?
A: Blank imports are used when you need a package’s initialization side effects (like database drivers registering themselves) but don’t need to use the package directly.
Q8: How would you create a package that provides multiple implementations?
A: Use build tags or separate packages with a common interface:
// +build mysql
package database
import _ "github.com/go-sql-driver/mysql"Q9: What’s the purpose of the vendor directory?
A: The vendor directory contains all dependency packages, allowing builds to work without downloading dependencies and ensuring reproducible builds.
Q10: How do you document a Go package?
A:
- Package comment above package declaration
- Comments for each exported element
- Can generate docs with
go docor view at pkg.go.dev