A First Hello World Program

Write your first Go program line by line - understand the package declaration, import statements, the main function, and how to build and run a minimal executable

You've installed Go and learned that the entry point of every executable is the main package with a main function. Now you'll write the smallest possible Go program that actually does something: printing a message to the terminal. This is the universal starting ritual in programming, but it's also a genuine test of your entire toolchain. If you can write, build, and run this program, you know your editor, compiler, and runtime are all wired together correctly.

The Hello World Code

Create a new file named main.go and type the following (or copy it exactly). Go is case‑sensitive, and every character here matters.

main.go
package main
import "fmt"
func main() {
    fmt.Println("Hello, World!")
}

This is the complete program. Five lines, but each one teaches you something essential about how Go programs are structured and executed. Let's walk through them in the order the Go compiler processes them, not in the order they appear on screen.

What Each Line Does

The package declaration – package main

Every Go source file must start with a package clause. It tells the compiler what namespace this file belongs to. main is special: it means "compile this into a standalone executable, not a reusable library." If you wrote package hello instead, the file would become a library, and there would be nothing to run directly.

The name main isn't chosen randomly – it's a keyword the Go toolchain looks for. When you run go build or go run on a directory, Go scans the files, finds the one with package main and a main() function, and wires that as the start of your program.

The import statement – import "fmt"

Before you can use any functionality that isn't built directly into the language, you must import the package that provides it. "fmt" is a package from Go's standard library that handles formatted input and output – things like printing to the console, reading from it, and formatting strings with placeholders.

The double quotes around the package path are required. The path "fmt" is a short name for a package that lives inside Go's installation directory. Later you'll import packages from the web or your own project, using full module paths like "github.com/user/package".

Unused imports are compile errors:

Go refuses to compile if you import a package and never use it. This keeps your code free of cruft, but it can surprise beginners. If you add import "os" and don't call anything from it, the compiler will stop with an error until you remove the line or actually use the package.

The main function – func main()

This is the door through which execution enters your program. The func keyword declares a function, main is its name, and the empty parentheses mean it takes no arguments and returns nothing. The Go runtime calls this function automatically when your executable starts – you never call main() yourself.

The curly braces {} enclose the function body. Go requires the opening brace to be on the same line as func main(). Putting it on the next line is a syntax error. This may feel arbitrary, but it's a consequence of Go's automatic semicolon insertion rule: a newline after the closing parenthesis would insert a virtual semicolon and break the program.

Inside the braces, Go executes statements in order, top to bottom. Right now, there's only one statement.

Printing to the console – fmt.Println("Hello, World!")

This line calls the Println function from the fmt package. The fmt. prefix is the way you access exported names from an imported package. Go capitalises the first letter of any name that should be visible outside its own package, so Println is exported; if you wrote fmt.println with a lowercase p, the compiler would reject it.

Println takes zero or more arguments, prints them separated by spaces, and adds a newline at the end. The argument here is a string literal – a sequence of characters surrounded by double quotes. When the program runs, those characters appear in your terminal exactly as written, followed by a line break.

Running the Program

You have two primary ways to turn the source code into running output. Both are simple, but they serve different purposes.

The quick way: go run

Open a terminal in the directory where main.go lives and type:

go run main.go

Behind the scenes, go run compiles your code into a temporary binary, executes it, and then deletes the binary. You'll see:

Hello, World!

This is the fastest feedback loop while you're experimenting. Use it to test small changes without creating permanent build artifacts.

The permanent way: go build

If you want an executable file you can keep, share, or run later, use:

go build main.go

This produces a binary named main (on Linux/macOS) or main.exe (on Windows) in the same directory. You can then run it directly:

./main

Again, you'll see Hello, World! printed. The binary is self-contained – it doesn't need Go installed on the machine where it runs, as long as the target operating system and architecture match.

Your environment is working:

If you see Hello, World! in your terminal, congratulations. Your Go installation, your editor, your terminal, and your understanding of the entry point are all aligned. This may look trivial, but it confirms that every layer from keystroke to output is functioning.

How the Startup Sequence Works (A Mental Model)

A simple way to picture what happens when you run the program is to imagine a factory with a single workbench. The package main line tells the factory manager that this is the final product, not a component that goes into something else. The import lines gather the necessary tools – here, just the fmt printing machine. The func main() is the startup button. When you press it, the manager walks to that button, and the first instruction inside the braces, fmt.Println(...), picks up the string and stamps it onto the terminal. Then the function ends, the manager clocks out, and the program exits.

That mental model won't carry you through an entire career, but it gives you a solid picture of the boundary between compile‑time organisation (package, import) and run‑time action (func main, the statements inside). Every Go program, no matter how large, begins with that same button press.

Common Mistakes When Writing Your First Program

Even a five‑line program has sharp edges. Here are the ones that trip up nearly everyone.

Missing package main or a misnamed main function:

If you write package hello or name your function Main instead of main, go run will complain with something like go run: cannot run non-main package. The toolchain is strict: an executable must have a package main with an exact func main() – lowercase, no arguments, no return type.

Using the built‑in println instead of fmt.Println:

Go has built‑in functions print and println that work without importing anything. They're intended for low‑level debugging and are not guaranteed to stay in the language. Using println("Hello") might work today, but it produces output on a different writer than fmt.Println and can silently stop working in the future. Always use fmt.Println for standard console output.

Forgetting to import fmt:

If you call fmt.Println without importing "fmt", the compiler will say undefined: fmt. The fix is to add import "fmt" at the top. Go won't guess what you mean; you must declare every dependency.

Modifying the Program

Once the basic version works, change the string inside Println to something else, save the file, and run it again. The ability to edit and re‑run quickly builds confidence. Try these small experiments:

  • Change "Hello, World!" to "Learning Go is fun."
  • Add a second fmt.Println call on the next line with a different message.
  • Use fmt.Print instead of fmt.Println – notice that Print does not add a newline, so the next output will appear on the same line.
  • Introduce a deliberate error, like removing the closing quote mark, and read the compiler's error message. The error messages in Go are designed to be precise, pointing at the exact line and character where the problem occurs.

Each variation teaches you something about how the compiler responds and how the output changes, without adding any new concepts.