Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Go  Module 9 – Packages, modules and dependencies

Packages, exported names and initialisation

Split Go code into packages, export names with a capital letter, keep a package private with internal/, break import cycles and predict init order.

  • Intermediate
  • 15 minutes
  • Examples run with Go 1.27.1 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Split a program into packages and choose what each one exports with capital letters
  • Limit which packages may import a package by placing it under internal/
  • Resolve an import cycle with a small interface or a third package
  • Predict the order in which package variables, init functions and main run
  • Name packages for what they provide instead of util or common

Before you start

What you’ll learn · Before you start

What you will learn

  • Split a program into packages and choose what each one exports with capital letters
  • Limit which packages may import a package by placing it under internal/
  • Resolve an import cycle with a small interface or a third package
  • Predict the order in which package variables, init functions and main run
  • Name packages for what they provide instead of util or common

Before you start

On this page

In the kirana module of this lesson, pricing.Total(bill) works from another package and pricing.tax(4800) stops the build, and the only difference between them is the case of the first letter. Go has no public or private keyword: folders make packages, a capital letter exports a name, and two more rules decide who may import whom, the go command’s rule for folders named internal and the specification’s ban on packages that import each other.

You will split a program into packages, decide what each one exports, keep a package inside your own tree, break an import cycle, and predict the order in which a program’s packages start before main runs. Every message quoted here was printed by Go 1.27.1.

A package is one folder of files that share a name

The kirana module bills a customer of a kirana, a neighbourhood grocery shop in India, in paise. Its go.mod declares the module path example.com/kirana, and each folder below it that holds .go files is one package. A package is the set of .go files in one folder, compiled together: every file starts with the same package clause, and anything declared at the top level of one file, a function, a type, a variable or a constant, can be used by the other files of that folder without an import. Here are two of the packages:

Two packages of the kirana module

Go (pricing.go) · kirana/pricing/pricing.go

// Package pricing works out what a customer pays, in paise.
package pricing

// Line is one line of a bill: what was bought, and how many.
type Line struct {
	Name       string
	Qty        int
	pricePaise int64 // the price of one item, set only by NewLine
}

// NewLine makes a bill line for qty items at pricePaise each.
func NewLine(name string, qty int, pricePaise int64) Line {
	return Line{Name: name, Qty: qty, pricePaise: pricePaise}
}

// Total adds up the lines and the tax on them, in paise.
func Total(lines []Line) int64 {
	var sum int64
	for _, l := range lines {
		sum += l.pricePaise * int64(l.Qty)
	}
	return sum + tax(sum)
}

// tax is 5 per cent of amount in this example, rounded to the nearest paisa.
func tax(amount int64) int64 {
	return (amount*5 + 50) / 100
}

Go (main.go) · kirana/cmd/billing/main.go

// Command billing prints one customer's bill.
package main

import (
	"fmt"

	"example.com/kirana/internal/ledger"
	"example.com/kirana/pricing"
)

func main() {
	bill := []pricing.Line{
		pricing.NewLine("rice 1 kg", 2, 6400),
		pricing.NewLine("toor dal 500 g", 1, 8950),
	}
	total := pricing.Total(bill)
	ledger.Record(total)
	fmt.Printf("total: Rs %d.%02d\n", total/100, total%100)
	fmt.Println(ledger.Summary())
}

The program in cmd/billing says package main; the other folders say the name their code is used by, such as package pricing. A package’s import path: The string an import declaration names, such as example.com/kirana/pricing: the module path followed by the package's folder in the module. Standard-library packages, such as fmt, have no module path in front. is the module path: The name on the module line of a go.mod file, such as example.com/kirana. It begins the import path of every package of the module and tells the go command where the module can be downloaded. joined with its folder, and go list ./... prints the import path of every package in the module:

The packages of kirana, and the program that uses two of them Python · layout_session.py
from session import run

# The packages of the kirana module and their import paths, then the program that uses two of them.
run("""
cat kirana/go.mod
cd kirana && go list ./...
cd kirana && go run ./cmd/billing
""")

Output

$ cat kirana/go.mod
module example.com/kirana

