Go Tutorials: Dependency Management with Go Modules

Go Modules become much easier once you keep one mental model in mind:

Your Go source code says what packages you use. go.mod says which modules/versions provide them. go.sum helps verify downloaded dependency content.

A module is normally your Go project or library together with its dependency information. go mod init creates the module’s go.mod file, while commands such as go get and go mod tidy keep dependency information synchronized with your source code. (Go.dev)


1. What is dependency management?

Suppose your program needs to generate a UUID.

You could write all the UUID logic yourself, or use an existing Go package:

import "github.com/google/uuid"

Now your application depends on code from another project.

That external code is a dependency.

Go Modules manage:

Your application
      |
      | imports
      v
github.com/google/uuid
      |
      | particular version
      v
    v1.6.0

The version of that dependency is recorded in your project’s go.mod.


2. What / Why / When / How / Where

QuestionSimple answer
What?Go Modules are Go’s dependency-management system.
Why?To track external libraries and their versions reliably.
When?For practically every normal Go project that contains packages or external dependencies.
How?Start with go mod init, add/use dependencies, and maintain them with commands such as go get and go mod tidy.
Where?Dependency information lives mainly in go.mod and go.sum at the module root.

3. The three things to remember

Think of a Go project like this:

go-mod-demo/
│
├── go.mod
├── go.sum
└── main.go

main.go

Your actual Go source code.

go.mod

Describes your module and records required module versions. (Go.dev)

Example:

module example.com/go-mod-demo

go 1.xx

require github.com/google/uuid v1.6.0

Don’t worry about the exact go version shown there; Go writes the appropriate value for your environment.

go.sum

Contains cryptographic hashes associated with downloaded module dependencies so Go can verify dependency content. (Go.dev)

You normally do not manually edit go.sum.


4. Complete example — UUID generator

This is the only example you need for understanding the basic workflow.

We will build:

Go Program
    |
    | import
    v
github.com/google/uuid
    |
    v
Generate UUID

The example uses github.com/google/uuid version v1.6.0. That version provides uuid.NewString(). (Chromium Git Repositories)


Step 1 — Create the project

mkdir go-mod-demo
cd go-mod-demo

Step 2 — Initialize a Go module

Run:

go mod init example.com/go-mod-demo

You should now have:

go-mod-demo/
└── go.mod

The command:

go mod init example.com/go-mod-demo

means:

go mod init
│   │   │
│   │   └── Name/path of our module
│   │
│   └────── Initialize
│
└────────── Go command

go mod init creates a new go.mod in the current directory and establishes that directory as the module root. (Go.dev)


Step 3 — Create main.go

Create this file:

package main

import (
	"fmt"

	// This package comes from an external Go module.
	"github.com/google/uuid"
)

func main() {
	// uuid.NewString() comes from the external dependency.
	id := uuid.NewString()

	fmt.Println("Generated UUID:", id)
}

That’s the complete application.

Notice this line:

"github.com/google/uuid"

Your application is saying:

“I need the uuid package.”

But Go still needs to know which module version should provide it.


Step 4 — Add the dependency

Run:

go get github.com/google/uuid@v1.6.0

Now look at your folder:

go-mod-demo/
│
├── go.mod
├── go.sum
└── main.go

And go.mod will contain a requirement for the dependency:

require github.com/google/uuid v1.6.0

The important connection is:

main.go
   |
   | import "github.com/google/uuid"
   |
   v
go.mod
   |
   | require github.com/google/uuid v1.6.0
   |
   v
Go downloads dependency

go get is one of Go’s standard ways to add or change a dependency version recorded for the module. (Go.dev)


Step 5 — Run the application

go run .

Example output:

Generated UUID: 550e8400-e29b-41d4-a716-446655440000

Your actual UUID will be different.

You have now successfully used an external Go dependency.


Step 6 — Clean the dependencies

Run:

go mod tidy

This is a very useful command.

It essentially asks Go:

“Look at my source code and make sure go.mod and go.sum match what the project really needs.”

go mod tidy adds missing module requirements, removes requirements that are no longer needed, and updates relevant go.sum entries. (Go.dev)

A good habit is:

go mod tidy

after adding or removing imports.


5. The complete workflow

flowchart TD
    A["Create Project"] --> B["go mod init"]
    B --> C["go.mod created"]
    C --> D["Write Go code"]
    D --> E["Import external package"]
    E --> F["go get dependency@version"]
    F --> G["go.mod / go.sum updated"]
    G --> H["go run ."]
    H --> I["go mod tidy"]

Or remember it simply as:

CREATE PROJECT
      ↓
go mod init
      ↓
WRITE CODE
      ↓
import dependency
      ↓
go get dependency
      ↓
go run .
      ↓
go mod tidy

6. Understanding go.mod

Your file might conceptually look like:

module example.com/go-mod-demo

go 1.xx

require github.com/google/uuid v1.6.0

Let’s decode it.

module

module example.com/go-mod-demo

