Modules & Packages (go mod)

Reviewed & published by Brayan K

Real Go programs span many files and pull in third-party libraries. This lesson covers how Go organizes code: packages (folders of related code) and modules (versioned collections managed by go mod). By the end you'll structure a project, export names across packages, and add dependencies.

Part of the free Go course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

📊 Modules vs Packages

ConceptIsDefined by
packageA folder of .go files sharing a namepackage X at top of file
moduleA versioned group of packagesgo.mod file
import pathHow others refer to your packagemodule path + folder

One module contains one or more packages. The package you run lives in package main and has a func main(); everything else is a library package imported by others.

1️⃣ Packages & Exported Names

Every Go file begins with package X, and all files in one folder must share that name. The rule for visibility is delightfully simple: a name starting with a capital letter is exported (visible to other packages); a lowercase name is private to its package. No public/private keywords — just capitalization.

// File: greeting/greeting.go
package greeting   // the package name — the folder is its home

import "fmt"

// Exported: capitalized name -> visible to other packages.
func Hello(name string) string {
    return fmt.Sprintf("Hello, %s! %s", name, secret())
}

// Unexported: lowercase name -> private to THIS package only.
func secret() string {
    return "(from the greeting package)"
}

Another package imports it by its import path (module path + folder) and calls only its exported names.

// File: main.go  (in the module root)
package main

import (
    "fmt"

    "example.com/myapp/greeting"  // module path + folder
)

func main() {
    // We can call Hello (exported) but NOT secret (unexported).
    fmt.Println(greeting.Hello("Alice"))
}

Your turn. Rename the function so it can be called from outside its package.

2️⃣ The go.mod File

A module is declared by a go.mod file at the project root. You create it once with go mod init <path>. It records the module's import path, the minimum Go version, and every dependency with its exact version — so anyone who clones your project gets the same builds.

// File: go.mod — created by "go mod init example.com/myapp"
module example.com/myapp   // the module's import path

go 1.22                    // minimum Go version

require (
    github.com/google/uuid v1.6.0   // a dependency, added by "go get"
)

3️⃣ The go Command Workflow

Day to day you'll use a handful of commands. go mod init starts a module, go get adds a dependency, go mod tidy syncs go.mod with what your code actually imports, and go run . builds and runs the current package.

# Start a new module (run once, in your project folder)
$ go mod init example.com/myapp
go: creating new go.mod: module example.com/myapp

# Add a dependency — downloads it and records it in go.mod
$ go get github.com/google/uuid
go: added github.com/google/uuid v1.6.0

# Tidy: add anything missing, remove anything unused
$ go mod tidy

# Build and run
$ go run .
Hello, Alice! (from the greeting package)

4️⃣ Using a Third-Party Package

Once a dependency is in go.mod, importing and using it is no different from the standard library — Go fetches it from the source repository and caches it. Below we generate a UUID with a popular library after a single go get.

// File: main.go
package main

import (
    "fmt"

    "github.com/google/uuid"   // a third-party module
)

func main() {
    // After "go get github.com/google/uuid", this just works.
    id := uuid.New()
    fmt.Println("generated id:", id.String())
    // e.g. generated id: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
}

🧩 Reorder Challenge

These are the steps to start a project and run it with a dependency. Put them in the correct order:

A. go run .
B. go mod init example.com/app
C. write main.go that imports the package
D. go get github.com/google/uuid
E. go mod tidy

B, D, C, E, A. Initialize the module first (it must exist before anything else), add the dependency, write the code that uses it, run go mod tidy to reconcile go.mod, then run. In practice go get and writing code interleave, but init is always first and run last.

🧠 Quick Recall

1. Can package main call this function?

package util
func format() string { return "x" }

No. format is lowercase, so it's unexported — visible only inside package util. Rename it Format to call it from another package.

2. Which command creates a go.mod?

$ go ____ example.com/myapp

go mod init example.com/myapp — this writes a go.mod declaring the module path. Run it once at the project root.

