Go / Basic: Values and Program Contracts / Lesson 1
Tooling, Modules, and the Program Model
Follow a Go program from package source to an executable, and record language and toolchain choices explicitly.
On this page
Lesson goals
- Distinguish a module, package, and command.
- Explain main, imports, formatting, and build checks.
- Read go.mod without confusing it with an exact toolchain lock.
A command is a package with an entry point
A source file begins with a package declaration. Files in one directory usually form one package; a command uses package main and defines func main(). Imported packages provide named interfaces, and the compiler rejects unused imports. Go source is compiled before execution even when go run makes the build step feel invisible.
The first program imports fmt and calls Println, which separates arguments with spaces and ends the line. main returns no value. Returning normally ends that goroutine; returning from main terminates the program, so later lessons must wait for background work explicitly rather than relying on it surviving.
package main
import "fmt"
func main() {
fmt.Println("Go", "ready")
}
Go ready
The command has one entry point and writes a deterministic line.
A module groups versioned packages
A module is a collection of packages released together. Its root contains go.mod, whose module directive defines the import-path prefix. A local learning module can use an example domain without contacting it. Standard-library imports such as fmt do not require downloading third-party modules. Keep each independent demonstration in its own temporary module to avoid duplicate main definitions.
The go directive specifies a minimum Go version and influences language semantics for packages in the module. A toolchain directive can influence tool selection, and automatic selection may download a toolchain. It is not enough to say “my machine has Go”; record go version, the module directives, and relevant environment settings.
Keep a small reproducible workflow
go fmt formats packages using the standard formatter; go build checks and builds the selected packages; go test runs package tests. A successful build proves type and syntax acceptance, not correct behavior for every input. Run these tools from the intended module directory and inspect the first relevant diagnostic rather than editing around later cascading messages.
For isolated standard-library examples, an offline configuration can disable module proxies and automatic toolchain downloads after a suitable compiler is installed. That is a validation convenience, not a universal project policy. Real dependency updates require deliberate retrieval, review, and reproducible version records rather than silently depending on whatever happens to be cached.
Trace a value through full statements
A variable is a named location holding a value of its declared type. The short declaration := introduces a local variable when its rules are satisfied; = assigns to an existing one. In the next program count starts at two and is then replaced by five. The call observes the result of that completed assignment.
This trace uses no assumptions about stack addresses, machine registers, or scheduling. Go implementations decide storage placement, and later compiler optimizations may remove a physical variable entirely. Explain behavior from the language contract and observable effects rather than claiming that every source line corresponds to one processor instruction.
package main
import "fmt"
func main() {
count := 2
count = count + 3
fmt.Printf("count=%d\n", count)
}
count=5
The integer value becomes five before the formatting call reads it.
Separate a baseline from the latest release
This course declares Go 1.22 language semantics, including the loop-variable change introduced there. Official documentation may describe a newer release; an API marked as added later must not be assumed available in a Go 1.22 project. Check the version annotation and the project’s actual compatibility requirements before copying a convenient new helper.
When reporting a failure, include the minimal source, exact command, module directives, toolchain version, input, and expected versus actual output. A stale executable or a command run in another module can make a correct edit appear ineffective. Reproduce in a clean temporary directory before drawing conclusions about the language.
Common errors and fixes
An imported package is rejected as unused.
- Why it happens
- No remaining expression refers to that import.
- How to fix it
- Remove the unused import or use its intended interface.
- Prevent it next time
- Let the formatter and build checks run after edits.
Two demonstrations cannot build together.
- Why it happens
- The same package contains multiple main definitions.
- How to fix it
- Place independent examples in separate temporary directories.
- Prevent it next time
- Treat each complete example as its own command.
A new helper is unavailable despite valid syntax.
- Why it happens
- The selected toolchain or library predates the API.
- How to fix it
- Use a baseline-compatible operation or deliberately update the project requirement.
- Prevent it next time
- Check official added-in-version annotations.
Check your understanding
1. Predict the second program after initializing count to four and adding seven. Identify which facts determine the output.
Show worked solution
The initializer stores four, then the assignment computes eleven and replaces count. Printf writes count=11 followed by a newline. The result follows from integer arithmetic on small representable values and statement order. It does not depend on a guessed address or compiler optimization level. The declaration still introduces one local int.
2. Explain why a go 1.22 directive alone does not record every fact needed to reproduce a build.
Show worked solution
It records a language/minimum-version requirement, while the selected compiler may be newer. Dependencies, go.sum, workspace settings, build tags, platform settings, and toolchain selection can also affect the build. Record those relevant inputs and run the same package set. For this standard-library-only example, a recorded compiler plus a clean small module keeps the reproduction especially simple.
Key takeaways
- A command, package, and module are different boundaries.
- Record language semantics and the actual toolchain separately.
- A reproducible example includes source, build context, input, and an explicit expected result.