Learn Go
beginner2 min read

Packages and Imports

Go organises code into packages. A package is a directory containing one or more .go files that share the same package declaration. Packages are the fundamental unit of code organisation and reuse in Go.

Package Declarations

Every Go file starts with a package declaration:

package main     // executable package
// or
package mathutil // library package

Packages with package main are compiled into executables. Any other name produces a library that other packages can import.

Exported Names

An identifier starting with an uppercase letter is exported — visible to other packages. Lowercase identifiers are package-private:

package mathutil
 
// Exported — other packages can use this
func Add(a, b int) int {
    return a + b
}
 
// Unexported — only visible within this package
func helper(n int) int {
    return n * 2
}

Idiomatic Go: Go enforces encapsulation through capitalisation alone — no public, private, or protected keywords are needed.

Importing Packages

Use the import keyword to bring packages into scope. Group multiple imports in a single parenthesised block:

package main
 
import (
    "fmt"
    "math"
    "strings"
)
 
func main() {
    fmt.Println(math.Sqrt(16))           // 4
    fmt.Println(strings.ToUpper("hello")) // HELLO
}

Import Aliases

Alias an import when names would conflict or when the default name is verbose:

package main
 
import (
    "fmt"
    mrand "math/rand"
    crand "crypto/rand"
)
 
func main() {
    fmt.Println(mrand.Intn(100))
    buf := make([]byte, 16)
    crand.Read(buf)
    fmt.Printf("%x\n", buf)
}

Blank Import

Importing a package solely for its side effects (usually init registration) uses _:

import _ "image/png" // registers the PNG decoder

The init Function

Each package may define one or more init functions. They run automatically, in order, after all variable initialisations in the package, and before main:

package main
 
import "fmt"
 
var greeting string
 
func init() {
    greeting = "Hello from init!"
    fmt.Println("init() called")
}
 
func main() {
    fmt.Println(greeting)
}
// Output:
// init() called
// Hello from init!

init functions cannot be called directly. Use them for one-time setup such as registering drivers, validating configuration, or seeding state.

Package Paths and the Standard Library

Standard-library packages are imported by their short path (e.g., "fmt", "net/http"). Third-party packages use a full module path:

package main
 
import (
    "fmt"
    "net/http"
)
 
func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello from Go!")
    })
    http.ListenAndServe(":8080", nil)
}

The internal Package Convention

A package placed in a directory named internal can only be imported by code in the parent directory tree. This enforces boundaries without requiring additional visibility modifiers:

myapp/
  internal/
    auth/    ← importable only within myapp/
  api/
  main.go

Key Takeaways