The idea in one minute#
Go is a compiled, statically typed, garbage-collected language built for writing servers and tools. The compiler turns your whole program — and a copy of the Go runtime — into one executable file with no dependencies. The runtime is not a virtual machine: it is ordinary compiled code inside your binary that schedules goroutines and manages memory.
Three design choices explain most of Go’s character: simplicity (one obvious way to do things, a language you can hold in your head), fast builds, and concurrency built in.
An analogy#
A food truck. Everything needed to serve — kitchen, fridge, generator — travels as one unit. Park it anywhere and it works. A Go binary is the truck: copy one file to a server and run it. Many other languages are a restaurant that needs the right building, gas line and staff (an interpreter, a VM, a folder of libraries) to exist before the first dish.
A picture#
flowchart TB SRC["main.go + packages"] --> GOC["go build<br/>parse, type-check, optimize"] MOD["go.mod, go.sum<br/>dependencies, pinned"] --> GOC GOC --> OBJ["machine code<br/>for one OS and CPU"] RT["Go runtime<br/>scheduler, allocator, GC"] --> LNK["linker"] OBJ --> LNK LNK --> BIN["one static executable"] BIN -->|"GOOS=linux GOARCH=arm64"| X["cross-compiled<br/>from any machine"] class SRC,MOD neutral class GOC,LNK compute class OBJ,RT memory class BIN,X io
How it really works#
What you get, and what you give up#
| You get | Because | You give up |
|---|---|---|
| One binary, trivial deployment | Static linking, runtime included | Small binaries (a “hello” is ~1.5–2 MB) |
| Fast compile times | Simple grammar, no header files, strict dependency rules | Some compiler cleverness |
| Memory safety | Bounds checks, no pointer arithmetic, garbage collection | Manual control of every allocation |
| Cheap concurrency | Goroutines scheduled by the runtime | Hard real-time guarantees |
| Readable code across teams | gofmt, few features | Expressiveness: no inheritance, no macros, exceptions replaced by error values |
Go is the language of much of today’s infrastructure: Docker, Kubernetes, Prometheus, Terraform, etcd, and — relevant to the rest of this library — NVIDIA’s Kubernetes device plugin and GPU operator, and Ollama.
The toolchain is one command#
| Command | Does |
|---|---|
go run . | Compile and run the package in this directory |
go build | Compile to a binary |
go test ./... | Run tests in this module (-race, -bench, -cover, -fuzz add more) |
go vet ./... | Report suspicious constructs |
gofmt -w . / go fmt ./... | Format — there is one style, and nobody argues about it |
go mod init, go mod tidy | Create a module; sync dependencies |
go get pkg@version | Add or change a dependency |
go doc fmt.Println | Read documentation |
go fix ./... | Rewrite code to use newer language and library features |
go tool pprof, go tool trace | Profile and trace (module V) |
go env | Show the build environment |
Building#
go build -o app . # for this machine
GOOS=linux GOARCH=arm64 go build -o app . # for another OS and CPU: no extra toolchain
go build -gcflags=-m . # print the compiler's escape and inlining decisions (III.02)
go build -race . # with the data race detector (IV.05)
go version -m ./app # which Go and which module versions built this binary
Cross-compilation is a pair of environment variables because the compiler and the standard library are written in Go and carry every target with them. It stops being that easy only when the program uses cgo to call C (V.04).
Releases and compatibility#
- A new release every six months (February and August). The two most recent are supported.
- The Go 1 compatibility promise: a program that compiles with one Go 1.x compiles with
later ones. Behaviour changes are gated by the
goline ingo.mod, so upgrading the toolchain does not silently change old modules. - The
goline ingo.modis the minimum language version; an optionaltoolchainline names the toolchain to use, and thegocommand will download it if needed.
The shape of a program#
package main // an executable is package main...
import "fmt" // ...that imports other packages...
func main() { // ...and starts here.
fmt.Println("hello")
}
Capitalized names (Println) are exported from a package; lower-case names are private to it.
That is the whole visibility system.
Code#
// env.go — what am I, and what did the toolchain build?
package main
import (
"fmt"
"os"
"runtime"
"runtime/debug"
)
func main() {
fmt.Println("Go version ", runtime.Version())
fmt.Println("target ", runtime.GOOS+"/"+runtime.GOARCH)
fmt.Println("compiler ", runtime.Compiler)
fmt.Println("CPUs visible ", runtime.NumCPU())
fmt.Println("GOMAXPROCS ", runtime.GOMAXPROCS(0), "(threads running Go code at once)")
fmt.Println("goroutines ", runtime.NumGoroutine(), "(just main, so far)")
// The runtime is already working before main starts: it has reserved memory.
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("heap in use %d kB\n", m.HeapInuse/1024)
fmt.Printf("obtained from OS %d kB\n", m.Sys/1024)
if exe, err := os.Executable(); err == nil {
if st, err := os.Stat(exe); err == nil {
fmt.Printf("this binary %.1f MB — your code plus the runtime\n", float64(st.Size())/1e6)
}
}
if info, ok := debug.ReadBuildInfo(); ok {
fmt.Println("module ", info.Main.Path)
for _, s := range info.Settings {
if s.Key == "GOARCH" || s.Key == "GOOS" || s.Key == "CGO_ENABLED" {
fmt.Printf(" %-12s %s\n", s.Key, s.Value)
}
}
}
}
Remember this#
- Go compiles to one static binary containing your code and the runtime.
- The runtime is a library in your process, not a virtual machine.
- One tool,
go, builds, tests, formats, vets, profiles and manages dependencies. - New releases keep old code compiling; the
goline ingo.modgates behaviour changes.
Try it#
- Run
env.gowithgo run, thengo buildit and run the binary. Is the reported binary path different? Why? - Cross-compile it for
linux/amd64and forwindows/amd64. Compare the file sizes. - Build with
-ldflags="-s -w"(strip symbol and debug tables). How much smaller is it, and what did you lose?
Check yourself#
- What is the Go runtime, and where does it live?
- Why is cross-compiling a Go program usually trivial?
- What does the
goline ingo.modcontrol?