go 1.27
$ cd kirana && go list ./...
example.com/kirana/cmd/billing
example.com/kirana/cmd/peek
example.com/kirana/internal/ledger
example.com/kirana/pricing
$ cd kirana && go run ./cmd/billing
total: Rs 228.38
takings so far: Rs 228.38

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 layout_session.py

The four paths are the four folders with example.com/kirana/ in front, and go run built the program from three of them and printed a bill of Rs 228.38. Step through the build of cmd/billing and watch each import line turn into a folder:

Interactive · step by step From an import path to a folder Step through the build of cmd/billing and watch each import line turn into a folder of the kirana module.
From an import path to a folder, step 6 of 6. Showing go.mod, cmd/billing (prints total: Rs 228.38), pricing, internal/ledger, fmt.importsimportsimportsgo.modmodule example.com/kiranacmd/billingprints total: Rs 228.38pricingpackage pricinginternal/ledgerpackage ledgerfmtstandard library

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. The module root. go.mod says module example.com/kirana. Every folder below it that holds .go files is one package, and that package's import path is the module path followed by the folder.
  2. The program. cmd/billing holds the files that say package main, so its import path is example.com/kirana/cmd/billing, and building it makes a program.
  3. An import is a path. billing imports example.com/kirana/pricing. The path starts with the module path, so the go command looks in the folder that follows it, pricing, and compiles every .go file there together.
  4. A deeper folder. example.com/kirana/internal/ledger works the same way and becomes the folder internal/ledger. Its last element, ledger, is also the name the code uses, as in ledger.Record.
  5. No dot, no module. fmt has no dot in its first element, so it is a standard-library package from the Go installation, never a folder of this module.
  6. Built. Each package is compiled once, with all its files, before the packages that import it. Then the program is linked and runs, and prints the bill of Rs 228.38.

By convention a package is named after the last element of its import path, so example.com/kirana/internal/ledger is used as ledger.Record. Only main breaks the convention, because the program needs that name.

Checkpoint

Check: A package is one folder

  1. Question 1 of 2 The module example.com/kirana has a folder internal/ledger whose files say package ledger. What does cmd/billing write to import it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: import "example.com/kirana/internal/ledger"

    The go command finds a package from its import path: the module path from go.mod, example.com/kirana, then the folder, internal/ledger. The last element, ledger, is the name used in code.

  2. Question 2 of 2 You add round.go to the pricing folder. It says package pricing and declares func round(p int64) int64. Can Total, in pricing.go, call round?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Yes, without any import

    A package is the set of .go files in one folder with the same package clause, compiled together, so unexported names are shared by all of its files.

A capital first letter exports a name, and nothing else does

cmd/peek is a second program in the same module that reaches for two names pricing keeps to itself, the field pricePaise and the function tax. It does not compile:

Two unexported names used from another package Python · exports_session.py
from session import run

# cmd/peek reaches for an unexported field and an unexported function of package pricing.
run(
    """
cd kirana && go build ./cmd/peek
""",
    expect_failure=["cd kirana && go build ./cmd/peek"],
)

Output

$ cd kirana && go build ./cmd/peek
# example.com/kirana/cmd/peek
cmd/peek/main.go:12:48: cannot refer to unexported field pricePaise in struct literal of type pricing.Line
cmd/peek/main.go:13:22: name tax not exported by package pricing
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 exports_session.py

The compiler names both mistakes: cannot refer to unexported field pricePaise in struct literal of type pricing.Line, and name tax not exported by package pricing. The rule is short. A name is exported name: A name that other packages can use. A Go name is exported when its first character is an upper-case letter and it is declared at the package level, or when it names a field or a method. when its first character is an upper-case letter, Unicode category Lu, and it is declared at the top level of the package, or is the name of a field or a method. Line, NewLine, Total, Name and Qty pass both tests; tax and pricePaise fail the first, so they are visible in every file of the pricing folder and in no other package.

Key idea

A name is exported when it starts with an upper-case letter and is declared at the package level, or names a field or method; everything else is shared only by the files of its own folder.

