Go

Packages & Modules

Every Go file you've seen in this domain started with package main — Introduction to Go mentioned it without explaining it. This chapter is the full picture: how Go organizes code into packages, and how modules manage dependencies between them, a structurally different approach from C's #include-based system.

Quick reference
What it is
A package is Go's unit of code organization — every file in a directory belongs to one. A module is a versioned collection of packages, Go's unit of dependency management.
Mental model
Where C's #include textually pastes a header's contents into your file, Go's import brings in a real, separately-compiled package by name — no text substitution involved at all.
Key numbers
One go.mod file per module, at the project root, naming the module and pinning every dependency's exact version.
Reach for it when
Organizing any Go project beyond a single file — which is to say, nearly every real Go project.

Packages

// file: mathutils/mathutils.go
package mathutils

func Square(n int) int {   // capitalized — EXPORTED, visible outside this package
    return n * n
}

func square2(n int) int {   // lowercase — unexported, only visible inside mathutils
    return n * n
}

Every .go file in the same directory belongs to the same package — there's no per-file boundary the way each .c file is its own translation unit in C's compilation model. Capitalization is, again, the entire access control mechanism, exactly as in struct fields.

import — not #include

import (
    "fmt"                          // standard library package
    "myproject/mathutils"           // a package within your own module
)

func main() {
    fmt.Println(mathutils.Square(5))   // 25
}

import is not text substitution — unlike C's #include, which pastes a header's literal contents into your file before real compilation starts, Go's import brings in an already-compiled, real package the compiler understands directly. There are no header files, no forward declarations, and no risk of the double-inclusion problem header guards exist to solve — Go's package system doesn't have that category of bug at all.

Modules and go.mod

go mod init myproject     # creates go.mod, declaring this directory as a module

go get github.com/some/dependency   # adds a dependency, recorded in go.mod with an exact version
// go.mod
module myproject

go 1.21

require github.com/some/dependency v1.2.3

A module is a versioned collection of one or more packages — go.mod names the module and pins every dependency to an exact version, so a build is reproducible across machines. This solves a real, historically painful problem: C has no standard, built-in equivalent, and dependency management in C/C++ projects is often handled ad hoc, per-project (a git submodule, a vendored copy, a system package manager).

Real-world applications

Gotchas

Field notes