Skip to content
Theory · From a file to a project · lesson 1 of 10

Package and module

What you'll learn

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 tidy does;
  • write tests in _test.go files and read a go test -v report;
  • build table tests with t.Run and choose between t.Errorf and t.Fatalf.
A factory gate with a nameplate go.mod and a few workshops behind it

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:

GO
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:

  1. package main says which package the file belongs to.
  2. import brings in two packages: fmt and strings.
  3. strings.ToUpper("go") calls the ToUpper function of the strings package and prints GO.
  4. strings.Repeat("ab", 3) repeats the string three times and prints ababab.

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 main with a main function 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:

GO
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:

TEXT
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:

GO
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:

  1. The import "example.com/shop/store" brings in the package from the store/ directory.
  2. The code refers to it by the name from its package line, that is, store.
  3. store.Total adds 100 and 250, subtracts the 35 discount and returns 315.

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:

GO
fmt.Println(store.discount(350)) // ✗
TEXT
name discount not exported by package store

The 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 package line; main is 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.

Sign in another way