The unexported field is deliberate. Other packages can read and set Name and Qty, but only code in pricing, such as NewLine, can read or set the price, so pricing decides how a price gets into a line, and it can change how it stores prices without breaking any caller.

Exporting a helper to silence the error

Renaming tax to Tax makes the build pass and turns a private detail into a promise: every caller can now depend on how the tax is computed, and you can no longer change it freely. Ask what the caller needs instead. Here it needs a bill’s total, which Total already gives.

Checkpoint

Check: Exported and unexported names

  1. Question 1 of 2 Which names of package pricing can cmd/peek, a package of the same module, use?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • NewLine
    • Line
    • The field Qty of a Line

    Being in the same module gives no access: the only test is the first letter. Line, NewLine, Total, Name and Qty are exported; tax and pricePaise are not.

  2. Question 2 of 2 Another package needs a bill line for one sugar packet at 4800 paise. What should it write, given pricing as it is?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: pricing.NewLine("sugar 1 kg", 1, 4800)

    An unexported field with an exported constructor lets a package decide how a value is built. Callers use what the package offers rather than reaching inside it.

internal/ keeps a package inside your own tree

An exported name can be used by any code that imports its package, even code in another module. Sometimes you want a package that every part of your module may share and nobody else may use, such as kirana’s ledger, which keeps the day’s takings. The go command has a rule for that, and no keyword is involved: an internal package: A package in or below a folder named internal. The go command lets only code whose import path starts with the path of the folder above internal import it., one in or below a folder named internal, can be imported only by code whose import path starts with the path of the folder above internal.

rival is a second module next to kirana. Its go.mod points example.com/kirana at the folder beside it with a replace line, so the build reads kirana from disk, and its main.go imports the ledger:

Another module imports kirana's internal package Python · internal_session.py
from session import run

# rival is another module. Its go.mod points example.com/kirana at the folder next to it, and its main.go imports
# kirana's internal package.
run(
    """
cat rival/go.mod
cd rival && go build .
""",
    expect_failure=["cd rival && go build ."],
)

Output

$ cat rival/go.mod
module example.com/rival

go 1.27

require example.com/kirana v0.0.0

replace example.com/kirana => ../kirana
$ cd rival && go build .
package example.com/rival
	main.go:7:2: use of internal package example.com/kirana/internal/ledger not allowed
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 internal_session.py

The build stops at the import: use of internal package example.com/kirana/internal/ledger not allowed. The ledger’s parent folder is the module root, example.com/kirana, and example.com/rival does not start with it. Step through three packages that import the ledger:

Interactive · step by step Who may import internal/ledger Step through three packages that import example.com/kirana/internal/ledger and see which the go command allows.
Who may import internal/ledger, step 5 of 5. Showing cmd/billing, pricing, internal/ledger, example.com/rival.allowedallowedrefusedcmd/billingin example.com/kiranapricingin example.com/kiranainternal/ledgerparent: example.com/kiranaexample.com/rivalanother module

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The parent path. ledger sits in internal/, whose parent is the module root, example.com/kirana. Only code whose import path starts with that parent may import it.
  2. Inside the tree. cmd/billing's path, example.com/kirana/cmd/billing, starts with example.com/kirana, so its import of ledger is allowed, and the bill builds.
  3. A sibling package. pricing could import ledger too, for the same reason. Any package of the module may; ledger's exported names are shared inside the tree and nowhere else.
  4. Outside the tree. example.com/rival does not start with example.com/kirana, so the go command stops the build with use of internal package example.com/kirana/internal/ledger not allowed.
  5. Paths, not modules. The rule compares import paths. A folder kirana/cmd/billing/internal/fmtx would be importable only by cmd/billing and the packages below it, not by pricing.

This makes internal the place for code you share inside a module but do not want to support for others: other modules cannot import it, so you can rename, split or delete it without breaking anyone outside your tree.

Checkpoint

Check: The internal/ rule

  1. One question The module example.com/shop has a package in billing/internal/tax. Which packages may import it?

    Choose every answer that is right.

    Show the answer: One question

    Answer:

    • example.com/shop/billing
    • example.com/shop/billing/report

    The rule compares import paths, not modules: only code whose path starts with the path of the folder above internal may import it. For kirana's ledger that folder was the module root.

