LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 716 min read

Program Structure & Packages

Understand how Go files and packages are organized, how imports work, and the capitalization rule that controls exported vs unexported names.

Introduction

Go organizes code into packages rather than loose files. Understanding how packages, imports, and visibility work is essential before writing anything beyond a single-file toy program — it's the foundation for how every real Go project, including the standard library itself, is structured.

This lesson covers the anatomy of a Go file, what packages are and how imports pull in code from elsewhere, and Go's uniquely simple rule for controlling what is visible outside a package.

Anatomy of a Go File

Every Go source file follows the same basic structure, in this exact order: a package declaration, followed by any import statements, followed by the actual code (functions, variables, types, etc.).

package greetings // 1. Package declaration
import "fmt" // 2. Imports
// 3. Code — functions, types, variables, constants
func Hello(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}

What Is a Package?

A package is Go's unit of code organization — essentially a folder of .go files that all declare the same package name and work together as one unit. Every Go program is made up of at least one package, and larger programs are typically split into many packages, each with a focused responsibility (for example, a `database` package, an `httpserver` package, and so on).

Package Naming Rule

All .go files in the same directory must declare the same package name. A directory named "greetings" conventionally (but not strictly required) contains files declaring "package greetings".

The import Statement

The `import` statement pulls in code from another package so you can use it. You can import a single package, or a grouped block of several packages using parentheses — the grouped form is idiomatic Go style for more than one import.

package main
import (
"fmt"
"strings"
)
func main() {
message := strings.ToUpper("go is fun")
fmt.Println(message)
}
Output

Click Run to see what this code prints.

To use anything from an imported package, you prefix it with the package name, as in `fmt.Println` or `strings.ToUpper`. Go will refuse to compile if you import a package but never actually use it — this keeps the codebase free of unused clutter.

Exported vs Unexported Identifiers

Go has a strikingly simple rule for controlling visibility, with no `public`/`private` keywords at all: if a function, variable, type, or constant name starts with an uppercase letter, it is exported (visible to other packages that import this one). If it starts with a lowercase letter, it is unexported (only usable within its own package).

package greetings
import "fmt"
// Hello is exported — starts with a capital letter.
// Other packages can call greetings.Hello(...)
func Hello(name string) string {
return formatGreeting(name)
}
// formatGreeting is unexported — starts with a lowercase letter.
// Only code inside package "greetings" can call it.
func formatGreeting(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
One Rule, No Exceptions

This capitalization rule applies uniformly to function names, variable names, struct field names, type names, and constants. There is no separate "public"/"private" keyword to learn in Go — just capitalization.

Building a Multi-File Package

Files in the same package can freely use each other's unexported identifiers without importing anything, since they're part of the same package. For example, a `main.go` and a `helpers.go` file both declaring `package main` in the same folder can call each other's functions directly.

main.go
package main
import "fmt"
func main() {
fmt.Println(shout("go"))
}
helpers.go
package main
import "strings"
// shout is unexported, but main.go can still call it directly
// because both files belong to the same package.
func shout(word string) string {
return strings.ToUpper(word) + "!"
}
Output (go run .)

Click Run to see what this code prints.

Standard Library Packages

Go ships with a rich standard library covering common needs, so you frequently won't need third-party packages for everyday tasks.

PackagePurpose
fmtFormatted printing and scanning input/output.
stringsString manipulation helpers (uppercase, split, contains, etc.).
strconvConverting between strings and other basic types.
osInteracting with the operating system (files, args, environment).
net/httpBuilding HTTP clients and servers.

Common Mistakes

Avoid These Mistakes
  • Importing a package but never using it — Go treats this as a compile error, not just a warning.
  • Expecting `public`/`private` keywords — Go only uses capitalization to control visibility.
  • Mixing different package names inside the same directory — every .go file in a folder must declare the same package name.
  • Forgetting that lowercase (unexported) identifiers are invisible outside their own package, even to code that imports it.

Best Practices

  • Only export (capitalize) names that genuinely need to be used by other packages — keep everything else lowercase.
  • Group related imports using the parenthesized form for readability.
  • Give packages short, clear, lowercase names (e.g. `httpclient`, not `HTTPClientPackage`).
  • Let `goimports` or your editor's Go extension automatically manage and sort your import list.

Frequently Asked Questions

Not strictly, but it is a very strong convention in Go, and deviating from it can confuse tooling and other developers. Stick to matching names unless you have a specific reason not to.

Yes. Exported and unexported identifiers can be freely mixed within a single file or across the files of a package.

Go's compiler treats unused imports as a compile-time error, not just a warning — you must remove the import or actually use it.

Key Takeaways

  • Every Go file starts with a package declaration, followed by imports, followed by code.
  • A package is a folder of .go files sharing the same package name.
  • Capitalized identifiers are exported (visible to other packages); lowercase identifiers are unexported (package-private).
  • Go has no public/private keywords — capitalization is the entire visibility system.
  • Unused imports are compile errors in Go, keeping codebases clean by default.

Summary

You now understand how Go organizes code into packages, how imports work, and the capitalization rule that governs visibility across the entire language. Next, you'll dig into how to declare variables and constants in Go.

Next Lesson →

Variables & Constants