Package System — The Foundation of Go Code Organization
All Go code is organized in packages. A package is Go's basic code organization unit that groups related functionality together.
Package Declaration and Imports
package main // Package declaration — first line of every .go file
import (
"fmt" // Standard library
"math/rand" // Sub-package
"os"
"github.com/gin-gonic/gin" // External package (requires module)
)
func main() {
fmt.Println("Hello, Go!")
}
Package naming rules:
- Lowercase only
- Short and clear (ideally one word)
- Should match directory name (exceptions:
main,_test) - Avoid plural forms or common prefixes
Exported vs Unexported
Go determines visibility by the first letter's case— uppercase means exported (public), lowercase means unexported (private).
package geometry
import "math"
// ✅ Exported — accessible from other packages
type Circle struct {
Radius float64 // Exported field
color string // Unexported field (only accessible in same package)
}
// ✅ Exported function
func NewCircle(radius float64) *Circle {
return &Circle{
Radius: radius,
color: "red", // Unexported fields can only be set in same package
}
}
// ✅ Exported method
func (c *Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// ❌ Unexported method — only usable within same package
func (c *Circle) validate() bool {
return c.Radius > 0
}
// Exported constant
const Pi = 3.14159
// Unexported constant
const maxRadius = 1000.0
Usage example:
package main
import (
"fmt"
"myproject/geometry"
)
func main() {
c := geometry.NewCircle(5.0)
fmt.Println(c.Area()) // ✅ Can access exported method
fmt.Println(c.Radius) // ✅ Can access exported field
// fmt.Println(c.color) // ❌ Compile error
// c.validate() // ❌ Compile error
}
init Function — Package Initialization
The init() function runs automatically when a package is first loaded.
package database
import (
"fmt"
"log"
)
var (
connectionPool *Pool
maxConnections = 10
)
// init function: auto-executes when package loads
func init() {
fmt.Println("Initializing database package...")
var err error
connectionPool, err = newPool(maxConnections)
if err != nil {
log.Fatalf("Failed to initialize DB pool: %v", err)
}
fmt.Println("Database package initialized")
}
type Pool struct {
size int
}
func newPool(size int) (*Pool, error) {
return &Pool{size: size}, nil
}
init Function Characteristics
package mypackage
import "fmt"
var order []string
func init() {
order = append(order, "first init")
fmt.Println("init #1 executed")
}
func init() {
// Multiple init functions allowed in the same file!
order = append(order, "second init")
fmt.Println("init #2 executed")
}
// init takes no arguments and returns no values
// Cannot be called directly (compile error if you try)
init execution order:
- Package-level variable initialization
init()functions in file order- Imported packages' init runs first
package main
import (
_ "myproject/database" // Import for side effects only (runs init)
"fmt"
)
func main() {
fmt.Println("main executing")
// Output order:
// Initializing database package...
// Database package initialized
// main executing
}
Import Aliases and Blank Imports
package main
import (
"fmt"
// Alias import — resolves name conflicts
mrand "math/rand"
crand "crypto/rand"
// Blank import — only for running init
_ "github.com/lib/pq" // Registers PostgreSQL driver
// Dot import — use without package prefix (not recommended)
. "math"
)
func main() {
fmt.Println(mrand.Intn(100)) // Use math/rand
fmt.Println(crand.Reader) // Use crypto/rand
fmt.Println(Pi) // Use math.Pi directly as Pi
}
Practical Package Structure
A realistic package structure example.
myapp/
├── main.go # package main
├── cmd/
│ └── server/
│ └── main.go # package main (server entry point)
├── internal/ # Cannot be imported from external packages
│ ├── auth/
│ │ └── auth.go # package auth
│ └── db/
│ └── db.go # package db
├── pkg/ # Packages to expose externally
│ └── validator/
│ └── validator.go # package validator
└── api/
└── handlers.go # package api
// internal/auth/auth.go
package auth
import (
"crypto/rand"
"encoding/hex"
"errors"
)
// ErrInvalidToken — exported error (sentinel)
var ErrInvalidToken = errors.New("invalid token")
// Token — exported type
type Token struct {
Value string
UserID string
ExpiresAt int64
}
// Generate — exported function
func Generate(userID string) (*Token, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return nil, err
}
return &Token{
Value: hex.EncodeToString(b),
UserID: userID,
}, nil
}
// validate — unexported (internal only)
func validate(token string) bool {
return len(token) == 64
}
Package Documentation
Add documentation to Go packages by writing comments above the package declaration.
// Package geometry provides calculations for geometric shapes.
//
// It computes areas and perimeters for circles, triangles,
// rectangles, and other shapes.
//
// Example usage:
//
// c := geometry.NewCircle(5.0)
// fmt.Println(c.Area())
package geometry
Use go doc to view documentation:
go doc geometry
go doc geometry.Circle
go doc geometry.Circle.Area
Key Takeaways
- Exported/Unexported: First letter uppercase = exported, lowercase = unexported
- init function: Runs automatically on package load, cannot be called directly
_blank import: Import for init or side effects only- Package name = directory name: Convention to keep them matching
- internal package: Only importable within the same module