An import cycle does not compile, so make the imports go one way

In the cafe module, Order’s Receipt method calls invoice.Render, and Render takes an orders.Order, so orders imports invoice and invoice imports orders, an import cycle: Packages that import one another, directly or through other packages. Go refuses to build them, so the imports of a program always form a graph without loops.. The specification forbids that: a package may not import itself, directly or indirectly. cyclefix is the same module with one change, an interface declared in invoice:

cyclefix: invoice asks for an interface, not for orders

Go (invoice.go) · cyclefix/invoice/invoice.go

// Package invoice formats receipts for anything that can be billed.
package invoice

import (
	"fmt"
	"strings"
)

// Billable is what invoice needs to know about an order, and no more.
type Billable interface {
	Lines() []string
	TotalPaise() int64
}

// Render formats a bill as one line.
func Render(b Billable) string {
	p := b.TotalPaise()
	return fmt.Sprintf("%s: Rs %d.%02d", strings.Join(b.Lines(), ", "), p/100, p%100)
}

Go (orders.go) · cyclefix/orders/orders.go

// Package orders holds what a customer ordered.
package orders

import "example.com/cafe/invoice"

// Order is one customer's order.
type Order struct {
	Items []string
	Paise int64
}

// Lines and TotalPaise make Order an invoice.Billable.
func (o Order) Lines() []string   { return o.Items }
func (o Order) TotalPaise() int64 { return o.Paise }

// Receipt formats the order as a receipt.
func (o Order) Receipt() string { return invoice.Render(o) }

The session builds the module with the cycle, then runs the fixed one and lists what each package imports:

The cycle, the fix, and the imports that remain Python · cycle_session.py
from session import run

# orders and invoice import each other in cycle/; in cyclefix/ invoice declares the interface it needs and imports
# nothing of the module, so the imports go one way.
run(
    """
cd cycle && go build ./...
cd cyclefix && go run ./cmd/cafe
cd cyclefix && go list -f '{{.ImportPath}} imports {{join .Imports ", "}}' ./...
""",
    expect_failure=["cd cycle && go build ./..."],
)

Output

$ cd cycle && go build ./...
package example.com/cafe/invoice
	imports example.com/cafe/orders from invoice.go
	imports example.com/cafe/invoice from orders.go: import cycle not allowed
[exit status 1]
$ cd cyclefix && go run ./cmd/cafe
masala dosa, filter coffee: Rs 140.00
$ cd cyclefix && go list -f '{{.ImportPath}} imports {{join .Imports ", "}}' ./...
example.com/cafe/cmd/cafe imports example.com/cafe/orders, fmt
example.com/cafe/invoice imports fmt, strings
example.com/cafe/orders imports example.com/cafe/invoice

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 cycle_session.py

The first build failed with the whole loop spelled out, imports example.com/cafe/orders from invoice.go, then imports example.com/cafe/invoice from orders.go: import cycle not allowed. After the fix, invoice imports only fmt and strings, and the receipt printed. Step through the change:

Interactive · step by step Two packages that import each other, and the fix Step through the import that closes the cycle, then the interface that lets the imports go one way.
Two packages that import each other, and the fix, step 6 of 6. Showing orders (has Lines and TotalPaise), invoice (Render(b Billable)), cmd/cafe.Receipt calls Renderimportsordershas Lines and TotalPaiseinvoiceRender(b Billable)cmd/cafepackage main

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. One direction. orders imports invoice, because Order's Receipt method calls invoice.Render to format the receipt.
  2. And back. invoice imports orders, because Render takes an orders.Order. Now each package needs the other to be compiled first.
  3. The build stops. go build names the loop, invoice imports orders imports invoice, and compiles neither. Go has no way to build half of a cycle.
  4. Ask for less. In cyclefix, invoice declares the interface Billable, two methods it needs, Lines and TotalPaise, and Render takes a Billable. invoice now imports nothing of the module.
  5. Satisfied without a word. Order already has Lines and TotalPaise, so it is a Billable without saying so, and Receipt can pass itself to Render.
  6. One way only. cmd/cafe imports orders, orders imports invoice, and nothing points back. The program builds and prints masala dosa, filter coffee for Rs 140.00.

