Packages and Imports in Go

How Go packages map to folders, how import paths are built from the module path, import aliases, blank imports, and the imported and not used error.

Every Go file starts with a package line, and every folder of Go files is one package. Packages are how Go splits code into pieces with clear boundaries. Understanding the folder-to-package rule and how import paths are formed removes most of the confusion people hit when their project grows past one file.

One Folder, One Package

All .go files in the same folder must have the same package name (test files may add a _test suffix). Files in the same package see each other's names directly, with no import needed.

shop/
  go.mod          module example.com/shop
  main.go         package main
  cart.go         package main
  price/
    price.go      package price
    tax.go        package price

main.go and cart.go share package main. The price folder is a separate package. Putting package main in one file of price/ and package price in another fails with found packages price (price.go) and main (x.go) in /path/to/shop/price.

Folders are packages, not just organization:

Creating a new folder creates a new package, with its own import path and visibility rules. Do not add folders only to tidy up files. Add one when you want a separate package with its own API.

Import Paths Come From the Module Path

The go.mod file at the root declares the module path, for example module example.com/shop. A package's import path is the module path plus the folder:

folder shop/price        -> import "example.com/shop/price"
folder shop/internal/db  -> import "example.com/shop/internal/db"
// file: shop/main.go
package main

import (
	"fmt"

	"example.com/shop/price"
)

func main() {
	fmt.Println(price.WithTax(100))
}

You refer to the package by its package name, the last part of the path by convention, followed by a dot: price.WithTax. Standard library packages have short paths like "fmt" and "net/http", and you use them as fmt and http.

Relative imports do not work:

import "./price" is not allowed in module mode and fails with "./price" is relative, but relative import paths are not supported in module mode. Always import with the full path starting from the module path.

Unused Imports Are Errors

Go refuses to compile a file that imports something it does not use:

./main.go:5:2: "os" imported and not used

This keeps dependencies honest. Most editors with gopls add and remove imports automatically when you save, using goimports. If you need an import temporarily while debugging, use it or remove it, there is no warning-only mode.

Import Aliases

When two packages have the same name, or a name is long, give one an alias:

package main

import (
	"fmt"
	crand "crypto/rand"
	mrand "math/rand/v2"
)

func main() {
	b := make([]byte, 4)
	crand.Read(b)
	fmt.Println(len(b), mrand.IntN(10) < 10)
}

This prints 4 true. Both packages are named rand, so the aliases crand and mrand keep them apart. Use aliases only when needed, since they make code harder to search.

Blank Imports for Side Effects

An import with _ as its name runs the package's init functions without giving you access to its names:

import _ "github.com/lib/pq" // registers the "postgres" database driver
import _ "image/png"         // lets image.Decode read PNG files
import _ "net/http/pprof"    // adds /debug/pprof handlers

The package registers itself with another package when it loads. This is the answer to the common question "what does an underscore before an import mean".

Import cycles are not allowed:

If package a imports b and b imports a, the build fails with import cycle not allowed. Fix it by moving the shared code into a third package both can import, or by defining a small interface in the package that needs it.

Your imports are set up right:

Every import starts with either a standard library name or your module path, the editor manages the import block, and go build ./... builds every package in the module without errors.

Summary

Each folder is one package, and all its files share the same package name. Import paths are the module path from go.mod plus the folder, and you use the package by its name with a dot. Unused imports and import cycles are compile errors, aliases resolve name clashes, and blank imports run a package's init code for side effects such as registering drivers.