Skip to content

Go β€” Programming Language Guide

Last reviewed: 2026-09-17

Go (Golang) is a statically typed, compiled programming language designed at Google for building efficient, reliable software at scale. It is known for a small language surface, first-class concurrency, fast compilation, and a standard library rich enough to build network services with no third-party dependencies.


Overview

Go emphasises:

  • Simplicity β€” a small language specification, few keywords, one canonical formatting style
  • Concurrency β€” goroutines and channels (CSP model)
  • Fast compilation β€” compiles to a single static binary
  • Built-in tooling β€” go fmt, go test, go mod, go vet
  • Standard library β€” HTTP server, JSON, crypto, templates, testing

Topics Covered

  • Packages, modules and import paths
  • Variables, structs, interfaces and type composition
  • Errors as values, defer, panic/recover
  • Goroutines, channels, sync and context
  • HTTP clients and servers with net/http
  • Reading OS-level information with gopsutil
  • Message queues and Kafka with kafka-go
  • Testing, benchmarking and the race detector
  • Cross-compilation and static binaries

Toolchain Commands

go mod init example.com/diskutils     # start a module (creates go.mod)
go get github.com/shirou/gopsutil/v3  # add a dependency
go mod tidy                           # drop unused, fetch missing deps
go build ./...                        # compile every package
go run main.go                        # compile and run
go test ./...                         # run tests
go test -race ./...                   # run tests with the race detector
go test -bench=. -benchmem ./...      # run benchmarks
go vet ./...                          # static checks
gofmt -l -w .                         # format (or: go fmt ./...)
go install example.com/tool@latest    # install a binary into $(go env GOPATH)/bin
go env GOPATH GOMODCACHE GOOS GOARCH  # inspect the toolchain environment

# Cross-compile a static Linux binary from any host
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app ./cmd/app

Errors, defer and Multiple Returns

Go has no exceptions: functions return (value, error) and the caller decides what to do.

package main

import (
    "fmt"
    "os"
)

func readConfig(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("open config: %w", err)
    }
    defer f.Close() // runs when readConfig returns

    buf := make([]byte, 4096)
    n, err := f.Read(buf)
    if err != nil {
        return nil, err
    }
    return buf[:n], nil
}

// Call site: unwrap and report, or return the error upward.
if _, err := readConfig("config.yaml"); err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}
  • %w wraps the underlying error so callers can unwrap it with errors.Is / errors.As.
  • defer runs LIFO at function exit β€” use it to close files, unlock mutexes and cancel contexts.
  • Reserve panic/recover for programmer errors, not for ordinary control flow.

Goroutines and Channels

package main

import (
    "fmt"
    "sync"
)

func worker(jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for j := range jobs {
        results <- j * j
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go worker(jobs, results, &wg)
    }

    for i := 1; i <= 9; i++ {
        jobs <- i
    }
    close(jobs)
    wg.Wait()
    close(results)

    for r := range results {
        fmt.Println(r)
    }
}
  • go f() starts a goroutine; the runtime multiplexes goroutines onto OS threads.
  • Channels are typed and optionally buffered (make(chan int, 100)); they carry data and synchronisation.
  • sync.WaitGroup waits for a set of goroutines; context.Context propagates cancellation and deadlines.
  • The race detector (go test -race) is the cheapest way to find concurrent access bugs.

Reading System Information with gopsutil

gopsutil exposes OS-level metrics (disk, CPU, memory, processes, network) from pure Go. This example lists every mounted partition:

package main

import (
    "fmt"

    "github.com/shirou/gopsutil/v3/disk"
)

func main() {
    partitions, err := disk.Partitions(true)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    if len(partitions) == 0 {
        fmt.Println("No disks found")
        return
    }
    for _, p := range partitions {
        fmt.Printf("device=%s mountpoint=%s fstype=%s\n", p.Device, p.Mountpoint, p.Fstype)
    }
}

Patterns worth reusing:

  • Third-party packages are pulled in with go get and pinned in go.mod / go.sum.
  • Idiomatic Go returns errors instead of panicking β€” check err on every call.
  • Related helpers exist for usage statistics (disk.Usage("/")), CPU (cpu.Percent), memory (mem.VirtualMemory) and processes (process.Processes).
  • Combined with net/http, the same data can be exposed as a small JSON endpoint for monitoring.

Kafka Messaging with kafka-go

segmentio/kafka-go is a pure-Go Kafka client β€” no cgo, no librdkafka:

// Producing
w := &kafka.Writer{
    Addr:         kafka.TCP("localhost:9092"),
    Topic:        "orders",
    Balancer:     &kafka.LeastBytes{},
    BatchTimeout: 10 * time.Millisecond,
    RequiredAcks: kafka.RequireAll,
}
defer w.Close()
w.WriteMessages(ctx, kafka.Message{Key: []byte("order-1"), Value: []byte(`{"id":"order-1"}`)})

// Consuming with a consumer group
r := kafka.NewReader(kafka.ReaderConfig{Brokers: brokers, Topic: "orders", GroupID: "order-workers"})
defer r.Close()
for {
    m, err := r.ReadMessage(ctx)
    if err != nil {
        break // context cancelled, partition closed, broker unavailable
    }
    fmt.Printf("key=%s value=%s\n", m.Key, m.Value)
}

Notes:

  • Message keys determine partition assignment β€” the same key always lands on the same partition, preserving per-entity ordering.
  • Protocol Buffers are a common payload format for strongly typed messages (protoc --go_out=. order.proto).
  • Writers batch by size and time (BatchSize / BatchTimeout); consumer groups give at-least-once delivery with automatic rebalancing across instances.

Key Go Concepts

Concept Description
Packages & modules package main, go mod init, import paths
Multiple returns func div(a, b int) (int, error)
Goroutines go funcName() β€” lightweight threads
Channels ch := make(chan int) β€” typed communication pipes
Interfaces Implicit implementation β€” "duck typing" at compile time
Structs Type composition over inheritance
Error handling Explicit error returns, no exceptions
defer Scheduled cleanup (close file, unlock mutex)
context Cancellation, deadlines, request-scoped values

Resources


To-do

Status: incomplete. Still to write:

  • Write the 'HTTP clients and servers with net/http' section promised in Topics Covered β€” no net/http example exists in the article (it is only mentioned in passing under gopsutil).
  • Expand 'Variables, structs, interfaces and type composition' beyond the one-line table rows β€” interfaces are promised in Topics Covered but only named.
  • Show sync/context in use: cancellation and deadlines are promised alongside goroutines but appear only as prose bullets, with no context example.