This is the identity/path of your module.

Think:

module = my project's module name

go

go 1.xx

This records the Go language/module compatibility version associated with the module.

For now, let the Go tools manage it.


require

require github.com/google/uuid v1.6.0

Means:

My project requires

github.com/google/uuid

at selected version

v1.6.0

7. Module vs Package

This distinction confuses many Go beginners.

They are not the same thing.

ModulePackage
Collection of one or more related Go packagesGo source code in a directory
Has a go.mod at its rootDeclared with package xyz
Used for dependency/version managementUsed for organizing and importing Go code
Example: github.com/google/uuid moduleExample: the uuid package you import

Simple mental model:

Module
│
├── Package A
├── Package B
└── Package C

A dependency is normally managed at the module level, even though your code imports packages.


8. go.mod vs go.sum

go.modgo.sum
Describes your moduleContains dependency verification hashes
Records required module versionsHelps verify downloaded module content
Human-readable dependency configurationMostly maintained automatically
Important to commit to GitNormally committed to Git too

Think:

go.mod
"What dependencies should I use?"

go.sum
"Can I verify the dependency content I downloaded?"

9. Important commands compared

CommandPurposeWhen you use it
go mod initCreate a new moduleAt project creation
go get package@versionAdd/change a dependencyWhen adding or changing dependencies
go mod tidySynchronize dependency files with source codeAfter dependency/import changes
go run .Compile and run the current packageDuring development
go buildCompile your projectWhen you want a binary/build check

The three module commands worth memorizing first are:

go mod init
go get
go mod tidy

Everything else can come later.


10. What happens if you remove the dependency?

Suppose you change main.go to:

package main

import "fmt"

func main() {
	fmt.Println("Hello")
}

There is no longer:

import "github.com/google/uuid"

Run:

go mod tidy

Go sees:

Source code
    |
    X  no UUID import
    |
go.mod

and can remove the now-unneeded UUID module requirement. This source-driven synchronization is exactly what go mod tidy is designed to do. (Go.dev)


11. Direct vs indirect dependencies

You may eventually see:

require (
    github.com/google/uuid v1.6.0
    example.com/something v1.2.3 // indirect
)

Don’t let this confuse you.

Direct

Your code directly imports it:

import "github.com/google/uuid"
YOUR PROGRAM
     ↓
    UUID

Indirect

Your dependency needs another module:

YOUR PROGRAM
     ↓
Dependency A
     ↓
Dependency B

Dependency B can appear as:

// indirect

An indirect requirement indicates that the main module does not directly import a package from that module, though it is needed through the dependency graph. (Go.dev)

For now:

Don’t manually manage // indirect. Let Go do it.


12. One golden rule

Don’t think:

“I need to manually maintain a big dependency file.”

Instead think:

Write imports
      ↓
Go examines your code
      ↓
Go manages module requirements
      ↓
go.mod + go.sum

Use the Go commands instead of manually editing dependency information unless you have a specific reason to do otherwise. The official Go documentation also recommends managing dependencies through Go commands so that go.mod stays consistent. (Go.dev)


13. Your everyday workflow

For a new project:

mkdir myapp
cd myapp

go mod init example.com/myapp

Write code.

If you need a dependency:

go get dependency@version

Then:

go run .

And periodically:

go mod tidy

That’s enough for learning Go Modules initially.


14. Final cheat sheet

You want to…Use
Start dependency managementgo mod init
Add/change a dependencygo get
Clean dependency informationgo mod tidy
See your dependenciesOpen go.mod
Understand integrity informationLook at go.sum
Use a dependency in codeimport "..."
Run the programgo run .

Remember this sentence

main.go uses packages → go.mod tracks required modules/versions → go.sum helps verify downloaded dependency content → go mod tidy keeps everything synchronized.

If that sentence makes sense, you understand the core of dependency management using Go Modules. (Go.dev)

Related Posts

Go Tutorials: Go Methods

Go methods become much easier once you understand one idea: A method is simply a function attached to a type. And the important rule is: A receiver…

Read More

Go Tutorials: Modules, Packages, Subpackages, Submodules, and Workspaces

This tutorial gives you one complete mental model for: The most important idea is: And sometimes: These two designs are very different. 1. What is a Go…

Read More

Go Tutorials: Numeric Types Beginner Tutorial

Go has four main families of numeric types: The simplest way to remember them is: Type family Stores Example int Whole numbers, positive or negative -10, 0,…

Read More

Go Tutorials: Go Testing, Benchmarking, and Profiling

These three topics become much easier once you separate the questions they answer: Topic Main question Main Go tool Result Testing Does my code work correctly? go…

Read More

Go Tutorials: Generics

1. What are Generics? Generics let you write one piece of code that works with multiple types while keeping Go’s compile-time type safety. Suppose you want a…

Read More

Go Concurrency Management Made Simple

This tutorial covers: The goal is: One concept → one purpose → one complete runnable example. Every example is independent. Save any example as: and run: 0….

Read More