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

go.mod and go mod tidy

In the first lesson of this theme you saw the first line of go.mod — the address on the factory gate. Yet in the course exercises you never created a go.mod yourself. Why? Every exercise on this site is built inside a ready-made module that the checker writes for you:

TEXT
module sandbox

go 1.26

On your own computer you have to set up the module yourself. The go mod init command does it, given the module path: go mod init example.com/shop, run in the project folder, creates a go.mod there whose first line is module example.com/shop. How to install Go is described in the "Set up your computer" block on the course page. In this lesson we look at what goes into go.mod and how to keep it in order.

Back to the factory. On the gate hangs the factory's passport: its address and the list of suppliers it gets parts from. Next to it is a log with the seal numbers of every batch received. And a storekeeper walks the workshops: what is needed but not ordered, he orders, and what is no longer needed, he cancels. In Go the passport is go.mod, the seal log is go.sum, and the storekeeper is the go mod tidy command.

What go.mod holds

go.mod is a short text with three kinds of lines:

  • module — the module path, which starts the import paths of all its packages;
  • go — the language version the code is written for;
  • require — a module from outside that the code needs, and its version.

While the project uses only the standard library, there are no require lines. Packages such as fmt and strings come with Go and are not written into go.mod.

Someone else's module in the project

Say the program needs unique identifiers. The uuid package of the github.com/google/uuid module can make them. Here is a program that imports it:

GO
package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Println(len(id.String())) // 36
}

Let's go through it line by line:

  1. The import path starts with the address of the outside module, github.com/google/uuid.
  2. uuid.New() creates a new random identifier.
  3. id.String() turns it into a string, and the program prints its length: 36.

The uuid module has to be downloaded and written into go.mod. That is what go mod tidy does — the command that brings go.mod in line with the code's imports:

TEXT
$ go mod tidy
go: finding module for package github.com/google/uuid
go: found github.com/google/uuid in github.com/google/uuid v1.6.0

The command found the import, downloaded the latest version of the module and added a require line to go.mod:

TEXT
module example.com/shop

go 1.26

require github.com/google/uuid v1.6.0

The go.sum file

Together with go.mod, a second file appeared — go.sum. It holds the checksums of the downloaded module:

TEXT
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

These are the seal numbers from the log. When the module is downloaded again, on another computer or a year later, Go checks its contents against these sums. If even one byte differs, the build stops. That is why go.mod and go.sum are always kept and shared together with the project's code.

When a module is no longer needed

Now let's remove the uuid import from main.go and run go mod tidy again. The command prints nothing, but go.mod goes back to how it was:

TEXT
module example.com/shop

go 1.26

The require line is gone, and the uuid lines have left go.sum too. The storekeeper cancelled a supplier nobody uses any more.

This is where the storekeeper picture stops working. A real storekeeper walks the workshops on his own, every day. go mod tidy does nothing until you run it, so you run it after every change to the imports.

Common mistake

An import of an outside module was added, but go mod tidy was never run:

GO
import "github.com/google/uuid" // ✗
TEXT
no required module provides package github.com/google/uuid; to add it:
	go get github.com/google/uuid

The build stops: go.mod has no require line for this module, and Go does not know which version to take. Go suggests go get, but go mod tidy works too: it adds every missing module at once.

Key points

  • go.mod holds the module path, the Go version and require lines for outside modules.
  • go mod init <path> creates go.mod in the project folder.
  • go.sum holds checksums of the downloaded modules; both files are kept with the project's code.
  • go mod tidy adds a require for every imported outside module and removes the unneeded ones.
  • The course exercises are built in a ready-made sandbox module, so they need no go.mod.

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