Go From Zero, Part 1: Why Go, Setting It Up, and Your First Program

This is Part 1 of 12 in Go From Zero, a beginner-to-production series on the Go programming language. You are at the very beginning, which is a great place to be.

A few years ago I joined a team where almost everything ran on Go. I came from a world of heavy frameworks and long build times, and I expected the usual learning curve. Instead, I was shipping small fixes in my first week. Not because I’m clever, but because Go is small. There just isn’t that much language to learn.

That’s the promise of this series. Over 12 posts we’ll go from “what is Go?” to building a real REST API, and along the way I’ll show you the habits that experienced Go developers follow without thinking about them. Every post links to the one before and after it, so you can read straight through like a book.

Today we keep it simple: why Go exists, how to install it, and how to write, run and build your first program.

Why Go exists

Go was designed at Google around 2007 by Robert Griesemer, Rob Pike and Ken Thompson, and released as open source in 2009. The story goes that they started sketching it while waiting for a huge C++ program to compile.

They wanted a language that:

  • Compiles fast. Big projects build in seconds, not minutes.
  • Is easy to read. You should be able to open any Go codebase and follow it, even if you didn’t write it.
  • Makes concurrency practical. Modern servers handle thousands of things at once, and the language should help with that instead of fighting it.
  • Produces a single binary. No runtime to install on the server, no dependency hunting. Copy one file and run it.

Those goals explain nearly every design decision you’ll meet in this series. When something in Go feels “too simple”, it’s usually on purpose.

Where Go shines

You’ve already used software written in Go, probably today. Docker, Kubernetes, Terraform, Prometheus, Grafana’s backend, Hugo and CockroachDB are all written in it. Go is especially popular for:

  • Web servers and APIs
  • Command-line tools
  • Cloud and infrastructure tooling
  • Networking services and proxies
  • Data pipelines and background workers

It’s less common for things like mobile apps, desktop GUIs or heavy data science, where other ecosystems are stronger. Knowing where a tool fits is part of using it well.

Installing Go

Head to the official download page at go.dev/dl and grab the installer for your operating system. Always pick the latest stable version. Go keeps strong backward compatibility, so newer is almost always better.

On macOS you can also use Homebrew:

brew install go

On Linux, the official tarball is usually the safest choice, because distro packages can lag behind:

# replace the version with the latest one from go.dev/dl
wget https://go.dev/dl/go1.24.7.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.24.7.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile

Then open a new terminal and check it worked:

go version
# go version go1.24.7 darwin/arm64 (yours may differ)

If you see a version number, you’re ready.

Editor tip: Install VS Code with the official Go extension, or use GoLand. Both give you autocompletion, inline errors and automatic formatting through gopls, the official Go language server. It makes learning much easier because the editor catches mistakes as you type.

Your first program

Make a folder and initialise a module. A module is just Go’s word for “a project with a name and a list of dependencies”. We’ll cover modules properly in Part 6.

mkdir hello && cd hello
go mod init example.com/hello

That creates a tiny file called go.mod. Now create main.go:

package main

import "fmt"

func main() {
    fmt.Println("Hello, Gopher! 👋")
}

Run it:

go run .
# Hello, Gopher! 👋

That’s a complete Go program. Let’s read it line by line, because every Go file follows this same shape.

  • package main says which package this file belongs to. A package named main is special: it builds into a program you can run, rather than a library.
  • import "fmt" pulls in the fmt package from the standard library. It handles formatted input and output.
  • func main() is the entry point. When your program starts, Go calls main, and when main returns, the program ends.
  • fmt.Println prints a line. Notice it starts with a capital letter. In Go, capitalised names are exported (public). Lowercase names stay private to their package. No public or private keywords needed.

From source code to a binary

go run compiles and runs in one step, which is great while you’re developing. When you want something you can ship, use go build.

From source code to a running program with go run and go build

go build -o hello .
./hello
# Hello, Gopher! 👋

The hello file is a standalone executable. You can copy it to another machine with the same OS and CPU type and it just runs, with nothing else installed.

Want to build for a different platform? Set two environment variables:

GOOS=linux GOARCH=amd64 go build -o hello-linux .
GOOS=windows GOARCH=amd64 go build -o hello.exe .

Cross-compiling is built in. In many languages that’s an afternoon of pain. In Go it’s one line.

A slightly more interesting program

Let’s make something that takes input and does a bit of work, so it feels less like a ritual.

package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {
    name := "stranger"
    if len(os.Args) > 1 {
        name = strings.Join(os.Args[1:], " ")
    }

    greeting := fmt.Sprintf("Hello, %s!", name)
    fmt.Println(greeting)
    fmt.Println("Your greeting has", len(greeting), "characters.")
}
go run . Sahil
# Hello, Sahil!
# Your greeting has 13 characters.

