Go Basics #1 Getting Started and Your First Program

6 min read

This series is a 7-part introductory course for people who are learning Go for the first time, or who already know another language and are coming over to Go.

  • #1 Getting started and your first program ← this post
  • #2 Variables, types, constants
  • #3 Control flow — if/for/switch
  • #4 Functions, multiple return, error type
  • #5 Collections — array/slice/map
  • #6 Structs and methods
  • #7 Packages and modules (go mod)

This post starts with why use Go, then walks you through setup, compilation, and running your first program.

What is Go? #

Go (or Golang) is a compiled language released by Google in 2009. Its design goals are clear.

  • Simple syntax — only 25 keywords, a gentle learning curve
  • Fast compilation and execution — statically compiled, single-binary output
  • Goroutines — a lightweight concurrency model
  • Standard library — rich from the start with HTTP server, JSON, cryptography, etc.

Go shines especially in CLI tools, backend servers, and distributed systems. Infrastructure tools like Docker, Kubernetes, and Terraform are all written in Go.

Who is it a good fit for? #

If any of the following applies, Go is a good fit.

  • You want to build CLI tools — easy to ship as a single binary
  • HTTP server / API — robust servers using just the standard library
  • Systems where concurrency matters — goroutines are the simplest answer
  • You want to move from JavaScript/Python to a faster language — the learning curve is gentle

You don’t get the same low-level control as C/C++, but Go is a sweet spot between systems programming and application programming.

Compared to other languages #

A rough sense of where Go sits.

GoPythonJavaScriptRust
Type systemStatic, inferredDynamicDynamic (TS is static)Static, strong
ExecutionCompiledInterpretedJITCompiled
Learning curveGentleGentlestGentleSteep
Memory managementGCGCGCOwnership / borrow checker
ConcurrencyGoroutinesasyncio/threadingEvent loopasync + threads

Go takes a practical position between simplicity and performance.

Installing Go #

macOS #

Homebrew
brew install go

Linux #

Package manager or direct
# Ubuntu/Debian
sudo apt install golang-go

# Or grab the tarball from the official site
# https://go.dev/dl/

Windows #

Download the MSI installer from go.dev/dl. Or winget install GoLang.Go.

Verify the install #

Check the version
go version
# prints something like: go version go1.22.0 darwin/arm64

This series assumes Go 1.22 or later. We use modern features like ServeMux pattern matching and generics.

Your first program — Hello World #

Create a working folder.

New project
mkdir hello-go
cd hello-go
go mod init hello

go mod init starts a new module. A go.mod file is created. We cover this in detail in #7 Packages and modules.

Create a main.go file with the following.

main.go
package main

import "fmt"

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

Run it.

Run
go run main.go
# Hello, Go!

go run compiles and runs in one step. If you want to produce a separate compiled binary:

Build
go build
./hello
# Hello, Go!

go build produces a binary with the same name as the module (hello). One of Go’s biggest strengths is right here — a single executable file with all dependencies statically linked in.

Walking through the code line by line #

main.go again
package main

import "fmt"

func main() {
	fmt.Println("Hello, Go!")
}
  • package main — this file belongs to the main package. An executable program always starts at the main function in the main package.
  • import "fmt" — imports the fmt package from the standard library. fmt is for I/O formatting.
  • func main() — the entry point of the program. A function with no parameters and no return value.
  • fmt.Println(...) — calls the Println function in the fmt package. Names exposed externally always start with an uppercase letter (a Go rule).

Go’s most distinctive syntax — semicolons and braces #

1) No semicolons #

No semicolons
fmt.Println("a")
fmt.Println("b")

You don’t write the semicolon at the end of a line. The compiler handles it automatically. One thing to watch out for though — the opening brace must be on the same line.

Brace position
// OK
func main() {
	fmt.Println("hi")
}

// ✗ compile error
func main()
{
	fmt.Println("hi")
}

2) Indentation is tabs #

Go’s standard tool gofmt automatically formats your code. Indentation is tabs, alignment follows fixed rules. This series follows gofmt output as is.

3) Auto-formatting — go fmt #

Format
go fmt ./...

Set up your editor to auto-format on save. The Go extension for VS Code handles this automatically.

A quick variable preview #

We cover this in detail in the next post, but just to set the tone.

Variable preview
package main

import "fmt"

func main() {
	name := "Curtis"
	age := 30
	fmt.Printf("Name: %s, Age: %d\n", name, age)
}

:= is shorthand syntax that declares and assigns at the same time. The type is inferred from the right-hand side (here string and int).

Mistakes caught at compile time #

A few places that surprise people new to Go.

1) Unused variables are a compile error #

Unused variable — error
func main() {
	x := 10   // ✗ x declared but not used
	fmt.Println("hi")
}

Other languages might warn, but Go blocks compilation outright. It’s a design that forces you to keep your code tidy.

2) Unused imports are also an error #

Unused import — error
import (
	"fmt"
	"strings"   // ✗ "strings" imported and not used
)

func main() {
	fmt.Println("hi")
}

Same reason. gofmt and goimports clean these up automatically, so with a properly configured editor you barely run into them.

3) Variable shadowing — compiles but risky #

Shadowing — compiles but risky
x := 10
if true {
	x := 20   // a new x — shadows the outer x but compiles
	fmt.Println(x)   // 20
}
fmt.Println(x)   // 10

This isn’t a compile error, but it often causes bugs. Tools (go vet) catch some cases.

Standard tooling — the go command #

Subcommands you’ll use often.

Common go commands
go run main.go         # run
go build               # build
go test ./...          # test (all packages)
go fmt ./...           # format
go vet ./...           # static analysis (checks for suspicious patterns)
go mod tidy            # tidy dependencies
go mod init <name>     # start a new module
go get <package>       # add a dependency

./... means “the current directory and all subdirectories”.

Editor — VS Code + Go extension #

If you install the Go extension in VS Code, you get autocomplete, jump-to-definition, formatting, and linting. The first time you open a Go file, you’ll be prompted to install additional tools — install them all.

JetBrains’ GoLand is also popular, but VS Code alone is more than enough.

Go Playground — try it without setup #

go.dev/play is the official tool for running Go directly in your browser. Useful for trying small examples or sharing code.

Wrapping up #

What this post covered:

  • Go is a language with simple syntax + static compilation + strong concurrency
  • Especially well-suited for CLI tools, HTTP servers, and distributed systems
  • Verify your install with go version. The series assumes 1.22+
  • Start a new project with go mod init
  • go run main.go or go build
  • package main + func main() is the entry point
  • Externally exposed names start with an uppercase letter — Go’s visibility rule
  • Unused variables/imports are compile errors
  • Standard tools like gofmt/go vet come built in

In the next post (#2 Variables, types, constants) we cover Go’s primitive types, the different ways to declare variables, constants, and the iota pattern.

X