SyntaxStudy
Sign Up
Go Organising Code with Packages
Go Beginner 1 min read

Organising Code with Packages

A package is the basic unit of code organisation in Go. Every Go file belongs to exactly one package, declared with the `package` statement at the top of the file. All files in the same directory must declare the same package name. The package name is the short, lowercase identifier used in code, while the import path is the full path to the directory. Identifiers that start with an uppercase letter are exported (visible outside the package), while identifiers starting with a lowercase letter are unexported (package-private). This simple convention replaces the `public`, `protected`, and `private` modifiers found in other languages. Unexported identifiers are part of the implementation detail and are free to change without affecting users of the package. Package design in Go favours small, focused packages with clear responsibilities. A common convention is to place the main executable packages in `cmd/` and reusable library packages in `internal/` (accessible only within the module) or at the module root. The `internal` directory provides enforced access control: only code within the parent of `internal` can import its packages.
Example
// Package layout for a small web service:
//
// myapp/
//   go.mod
//   main.go               (package main)
//   cmd/
//     server/
//       main.go           (package main — the binary entrypoint)
//   internal/
//     config/
//       config.go         (package config — unexported to outside module)
//     handler/
//       user.go           (package handler)
//   pkg/
//     validate/
//       validate.go       (package validate — public reusable library)

// internal/config/config.go
package config

import "os"

// Config holds application configuration.
type Config struct {
    Port     string
    DSN      string
    LogLevel string
}

// Load reads configuration from environment variables.
func Load() Config {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    return Config{
        Port:     port,
        DSN:      os.Getenv("DATABASE_URL"),
        LogLevel: os.Getenv("LOG_LEVEL"),
    }
}

// pkg/validate/validate.go
package validate

import (
    "errors"
    "strings"
)

// Email returns an error if s is not a valid email address.
func Email(s string) error {
    if !strings.Contains(s, "@") {
        return errors.New("validate: invalid email address")
    }
    return nil
}