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, constantsfunc 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).
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)}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)}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.
package main
import "fmt"
func main() { fmt.Println(shout("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) + "!"}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.
| Package | Purpose |
|---|---|
| fmt | Formatted printing and scanning input/output. |
| strings | String manipulation helpers (uppercase, split, contains, etc.). |
| strconv | Converting between strings and other basic types. |
| os | Interacting with the operating system (files, args, environment). |
| net/http | Building HTTP clients and servers. |
Common 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.