iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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/ convention for multi-binary projects. The discipline pays back in repos that read cleanly from any entry point.

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…