Before this:Interfaces & composition
Packages & modules
Key takeaways
A package is a directory of Go files compiled together; a module is a
versioned collection of packages defined by a go.mod file. You import
packages by path, and capitalization controls visibility — an Uppercase name
is exported, a lowercase name is private to its package. No public/private
keywords needed.
Once a program grows past one file, you need structure. This lesson covers how Go organizes code and manages the libraries it depends on.
Packages: the unit of code
Every Go file starts with a package line, and all files in a directory belong to
the same package. You pull one package into another with import:
package scanner
import (
"fmt"
"github.com/mattcheramie/gophertrunk/internal/dsp"
)
Standard-library packages are imported by short name (fmt, os, net);
third-party and internal packages by their full path. GopherTrunk is organized this
way — internal/dsp, internal/scanner, and so on — each a package with a clear
job.
Visibility by capitalization
Go has no public or private keyword. Instead, the first letter of a name
decides:
func Decode(...) // exported — other packages can call it
func decode(...) // unexported — visible only inside this package
The same rule applies to types, struct fields, constants, and variables. It makes the public surface of a package obvious at a glance: capitals are the API, lowercase is the implementation.
Modules: versioned dependencies
A module is the unit you version and share. Its go.mod file names the module
and pins every dependency:
module github.com/mattcheramie/gophertrunk
go 1.22
require (
github.com/some/library v1.4.2
)
When you import a package you don’t yet have, go get or go mod tidy fetches it
and records the exact version, and go.sum locks its checksum. A fresh checkout
therefore builds with identical dependencies for everyone — no “works on my
machine” surprises. That reproducibility is what makes Go projects easy to
build in CI and to
ship as one binary.
The internal directory
A package under a directory named internal/ can only be imported by code in the
same module. GopherTrunk uses this deliberately: its guts live under internal/, so
they’re private implementation the rest of the world can’t depend on, leaving the
maintainers free to change them.
Quick check: a function named decode (lowercase d) is…
Recap
- A package is a directory of files compiled together; you import it by path.
- A module (
go.mod) versions a set of packages and pins dependencies for reproducible builds. - Capitalization controls visibility —
Uppercaseis exported,lowercaseis private. - The
internal/directory hides packages from outside the module.
Next up: Go’s core collections — slices and maps — plus generics.
Frequently asked questions
What is the difference between a package and a module?
A package is a single directory of Go files that are compiled together and imported as a unit. A module is a collection of packages versioned together, defined by a go.mod file at its root. You import packages; you version and distribute modules. A small project is one module containing several packages.
How does Go decide what is public?
By capitalization, not keywords. An identifier — a function, type, field, or variable — whose name starts with an uppercase letter is exported and visible to other packages. A lowercase name is private to its own package. There is no public or private keyword; the first letter says it all.