Go Backend Development / Free preview / Session 2

Free preview

GO101 · Session 2: Anatomy of a Go program

You are reading a free preview — session 2 of GO101 (Go Language Fundamentals), the first module of Go Backend Development. Enrolled students get all 288 sessions, the module materials, grading and the production capstone. Become a production-ready Go backend developer in 6 months.

Enrol free in Go Backend Development See the full programme

Chapter 2 — Anatomy of a Go Program

Objectives. Read and write the structure of a Go program confidently: packages, imports, exported and unexported names, naming conventions and doc comments — from day one. Time budget: about 2 hours, roughly 40 minutes of theory and 80 minutes hands-on.

Theory

Packages are the unit of organisation

Every Go file starts with a package clause. Files in the same directory form one package and share everything in it — a function declared in one file is callable from the others with no import and no qualification. A program needs exactly one package main with one func main; libraries use any other package name.

An import brings in another package's exported names, referenced as pkg.Name. Two consequences shape daily work:

  • Unused imports and unused local variables are compile errors, not warnings. Go treats leftovers as mistakes in progress and makes you clean them up immediately. This feels strict for a day and then becomes invisible — and codebases stay clean by construction.
  • Imports are per file, not per package: each file imports exactly what it uses.

Exported and unexported: capitalisation is the visibility rule

Go has no public/private keywords. A name that starts with a capital letter is exported — visible to other packages. A lowercase name is visible only inside its own package. PendingCount can be called from anywhere the package is imported; pendingCount cannot. This one rule replaces the access-modifier machinery of Java or C#, and it makes the public surface of a package visible at a glance in any file.

Naming conventions

Go names are short and descriptive in proportion to their scope: a loop index is i, a package-level concept earns a full word. Conventions that the community (and reviewers) enforce:

  • MixedCaps for multi-word names: taskCount, never task_count or TaskCount for a local.
  • Acronyms keep one case: userID, httpServer — not userId or HttpServer.
  • Package names are short, lowercase, single words naming what the package provides (store, model), never util or common.
  • Getters are named Name(), not GetName() (a later course leans on this; start now).

Doc comments and semicolon insertion

A doc comment sits directly above a declaration with no blank line between, and starts with the name it documents: // PendingCount returns .... Session 19 turns this into documentation you can read with go doc; the habit starts here.

Finally, a piece of mechanics that explains Go's brace style. The compiler inserts semicolons automatically at line ends where a statement could end — which is why an opening brace must sit at the end of the preceding line: putting it on the next line would end the statement first. gofmt enforces the resulting style mechanically, so you never think about it again.

Worked examples

Example 2.1 — One package, two files

banner.go:

package main

// Banner returns the program's start-up line.
// Exported (capital B): callable from other files and packages.
func Banner() string {
	return "taskman — your tasks, in the terminal"
}

// pendingCount is unexported (lowercase p): visible only in this package.
func pendingCount() int {
	return 3
}

// PendingCount exposes the pending count to callers outside the package.
func PendingCount() int {
	return pendingCount()
}

main.go:

// Example 2.1 — a program split across two files in one package.
package main

import "fmt"

func main() {
	fmt.Println(Banner())
	fmt.Println("tasks pending:", PendingCount())
}

Output:

taskman — your tasks, in the terminal
tasks pending: 3

Note what did not happen: main.go never mentions banner.go. Same package, same scope. And notice the wrapper pattern: the real work sits in an unexported function, and the exported name is a thin, documented doorway. You will use that shape constantly in package design.

Example 2.2 — Naming and doc comments

// Example 2.2 — naming and doc comments: read the file, then run it.
package main

import "fmt"

// MaxTasks is the largest number of tasks the CLI will store.
// A doc comment starts with the name it documents.
const MaxTasks = 10000

// taskID is unexported: MixedCaps, no underscores, acronyms in full caps.
type taskID int

func main() {
	var id taskID = 42
	fmt.Println("max tasks:", MaxTasks)
	fmt.Println("sample id:", id)
}

Output:

max tasks: 10000
sample id: 42

Declaring a named type (type taskID int) for a bare number is a first taste of Go's type discipline: a taskID is not interchangeable with a count or an age, even though all three are integers underneath. Sessions 13–14 build the project's domain types on exactly this move.

Guided lab

  1. Take a deliberately messy program (your tutor file or one from the course repository): inconsistent spacing, snake_case names, a stray unused import, a misleading comment.
  2. Run gofmt -l . to list what formatting alone would change; run gofmt -w and re-check.

Guided lab.

Expected: after -w, gofmt -l . prints nothing.

  1. Fix what gofmt cannot: rename identifiers to Go conventions, delete the unused import (the compiler names it), and rewrite one comment into a doc comment that starts with the identifier's name.
  2. Run go vet ./....

Guided lab.

Expected: silence. go vet passing plus gofmt clean is this programme's definition of "the file is finished", from today onwards.

  1. Split the program's helper into a second file in the same package and confirm it still builds with go run . — then try go run main.go and read the "undefined" error, so you recognise it forever.

Practice exercises

  • Exercise 2.1 (core). Write a two-file program: banner.go provides an exported Line() built on an unexported helper; main.go prints it.
  • Exercise 2.2 (core). Rewrite these names the Go way and use them in a program that prints them: my_task_count, HttpServer, user_id, MAX_SIZE (as a local variable).
  • Exercise 2.3 (core). Declare a constant DefaultLimit and a function DescribeLimit() with doc comments that start with their names; print the description.
  • Exercise 2.4 (stretch). In one package across two files, call an unexported function defined in the other file. In a comment, state the rule that makes this legal — and what would change if the files were in different packages.

Solutions are in the appendix (Appendix A, Chapter 2).

Common errors and pitfalls

  • imported and not used. The compiler counts an import as used only if the file references one of its names. Delete the import or finish the thought — never silence it with a blank import in ordinary code.
  • declared and not used. Same rule for local variables. If you are mid-edit, this error is the compiler tapping your shoulder, not obstructing you.
  • Trying to call a lowercase name from another package. It fails with "undefined" or "cannot refer to unexported name". Capitalise it deliberately and add a doc comment — exporting is a design decision.
  • Braces on their own line (habit from Java/C#): the compiler reports missing ',' in... or a syntax error pointing somewhere confusing. Automatic semicolon insertion ended the statement early. gofmt will keep you honest.

Session summary

A Go program is files grouped into packages; same-package files share everything, imports are per file, and capitalisation alone controls visibility across packages. Unused imports and variables are compile errors. Naming and doc comments are conventions the whole ecosystem can read.

Key points.

  • Exported = capital first letter. There are no access-modifier keywords.
  • Unused imports and local variables do not compile — clean as you go.
  • One package per directory; package main + func main makes a program.
  • Doc comments start with the name they document.

Recommended videos

  • Golang Tutorial for Beginners | Full Go Course — TechWorld with Nana — https://www.youtube.com/watch?v=yyUHQIec83I — watch the chapter at 12:54 (structure of a Go file) and compare its file layout with Example 2.1.
  • Learn Go Programming - Golang Tutorial for Beginners — Michael Van Sickle (freeCodeCamp.org) — https://www.youtube.com/watch?v=YS4e4q9oBaU — the chapter at 0:35:48 (variables) is a useful second pass over declaration and naming in practice.