Package and module
What you'll learn:
- tell a package from a module and work out the import path of your own package;
- read go.mod and understand what
go mod tidydoes; - write tests in
_test.gofiles and read ago test -vreport; - build table tests with
t.Runand choose betweent.Errorfandt.Fatalf.

Until now every program you wrote fit in one file. A real Go project is tens or hundreds of files, and they need a place each. In this theme we go from one file to a project: how Go splits code into parts, how the parts find each other, how to check it all.
Picture a factory. The factory has its address on the gate. Inside there are workshops, each with its own job, and the workshops hold machines. Go is organized the same way:
- a module is the factory with its address on the gate;
- a package is a workshop, that is, a directory of files;
- a file is a machine in the workshop.
You met packages in the first theme. Every program started with the line package main, and fmt and strings are packages of the standard library:
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.ToUpper("go")) // GO
fmt.Println(strings.Repeat("ab", 3)) // ababab
}Let's walk through the program line by line:
package mainsays which package the file belongs to.importbrings in two packages:fmtandstrings.strings.ToUpper("go")calls theToUpperfunction of thestringspackage and printsGO.strings.Repeat("ab", 3)repeats the string three times and printsababab.
The function name is preceded by the package name and a dot. That is how Go knows which workshop to take the machine from.
A package is a directory
A package is all the .go files in one directory that have the same name after package on their first line. These rules follow from that:
- one directory holds exactly one package;
- the files of a package see each other's functions, types and variables without any import;
- package
mainwith amainfunction is a program you can run; - a package with any other name is code for other packages: they import it, nobody runs it.
Exported names
A workshop does not hand out everything it has. Some things go to the dispatch window, and some stay inside. In Go the first letter of a name decides:
- a name that starts with a capital letter is exported: other packages see it;
- a name that starts with a lowercase letter stays inside its package.
Here is a store package that totals a purchase with a discount:
package store
func Total(prices []int) int {
sum := 0
for _, p := range prices {
sum += p
}
return sum - discount(sum)
}
func discount(sum int) int {
return sum / 10
}Total starts with a capital letter, so another package can call it. discount is an internal detail: Total calls it because they share a package, and from outside it is invisible.
Module and import path
A module is a tree of directories with packages and a go.mod file at its root. The first line of go.mod is the address on the factory gate: module example.com/shop. Here is the shop module on disk, with the import paths of its packages on the right:
shop/
├── go.mod module example.com/shop
├── main.go package main
└── store/
└── store.go package store → "example.com/shop/store"The import path of a package is the module path plus the package's directory inside the module. The package in the store/ directory is imported as "example.com/shop/store". Nobody imports the main package at the root: it is the program itself.
Here is a main.go that uses the store package:
package main
import (
"fmt"
"example.com/shop/store"
)
func main() {
fmt.Println(store.Total([]int{100, 250})) // 315
}Let's go through the call step by step:
- The import
"example.com/shop/store"brings in the package from thestore/directory. - The code refers to it by the name from its
packageline, that is,store. store.Totaladds100and250, subtracts the35discount and returns315.
Standard packages such as fmt are imported by a short path with no address: they come with Go. Your own packages always start with the module path.
This is where the factory picture stops working. In a factory two workshops can pass parts back and forth. Go does not allow that: if store imports a report package and report imports store, the build stops with import cycle not allowed. Imports always go one way.
Common mistake
main called a function whose name starts with a lowercase letter:
fmt.Println(store.discount(350)) // ✗name discount not exported by package storeThe program does not compile. discount exists, but it lives inside the store package, and main is another package. When a function is needed outside, it is renamed with a capital letter: Discount.
Key points
- A package is a directory of files with one name on their
packageline;mainis a program, other packages are imported. - A name with a capital letter is exported; a lowercase one is visible only inside its package.
- A module is a tree of packages with go.mod at its root; the first line of go.mod sets the module path.
- An import path is the module path plus the package's directory:
"example.com/shop/store".
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.