Packages & Imports
Go packages are the unit of code reuse. Imports, exported names, internal directories, and the layout that scales.
Go — packages
EXAMPLE
// ===== A package =====
// A directory's .go files form a package (declared at the top: package X).
// Each binary needs a 'main' package with func main().
// myapp/
// go.mod
// main.go
// internal/
// pricing/
// pricing.go
// pricing_test.go
// storage/
// storage.go
// ===== go.mod =====
module github.com/me/myapp
go 1.22
require (
github.com/google/uuid v1.6.0
)
// ===== Importing =====
package main
import (
"fmt"
"github.com/google/uuid"
"github.com/me/myapp/internal/pricing"
)
func main() {
fmt.Println(uuid.NewString(), pricing.Total(100, 0.1))
}
// ===== Exported vs unexported =====
// Capitalised identifiers are EXPORTED (public).
// Lowercase identifiers are PACKAGE-PRIVATE.
package pricing
const taxRate = 0.10 // private
const MaxItems = 100 // exported
func Total(subtotal float64, tax float64) float64 {
return subtotal * (1 + tax)
}
func discount(p float64) float64 { return p * 0.9 } // private
// ===== internal/ directory =====
// Code under internal/ is only importable by code in the SAME module subtree.
// Use it to enforce 'don't import this from outside the module':
// internal/pricing -> importable only from github.com/me/myapp/...
// ===== vendor/ (optional) =====
go mod vendor // copies deps under vendor/
go build -mod=vendor // forces use of vendor/
// Useful for air-gapped builds; otherwise rely on module cache + go.sum.
// ===== Common package layouts =====
// Standard: cmd/<binary>/main.go for many binaries
// myapp/
// cmd/
// myapp/main.go
// mycli/main.go
// internal/...
// pkg/ (optional; library-style packages)
// ===== Init functions =====
// init() runs once per package, before main:
package config
var Default = load()
func init() {
// run before main
}
// Use sparingly; explicit constructors usually read better.
// ===== Tests =====
// Same dir as the code; file name *_test.go; package matches.
package pricing
import "testing"
func TestTotal(t *testing.T) {
if Total(100, 0.1) != 110 { t.Fatal("bad") }
}
// Run: go test ./...
// ===== Modules + workspaces =====
// One repo, one module (go.mod at root).
// Workspaces (go.work) for multi-module repos:
go work init ./svc-a ./svc-b
// Lets the modules reference each other locally without replace directives.
// ===== Patterns to internalise =====
// - One package per concern; small + focused
// - Capitalise for export; lowercase to keep internal
// - internal/ for module-only code
// - cmd/<name>/main.go layout for many binaries
// ===== Pitfalls =====
// - Cyclic imports (Go forbids them; refactor)
// - Storing types in 'util' / 'common' grab-bag packages
// - Package names mismatching directory name -> compile errors
// - Importing internal/ from OUTSIDE the module -> 'use of internal package not allowed'
Why it matters
Packages are how Go scales code. Capitalise for public, lowercase for private, lean on internal/ to lock visibility, and follow the cmd/
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.ToUpper("go"))
}
Try it Yourself »
Discussion
Loading…