There are two dependable fixes. The one shown asks for less: the package that needs something declares a small interface with only the methods it uses, and any type with those methods satisfies it without importing anything. The other moves what both packages share, here the Order type, into a third package that both import and that imports neither. Choose the interface when one package only calls a few methods of the other, and the third package when both really work on the same data.

Checkpoint

Check: Breaking an import cycle

  1. One question Package mail imports users to read users.User. Now users must send one welcome mail through a mail.Mailer, whose method is Send(to, body string) error. What keeps the imports one way?

    Choose one answer.

    Show the answer: One question

    Answer: users declares type Sender interface { Send(to, body string) error } and is given a mail.Mailer as a Sender by main

    The package that needs a capability declares a small interface with just the methods it calls; the concrete type lives elsewhere and is passed in. A third package holding shared data is the other dependable fix.

Variables first, then init, package by package, before main

Before main runs, Go initialises every package of the program. Inside one package the package-level variable: A variable declared outside every function. It exists for the whole run, and Go gives it its value before main starts, setting each one after the variables its value depends on. More in “Variables, short declarations and zero values” come first, then the init function: A function named init, with no parameters and no results, declared at the package level. It runs once, after the package's variables have their values, and no code can call it.. The variables of this program are declared in the order total, price, qty:

initvars.go: the order in which one package starts Go · initvars.go
// initvars.go: the order in which one package starts.
package main

import "fmt"

var total = show("total", price*qty)
var price = show("price", 4800)
var qty = show("qty", 3)

func show(name string, v int) int {
	fmt.Println("variable", name, "=", v)
	return v
}

func init() { fmt.Println("first init: total is", total) }

func init() { fmt.Println("second init") }

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

Output

variable price = 4800
variable qty = 3
variable total = 14400
first init: total is 14400
second init
main

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run initvars.go

It set price, then qty, then total, 14400. The specification’s rule is to initialise, step by step, the variable that comes first in declaration order among those whose values no longer depend on an uninitialised variable. total depends on price and qty, so it waits for both. Then the two init functions ran in the order they are written, and main last. When a package has several files, the specification encourages build tools to hand them to the compiler in file-name order, so that the order of declarations across files is the same on every build.

Across packages, a package starts only after every package it imports. The shopinit module has three: cart imports prices, greeting and prices import nothing of the module, and main imports cart and greeting:

The imports of shopinit, then the order in which its packages start Python · startup_session.py
from session import run

# Which package imports which, then the order in which they start when the program runs.
run("""
cd initorder && go list -f '{{.ImportPath}}: {{join .Imports " "}}' ./...
cd initorder && go run .
""")

Output

$ cd initorder && go list -f '{{.ImportPath}}: {{join .Imports " "}}' ./...
example.com/shopinit: example.com/shopinit/cart example.com/shopinit/greeting fmt
example.com/shopinit/cart: example.com/shopinit/prices fmt
example.com/shopinit/greeting: fmt
example.com/shopinit/prices: fmt
$ cd initorder && go run .
greeting: variable Text
greeting: init
prices: variable List
prices: init
cart: variable Items, sees 2 prices
cart: init
main: variable started
main: init
main: main runs; Namaste! 2 items

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 startup_session.py

greeting started first, then prices, then cart, then main, although main.go imports cart before greeting. Since Go 1.21 the specification defines the order exactly: sort all packages by import path, then repeatedly initialise the first package in that list whose imports have all been initialised. Step through it:

Interactive · stage by stage How the shopinit program starts Step through the rule that picks the next package to initialise, and compare each step with what the program printed.
How the shopinit program starts, step 7 of 7. Showing Standard library, greeting, prices, cart, package main, func main. In focus: func main.Standard libraryfmt and what it importsgreetingimports fmt onlypricesimports fmt onlycartimports pricespackage mainimports cart and greetingfunc mainruns last

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. The sorted list. The packages are sorted by import path. example.com/shopinit comes first among the module's, then cart, greeting and prices; fmt and the other standard-library packages sit at their own places in the alphabet.
  2. The standard library. Each step starts the first package in the list whose imports have all started. Every package of the module imports fmt, so fmt and the packages it needs start before any of them.
  3. greeting. main and cart come earlier in the list but wait for packages that have not started. greeting needs only fmt, so it goes first; its variable Text, then its init.
  4. prices. The next step looks from the top of the list again. cart still waits for prices, which is now the first one ready, so List is loaded and prices runs its init.
  5. cart. Now every import of cart has started. Its variable Items is set, and it sees the 2 prices that prices loaded a step earlier, then its init runs.
  6. package main. The main package starts last, like any package, once cart and greeting have started; its variable started, then its init.
  7. main runs. Only now does func main run, with every package of the program ready. It prints Namaste! 2 items.

Because the order is defined, a package can rely on the packages it imports being ready, as cart relied on the price list. Nothing else is promised, so never rely on a package that you do not import having started.

An import with the name _, the blank identifier: The name _ (an underscore), which takes a value and throws it away. It lets code ignore a result on purpose, or import a package only for what it does when it starts. More in “Your first Go program: go run and go build”, imports a package only for its initialisation. The image package’s documentation names the standard case: decoding a PNG needs the decoder that image/png registers in its init, so a program writes import _ "image/png". The two programs below differ only in that line:

Decoding a PNG with and without the blank import

Go (register_png.go) · register_png.go

// register_png.go: a blank import runs the init of image/png.
package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"image"
	_ "image/png" // its init registers the "png" format with package image
)

// A grey image of 2 by 1 pixels, encoded as PNG (73 bytes).
const pngHex = "89504e470d0a1a0a0000000d4948445200000002000000010800000000d149205600" +
	"00001049444154789c000300fcff02ff00030002070102e07a5cf80000000049454e44ae426082"

func main() {
	data, _ := hex.DecodeString(pngHex)
	cfg, format, err := image.DecodeConfig(bytes.NewReader(data))
	fmt.Printf("format %q, %dx%d, error: %v\n", format, cfg.Width, cfg.Height, err)
}

Output

format "png", 2x1, error: <nil>

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run register_png.go

Go (register_none.go) · register_none.go

// register_none.go: the same program without the blank import.
package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"image"
)

// A grey image of 2 by 1 pixels, encoded as PNG (73 bytes).
const pngHex = "89504e470d0a1a0a0000000d4948445200000002000000010800000000d149205600" +
	"00001049444154789c000300fcff02ff00030002070102e07a5cf80000000049454e44ae426082"

func main() {
	data, _ := hex.DecodeString(pngHex)
	cfg, format, err := image.DecodeConfig(bytes.NewReader(data))
	fmt.Printf("format %q, %dx%d, error: %v\n", format, cfg.Width, cfg.Height, err)
}

Output

format "", 0x0, error: image: unknown format

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run register_none.go

With the blank import, image.DecodeConfig recognised the format, png, 2x1; without it, the same bytes gave image: unknown format. That is the kind of work init is for: cheap, local registration that cannot fail. An init function takes no arguments, returns nothing and cannot be called, so it can report a failure only by panicking, and it runs in every program and test binary that imports its package, needed or not. Open files, network connections and anything that can fail belong in a function that the program calls from main and that returns an error.

Key idea

A package starts after every package it imports: its variables in dependency order, then its init functions as written, and main runs only when every package has started.

Checkpoint

Check: The order in which packages start

  1. Question 1 of 2 One package declares var a = b + 1, then var b = f(), then var c = 2, where func f() int { return c * 10 }. In which order are the variables initialised?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: c, then b, then a

    At each step the first variable in declaration order whose value depends on no uninitialised variable is set, as initvars.go set price, qty and then total.

  2. Question 2 of 2 Module example.com/app: main imports report and alerts; report imports store; alerts and store import nothing of the module, and every package imports fmt. In what order do the four packages start?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: alerts, store, report, main

    Go 1.21 defined it exactly: sort all packages by import path, then repeatedly initialise the first one whose imports have all been initialised. shopinit started greeting, prices, cart and main in the same way.