A few new things here:

  • When you import more than one package, you group them in parentheses.
  • os.Args is a list of command-line arguments. The first one is the program itself, so we skip it with os.Args[1:].
  • name := "stranger" declares a variable and gives it a value in one go. Go works out the type for you. We’ll go deep on this in Part 2.
  • fmt.Sprintf formats a string without printing it. %s is a placeholder for a string.

The Go toolbox you get for free

One of my favourite things about Go is that the go command ships with everything you need day to day. No extra installs.

  • go run . compiles and runs your program.
  • go build produces a binary.
  • go fmt ./... formats all your code in the official style.
  • go vet ./... spots suspicious code, like wrong format verbs in Printf.
  • go test ./... runs your tests (Part 10).
  • go mod tidy adds missing dependencies and removes unused ones (Part 6).
  • go doc fmt.Println shows documentation right in your terminal.

The ./... pattern means “this folder and everything under it”. You’ll type it a lot.

Best practices to start with on day one

These habits cost nothing now and save a lot of pain later.

1. Let gofmt decide your style. Go has one official format, and the whole community uses it. Tabs vs spaces, brace placement, alignment: all decided. Set your editor to format on save and never think about it again. As Rob Pike put it, gofmt’s style is no one’s favourite, yet gofmt is everyone’s favourite.

2. Run go vet often. It catches real bugs, not just style issues.

3. Don’t fight the compiler about unused things. Go refuses to compile if you import a package you don’t use or declare a local variable you never read. It feels strict at first. After a week you’ll be grateful, because it keeps code clean.

4. Read the standard library. Go’s standard library is excellent and very readable. Before you reach for a third-party package, check whether net/http, encoding/json, strings or friends already do the job.

5. Use the Go Playground to try ideas quickly in the browser. It’s also great for sharing a snippet when you ask for help.

Common beginner stumbles

  • “declared and not used”: you created a variable and never used it. Use it, or remove it.
  • “imported and not used”: same idea, for imports. Most editors remove these automatically on save.
  • Opening brace on a new line. Go needs func main() { with the brace on the same line. This is because Go inserts semicolons automatically at line ends, and a lone { on the next line would break things.
  • **go: cannot find main module**: you’re running a command outside a folder that has a go.mod. Run go mod init first.

Recap

  • Go was built for fast builds, readable code, easy concurrency and simple deployment.
  • Install it from go.dev/dl and check with go version.
  • Every runnable program has package main and a func main().
  • Capitalised names are exported; lowercase names are private.
  • go run, go build, go fmt, go vet and go test cover most of your daily work.

Try this before the next part: extend the greeting program so it prints the greeting in uppercase too. Hint: look at strings.ToUpper using go doc strings.ToUpper.

In Part 2 we’ll learn the building blocks you’ll use in every Go file: variables, types, if, for, switch and functions, including Go’s habit of returning more than one value.


Where to go from here

➡️ Next: Part 2: Variables, Types, Control Flow, and Functions

The full Go From Zero series

  1. Part 1: Why Go, Setting It Up, and Your First Program (you are here)
  2. Part 2: Variables, Types, Control Flow, and Functions
  3. Part 3: Arrays, Slices, Maps, and Structs
  4. Part 4: Methods, Interfaces, and Composition
  5. Part 5: Error Handling the Go Way
  6. Part 6: Packages, Modules, and Project Layout
  7. Part 7: Goroutines, Channels, and select
  8. Part 8: sync, context, and Concurrency Patterns
  9. Part 9: Generics Without the Headache
  10. Part 10: Testing, Benchmarks, and Fuzzing
  11. Part 11: Best Practices for Production-Ready Go
  12. Part 12: Building a REST API with the Standard Library

If this helped you, share it with a friend who is learning Go, or subscribe to get the next posts by email. Questions are welcome in the comments.

comments powered by Disqus

Releted Posts

Go From Zero, Part 12: Building a REST API with the Standard Library

This is Part 12 of 12 in Go From Zero, a beginner-to-production series on the Go programming language. If you missed it, Part 11: Best Practices for Production-Ready Go is the place to catch up.

Read more

Go From Zero, Part 11: Best Practices for Production-Ready Go

This is Part 11 of 12 in Go From Zero, a beginner-to-production series on the Go programming language. If you missed it, Part 10: Testing, Benchmarks, and Fuzzing is the place to catch up.

Read more

Go From Zero, Part 10: Testing, Benchmarks, and Fuzzing

This is Part 10 of 12 in Go From Zero, a beginner-to-production series on the Go programming language. If you missed it, Part 9: Generics Without the Headache is the place to catch up.

Read more