3. What does go mod tidy do?

$ go mod tidy

It adds any imports your code uses but go.mod is missing, and removes dependencies no longer imported. It keeps go.mod/go.sum in sync with the actual code.

Pro Tips

Common Errors (and the fix)

Frequently Asked Questions

Q: What's the difference between a package and a module?

A package is a single folder of related code. A module is a versioned bundle of one or more packages with a go.mod file and an import path. You import packages; you version and distribute modules.

Q: How does Go know what's public?

Purely by capitalization. A capitalized identifier (function, type, field, constant) is exported and visible to importers; a lowercase one is package-private. This applies to struct fields too — which is why JSON only sees capitalized fields.

Q: Do I need to be online to build?

Only the first time you fetch a dependency. Go caches downloaded modules locally, so subsequent builds work offline. go.sum verifies that cached copies haven't changed.

Q: How do I upgrade a dependency?

Run go get example.com/dep@latest (or a specific version like @v1.5.0), then go mod tidy. Go updates go.mod and go.sum to the chosen version.

Mini-Challenge: Structure a Project

No blanks — a design brief. Sketch the files and commands for a small two-package project, deciding which names must be exported. This mirrors how every real Go repository is laid out.

// 🎯 MINI-CHALLENGE: structure a tiny project
//
// Lay out a project that:
//   1. Has a module path "example.com/shop" (which command creates this?).
//   2. Contains a package "cart" in a folder cart/ with an EXPORTED
//      function Total(prices []float64) float64 that sums the slice.
//   3. Has a main package in the root that imports "example.com/shop/cart"
//      and prints cart.Total([]float64{1.5, 2.5, 6}) -> 10.
//
// Sketch the files (go.mod, cart/cart.go, main.go) and the commands.
// Which names must be capitalized, and why?
//
// ✅ Running "go run ." should print:  total: 10

🎉 Lesson Complete!

Practice quiz

What is a Go package?

  • A single file
  • A folder of files sharing one package name
  • A compiled binary
  • A remote server

Answer: A folder of files sharing one package name. A package is a directory of source files that all declare the same package name.

How is a name made exported (visible outside its package)?

  • A keyword 'export'
  • A capitalized first letter
  • An export tag
  • Being in main

Answer: A capitalized first letter. Capitalized identifiers are exported; lowercase ones are package-private.

What file declares a Go module?

  • package.json
  • go.mod
  • module.go
  • Gopkg.toml

Answer: go.mod. go.mod declares the module path and its dependencies.

Which command creates a new module?

  • go new
  • go mod init
  • go create
  • go module

Answer: go mod init. go mod init <path> creates the go.mod file for a new module.

Which command adds a dependency to your module?

  • go add
  • go install
  • go get
  • go fetch

Answer: go get. go get adds (or upgrades) a dependency and records it in go.mod.

Which command removes unused dependencies and adds missing ones?

  • go clean
  • go mod tidy
  • go fix
  • go vet

Answer: go mod tidy. go mod tidy reconciles go.mod/go.sum with the imports your code actually uses.

What is the purpose of go.sum?

  • Lists TODOs
  • Pins and verifies dependency checksums
  • Stores secrets
  • Configures the editor

Answer: Pins and verifies dependency checksums. go.sum records cryptographic checksums so builds are reproducible and verifiable.

How do you import one of your own packages?

  • By file path
  • By module path plus the package folder
  • By package name only
  • You cannot

Answer: By module path plus the package folder. Internal packages are imported using the module path joined with the subfolder.

Which package name marks an executable program?

  • package app
  • package main
  • package exe
  • package run

Answer: package main. package main (with func main) builds an executable; other names build libraries.

Can a lowercase function from another package be called directly?

  • Yes, always
  • No, it is unexported and package-private
  • Only in tests
  • Only with reflection

Answer: No, it is unexported and package-private. Lowercase names are unexported, so they are not accessible from outside their package.

Continue this course