Name a package for what it gives its callers

A package’s name appears at every place it is used, in front of each name it exports, so it is part of every call. The Go blog’s guidance on package names comes down to a few rules: short, lower case, no under_scores or mixedCaps, a name that says what the package provides, and exported names that do not repeat it:

Instead of Write Why
util.FormatRupees rupees.Format util says nothing about what is inside, and it collects unrelated code over time
pricing.PricingTotal pricing.Total the caller already wrote pricing
package priority_queue package queue under_scores and mixedCaps are not used in package names
package api, types or interfaces a package per responsibility, such as orders or invoice one package of everything is imported by everything, which invites cycles
package buf package bufio do not take a name callers want for their variables

The standard library follows the same rules, as in http.Server, bufio.Reader and time.Now. If no short name fits what a package does, the package probably does two things and should be split. A quick test is to write the call site first: when pricing.Total(bill) reads like a phrase, the package and its names fit together.

Checkpoint

Check: Naming a package

  1. One question Package invoice exports InvoiceRender, InvoiceTotal and NewInvoice, which returns an *Invoice. What does the naming guidance suggest?

    Choose one answer.

    Show the answer: One question

    Answer: Render, Total and New

    A package's name is part of every call, so its exported names should read well after it: http.Server, not http.HTTPServer.

Practise

Most questions use packages you have not met here, such as mail, cart or numwords, so each one asks you to apply a rule rather than recall the kirana example. The session opens with the idea your earlier answers mark as weakest:

Practice that adapts to you

Practise this lesson

14 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 14 What is a Go package?

    Choose one answer.

    Show the answer: Question 1 of 14

    Answer: The .go files in one folder, with the same package clause, compiled together

    A package is a folder of .go files with one package clause. A module, declared by go.mod, is a tree of packages.

  2. Question 2 of 14 The module example.com/tools has folders cmd/lint and cmd/report, each with files that say package main. What does go build ./cmd/lint make?

    Choose one answer.

    Show the answer: Question 2 of 14

    Answer: One program, from the files of cmd/lint and the packages they import

    cmd/<name> folders are the usual way to hold several programs in one module, each folder one package main.

  3. Question 3 of 14 When is a Go name exported?

    Choose one answer.

    Show the answer: Question 3 of 14

    Answer: When it starts with an upper-case letter and is declared at the package level, or names a field or method

    The first letter, Unicode category Lu, and the place of the declaration decide it, nothing else.

  4. Question 4 of 14 type Ledger struct { total int64 } has the methods (l *Ledger) add(p int64) and (l *Ledger) Total() int64. Which can another package use?

    Choose every answer that is right.

    Show the answer: Question 4 of 14

    Answer:

    • The type Ledger
    • The method Total

    Each name is judged on its own: an exported type can have unexported fields and methods, which keeps them in the package's control.

  5. Question 5 of 14 Which package-level function is exported?

    Choose one answer.

    Show the answer: Question 5 of 14

    Answer: Über

    The test is the Unicode category of the first character only. Most Go code uses ASCII names, but the rule is the same for any letter.

  6. Question 6 of 14 Who enforces the internal/ rule?

    Choose one answer.

    Show the answer: Question 6 of 14

    Answer: The go command, when it resolves imports

    A folder named internal is an ordinary folder to the compiler, and the go command decides who may import it.

  7. Question 7 of 14 kirana gains cmd/billing/internal/fmtx. Which package may import it?

    Choose one answer.

    Show the answer: Question 7 of 14

    Answer: example.com/kirana/cmd/billing

    The prefix that may import is the path of the folder above internal, wherever it is in the tree.

  8. Question 8 of 14 orders imports invoice and invoice imports orders. What does go build ./... do?

    Choose one answer.

    Show the answer: Question 8 of 14

    Answer: It stops and prints the loop, ending in import cycle not allowed

    Packages are compiled and initialised after the packages they import, which needs the imports to form a graph without loops.

  9. Question 9 of 14 cart and checkout both work on the same Money type and keep importing each other to reach it. Which fix fits best?

    Choose one answer.

    Show the answer: Question 9 of 14

    Answer: Move Money into a third package, money, which both import and which imports neither

    An interface fits when one package only calls a few methods of the other; a third package fits when both share the same data.

  10. Question 10 of 14 When does a package's init function run?

    Choose one answer.

    Show the answer: Question 10 of 14

    Answer: Once, after the package's variables have their values and after the packages it imports have started, before main

    Initialisation is variables first, then init functions in the order written, one package at a time, and main runs only after every package has started.

  11. Question 11 of 14 What does import _ "image/png" do in register_png.go?

    Choose one answer.

    Show the answer: Question 11 of 14

    Answer: It imports image/png only to run its initialisation, which registers the png format with package image

    The blank identifier as the import name keeps only the package's side effects, its variables and init functions, which is how decoders and drivers register themselves.

  12. Question 12 of 14 A package must read a settings file that may be missing. Where should the read happen?

    Choose one answer.

    Show the answer: Question 12 of 14

    Answer: In a function such as Load(path string) (Settings, error) that main calls

    init is for cheap work that cannot fail, such as registering a decoder; anything that can fail belongs in a function that returns an error.

  13. Question 13 of 14 What is wrong with a package named util?

    Choose one answer.

    Show the answer: Question 13 of 14

    Answer: The name says nothing about what is inside, so unrelated code collects in it

    A package name should tell callers what the package provides, and a vague one also attracts code that has no place elsewhere.

  14. Question 14 of 14 A package turns amounts into words for cheques, such as 1250 into one thousand two hundred fifty. Which name and API fit best?

    Choose one answer.

    Show the answer: Question 14 of 14

    Answer: package numwords with func Format(n int64) string, called as numwords.Format(1250)

    Choose the package name and its exported names together, from the caller's side: numwords.Format says it all.

