On this page
Modules and Packages
Go's Module and Package System
Go's module system, introduced in Go 1.11 and made the standard in Go 1.16, solves one of the most complicated problems in any language: reproducible, versioned dependency management. Before modules, Go used GOPATH — a system that caused many problems with dependency versions.
Understanding the difference between modules and packages is fundamental:
- Package: a directory with
.gofiles sharing the samepackage name. It is the unit of code organization. - Module: a collection of packages with a common import prefix and a
go.modfile. It is the unit of distribution and versioning.
Initializing a Module
# Create the project directory
mkdir my-api
cd my-api
# Initialize the module with the import path
go mod init github.com/yourusername/my-api
# Result: go.mod file created
cat go.modmodule github.com/yourusername/my-api
go 1.26The module path is the unique identifier for your module. By convention use the repository path (GitHub, GitLab, etc.), but it can be any string.
Idiomatic Project Structure
my-api/
├── go.mod
├── go.sum
├── main.go ← entry point
├── cmd/
│ ├── server/
│ │ └── main.go ← HTTP server
│ └── migrate/
│ └── main.go ← migration CLI tool
├── internal/ ← private packages (not importable outside the module)
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ ├── user.go
│ │ └── user_test.go
│ └── repository/
│ └── user.go
├── pkg/ ← public packages (importable by other modules)
│ └── validator/
│ └── validator.go
└── api/
└── openapi.yamlThis structure follows the Standard Go Project Layout, though for small projects it is perfectly acceptable to have everything in the root directory.
Packages: The Unit of Organization
All .go files in the same directory must declare the same package name:
// user/user.go
package user
type User struct {
ID int
Name string
}
// user/repository.go
package user // same directory, same package
type Repository struct {
db *sql.DB
}Exported vs. Unexported Identifiers
In Go, visibility is controlled by the case of the first character:
package mypackage
// EXPORTED — visible from outside the package
type User struct {
ID int // exported field
Name string // exported field
email string // unexported field — lowercase
}
// EXPORTED
func NewUser(name, email string) *User {
return &User{Name: name, email: email}
}
// EXPORTED
const MaxUsers = 1000
// EXPORTED
var ErrNotFound = errors.New("user not found")
// UNEXPORTED — only visible in this package
func validateEmail(email string) bool {
return strings.Contains(email, "@")
}
// UNEXPORTED
type internalConfig struct {
timeout int
retries int
}Importing Packages
// Single import
import "fmt"
import "os"
import "net/http"
// Block import (preferred)
import (
"fmt"
"os"
"net/http"
"time"
// External dependencies — separate group by convention
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// Alias to avoid name collisions
import (
mathRand "math/rand"
cryptoRand "crypto/rand"
)
// Side-effect import (only init())
import _ "github.com/lib/pq" // register PostgreSQL driver
// Dot import — import all into current scope (rare)
import . "fmt" // allows using Println instead of fmt.PrintlnThe `internal` Directory
Go has a special rule: packages inside an internal directory can only be imported by code within the parent directory tree:
my-app/
├── internal/
│ └── config/ ← only importable from within my-app/
│ └── config.go
└── cmd/
└── server/
└── main.go ← can import my-app/internal/configThis lets you have internal APIs without exposing them publicly — essential for large projects.
Dependency Management
Adding a Dependency
# Add the latest version
go get github.com/gin-gonic/gin
# Add a specific version
go get github.com/gin-gonic/[email protected]
# Add the latest pre-release
go get github.com/gin-gonic/gin@latest
# Update all dependencies to latest patch/minor
go get -u ./...
# Only update patches (safer)
go get -u=patch ./...Removing a Dependency
# After removing the imports from code:
go mod tidyThe `go.mod` File
module github.com/yourusername/my-api
go 1.26
require (
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/validator/v10 v10.22.0
gorm.io/driver/postgres v1.5.9
gorm.io/gorm v1.25.12
)
require (
// indirect dependencies (managed automatically)
golang.org/x/crypto v0.26.0 // indirect
...
)The `go.sum` File
go.sum contains cryptographic hashes of each dependency. Go verifies these checksums before using any module, guaranteeing dependency integrity. Never edit go.sum manually — Go manages it automatically.
Essential Module Commands
# View all dependencies (direct and indirect)
go list -m all
# See why a dependency is included
go mod why github.com/gin-gonic/gin
# Verify dependency integrity
go mod verify
# Download all dependencies to the local cache
go mod download
# Create a vendor/ directory (for reproducible builds without network)
go mod vendor
# Clean the module cache
go clean -modcacheInstalling Executables with `go install`
# Install a Go executable
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# The binary is installed in $GOPATH/bin (or $GOBIN)
# Make sure $GOPATH/bin is in your PATH
which golangci-lintSpecial Packages
`package main`: The Entry Point
Every executable Go program has exactly one package main with a main() function:
// main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}The `init()` Function
Each package can have one or more init() functions that run automatically when the package is loaded, before main():
package config
var globalConfig *Config
func init() {
// This function runs automatically
globalConfig = loadConfigFromEnv()
}Execution order: all init() functions in dependencies → package's init() → main().
Organizing Tests
Tests in Go live alongside the code they test, in _test.go files:
package/
├── user.go
└── user_test.go ← tests in the same directory
# Run tests
go test ./... # all packages
go test ./internal/... # internal only
go test -v ./... # verbose
go test -cover ./... # with coverageWith the module system mastered, in the next lesson we will build REST APIs using Go's powerful standard library HTTP server.
Sign in to track your progress