LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 3119 min read

Packages & Modules

Learn how Go organizes code into packages, how go mod init creates a module, what go.mod and go.sum do, and how to import your own packages.

Introduction

Every Go file belongs to a package, and every real Go project belongs to a module. Packages are how Go organizes code into reusable units; modules are how Go tracks a project's dependencies and versions. Understanding both is essential the moment your program grows past a single main.go file.

What You Will Learn
  • What a package is and how the package keyword works.
  • How to turn a directory into a module with go mod init.
  • What go.mod and go.sum actually record.
  • How to create and structure your own package.
  • How to import and use a package you wrote yourself.

What Is a Package?

A package is simply a directory of .go files that all declare the same package name at the top of the file. Every executable program has exactly one package main with a func main() as its entry point. Everything else — the standard library's fmt, strings, and net/http, plus any code you write yourself — lives in a package that can be imported.

package main
import "fmt"
func main() {
fmt.Println("every Go file starts with a package declaration")
}

Identifiers (functions, types, variables) that start with an uppercase letter are exported — visible to other packages that import this one. Lowercase identifiers stay private to the package. This is Go's entire visibility system: no public/private keywords, just capitalization.

Creating a Module with go mod init

A module is a collection of packages that are versioned and released together, described by a go.mod file at the root of the project. You create one with go mod init, passing a module path — typically the repository URL where the code lives.

mkdir mytool
cd mytool
go mod init github.com/yourname/mytool
Output

Click Run to see what this code prints.

From this point on, go build, go run, go test, and go get all understand the project as a single module and use the module path as the import prefix for any packages inside it.

Understanding go.mod and go.sum

go.mod declares the module's own path, the Go version it targets, and any external dependencies with their required versions. go.sum records cryptographic checksums for every dependency (and its transitive dependencies) so builds are reproducible and tamper-evident.

go.mod
module github.com/yourname/mytool
go 1.22
require (
github.com/google/uuid v1.6.0
)
FilePurpose
go.modDeclares the module path, Go version, and direct/indirect dependency versions.
go.sumStores checksums of every dependency so the same code is fetched every time.
Commit Both Files

Always commit go.mod and go.sum to version control. Together they guarantee that anyone building your project gets exactly the same dependency code you did.

Creating Your Own Package

To create a reusable package, put its .go files in their own subdirectory and give them a package name matching that directory (by convention, not strict requirement). For example, a small math helper package might live at mathutil/mathutil.go.

mathutil/mathutil.go
package mathutil
// Square returns n multiplied by itself.
func Square(n int) int {
return n * n
}
// isEven is unexported and only usable within this package.
func isEven(n int) bool {
return n%2 == 0
}

Square is exported (capital S) so other packages can call it. isEven is unexported and stays private to mathutil.

Importing Your Own Packages

To use mathutil from main.go, import it using the module path plus the subdirectory path, exactly as declared in go.mod.

main.go
package main
import (
"fmt"
"github.com/yourname/mytool/mathutil"
)
func main() {
fmt.Println(mathutil.Square(6))
}
Output

Click Run to see what this code prints.

Because mytool's go.mod declares its module path as github.com/yourname/mytool, the mathutil subdirectory is automatically importable as github.com/yourname/mytool/mathutil — no extra registration step needed.

Common Mistakes

Avoid These Mistakes
  • Forgetting to run go mod init before adding imports, causing "no required module" errors.
  • Mixing multiple package names in a single directory, which Go will refuse to compile.
  • Manually editing go.sum instead of letting go mod tidy manage it.
  • Naming a package the same as a common standard-library package, causing import confusion.
  • Committing go.mod but forgetting go.sum (or vice versa).

Best Practices

  • Use short, lowercase, no-underscore package names, matching the directory name.
  • Run go mod tidy periodically to keep go.mod and go.sum accurate and minimal.
  • Group related functionality into its own package instead of dumping everything into package main.
  • Keep exported APIs small and intentional — export only what callers actually need.
  • Document exported identifiers with a comment starting with their name, e.g. "// Square returns...".

Frequently Asked Questions

For a single throwaway file you can run it directly with go run file.go without a module. For anything with imports beyond the standard library, or that you plan to build/share, initialize a module.

No, every .go file in the same directory must declare the same package name (test files ending in _test.go are a minor exception, allowed to use a "_test" suffix package).

A package is a single directory of Go source files. A module is one or more packages versioned and distributed together, described by a single go.mod file.

Key Takeaways

  • Every Go file belongs to a package declared at the top of the file.
  • Capitalized identifiers are exported; lowercase identifiers are private to the package.
  • go mod init creates a go.mod file that turns a directory into a module.
  • go.sum records checksums so dependency downloads are verifiable and reproducible.
  • Your own packages are imported using the module path plus their subdirectory.

Summary

Packages let you organize Go code into focused, reusable units, while modules track versions and dependencies for an entire project. With go mod init, a clear package layout, and disciplined use of exported names, your codebase stays easy to navigate as it grows. Next, you will start Go's concurrency story with goroutines.

Next Lesson →

Goroutines