Interview questions

Warm-up (fresher): What makes a name in a Go package exported?

Its first character is an upper-case letter and it is declared at the package level, or it is the name of a field or a method. Total in package pricing is exported and other packages call it as pricing.Total; tax and a field named pricePaise are not, so only the files of the pricing folder can use them. There is no public or private keyword, and a folder named internal adds a separate rule about which packages may import it.

Two longer questions of this module build on this lesson:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    Your module has a package named helpers, meant only for its own commands. Another team now imports it and complains whenever you change it. What would you have done differently, and how do you fix it now without breaking them overnight?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A package opens its database connection in an init function and panics when the database is down. Since then, the unit tests of unrelated packages fail on every laptop without a database. Explain why, and how you would restructure the package.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • A package is the .go files of one folder, compiled together, and its import path is the module path followed by its folder, as go list ./... printed for kirana.
  • A name is exported when its first letter is upper case and it is declared at the package level or names a field or method; unexported names are shared only inside the package’s folder.
  • A package under internal/ can be imported only by code whose path starts with the folder above internal, so example.com/rival could not import kirana’s ledger.
  • Two packages may not import each other, directly or through others; a small interface in the package that needs it, or a third package for shared data, makes the imports go one way.
  • Each package starts after the packages it imports, its variables in dependency order and then its init functions, and main runs last; init is for cheap registration that cannot fail.
  • Name a package for what it provides, short and lower case, and never repeat the package name in its exported names.

Terms in this lesson

exported name
A name that other packages can use. A Go name is exported when its first character is an upper-case letter and it is declared at the package level, or when it names a field or a method.
import cycle
Packages that import one another, directly or through other packages. Go refuses to build them, so the imports of a program always form a graph without loops.
import path
The string an import declaration names, such as example.com/kirana/pricing: the module path followed by the package's folder in the module. Standard-library packages, such as fmt, have no module path in front.
init function
A function named init, with no parameters and no results, declared at the package level. It runs once, after the package's variables have their values, and no code can call it.
internal package
A package in or below a folder named internal. The go command lets only code whose import path starts with the path of the folder above internal import it.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.