Go: Systems Programming Language for Cloud Infrastructure

Goroutines and Concurrency Model

Go's killer feature: goroutines (lightweight threads). Creating 1000s of goroutines trivial: for i := 0; i < 10000; i++ { go myFunction() } spawns 10K concurrent tasks (costs ~2KB per goroutine, vs 2MB per OS thread). Context switching: scheduler multiplexes goroutines onto CPU cores (N goroutines on 4 cores = efficient). Example: HTTP server handles 10K simultaneous clients (each client = goroutine, not thread). Java equivalent requires thread pool (10K threads = 20GB memory, impractical). Channels: typed communication between goroutines. Example: ch := make(chan string), go func() { ch <- "hello" }(), msg := <-ch (receive). Buffered channels: ch := make(chan int, 100) (buffer 100 items before blocking). Channel patterns: fan-out (one producer, multiple consumers), fan-in (multiple producers, one consumer), worker pools (fixed number of goroutines process work). Select statement: multiplexes channels (similar to epoll). Example: select { case msg := <-ch1: ..., case msg := <-ch2: ..., case <-timeout: ... } handles multiple channels/timeout.

Implicit Interfaces and Duck Typing

Go interfaces: implicit implementation (no "implements" keyword). interface Reader { Read(p []byte) (n int, err error) }. Any struct with Read method implements Reader (no declaration needed). Benefit: decoupled code (multiple types implement same interface without modification). Example: io.Writer interface { Write(p []byte) (n int, err error) }. Files, buffers, network sockets all implement Writer. Function: func WriteData(w io.Writer, data []byte) works with all types implementing Writer. Polymorphism without inheritance: func (f *File) Write(...) and func (b *bytes.Buffer) Write(...) both satisfy io.Writer. Composition over inheritance: type Server struct { Logger io.Writer, Handler http.Handler }. Embedding: type Logger struct { *log.Logger } embeds Logger methods directly (method promotion). Empty interface: interface{} matches any type (like any in TypeScript). Type assertion: i.(string) extracts string if i is string, panics otherwise. Type switch: switch v := i.(type) { case string: ..., case int: ... } handles multiple types.

Defer, Error Handling, and Resource Management

Defer: defer statement schedules function execution when surrounding function returns. Example: file, err := os.Open("data.txt"), defer file.Close() ensures file closes (even if panic). Multiple defers: stack order (last in, first out). Use case: defer mutex.Unlock() after defer mutex.Lock() (LIFO ensures unlock happens after lock). Error handling: Go uses explicit error return values (no try-catch). Example: func getData() (string, error) { ... return data, nil or return "", err }. Caller checks: data, err := getData(), if err != nil { log.Fatal(err) }. Error wrapping: fmt.Errorf("failed to read %s: %w", filename, err) (w verb preserves error chain). Error interface: type error interface { Error() string }. Custom errors: type MyError struct { Code int }, func (e MyError) Error() string { return fmt.Sprintf("error: %d", e.Code) }. Sentinel errors: var ErrNotFound = errors.New("not found") (static error for comparison). Resource management: file/connection errors close-on-defer automatically (prevents leaks).

Type System and Method Receivers

Structs: type Person struct { Name string, Age int }. Methods: receiver specifies which type method belongs to. func (p *Person) Greet() string { return "Hi, " + p.Name }. Pointer receiver (*Person): allows mutation. Value receiver (Person): read-only copy. Embedded types: type Employee struct { Person, Title string }. Embedded fields accessible directly: emp.Name (not emp.Person.Name). Method resolution: if both Person and Employee have Walk(), Employee's Walk() called (method shadowing). Slices: []int (dynamic array). append(slice, item) extends (reallocates if needed). Make: s := make([]int, 10, 20) creates slice, length 10, capacity 20 (preallocate storage). Maps: map[string]int used as hash tables. v := m["key"] returns 0 if missing (zero value). ok pattern: v, ok := m["key"] (v = value, ok = true if exists). Range: for i, v := range slice { ... } iterates (i = index, v = value).

Build System and Cross-Compilation

Go compilation: go build produces static binary (single executable, no runtime dependencies). Example: go build main.go → main (executable, ~10MB typical). No dependency shipping: native C binaries require DLLs/SOs. Static compilation simplifies deployment (no "dll hell", docker image ~20MB vs 200MB). Cross-compilation: GOOS=windows GOARCH=amd64 go build → Windows executable on Linux (no toolchain needed). CGO disabled by default (speeds compilation, enables cross-compilation). Performance: compiled Go vs interpreted languages (Python 10x faster, Java comparable). Startup time: 10-100ms (single binary, no VM startup). Memory: Go program ~50MB base (simple http server), Python with dependencies ~200MB. Build cache: incremental builds reuse compiled packages (rebuild <1 second for small changes). Vendoring: go mod vendor copies dependencies into vendor/ folder (reproducible builds, offline compilation).

Standard Library and Ecosystem

Standard library: net/http (web servers), encoding/json (JSON parsing), database/sql (DB abstraction), os/exec (shell commands), time/duration (scheduling), crypto/* (hashing, encryption). Third-party packages: github.com/user/package. go get downloads/manages dependencies (go.mod file tracks versions). Frequently used: gin (HTTP framework, 10x faster routing than net/http), sqlc (type-safe SQL), grpc (protocol buffers, high-performance RPC). Performance libraries: fasthttp (faster than net/http, reduces allocations), msgpack (binary serialization, 5x faster than JSON). Monitoring: pprof (CPU profiling, identify bottlenecks), trace (execution timeline). Example: go test -cpuprofile=cpu.prof (generates profile), go tool pprof cpu.prof (analyzes). Testing: *testing.T parameter (standard library, simple assert-free style). Benchmarking: func BenchmarkSort(b *testing.B) { for i := 0; i < b.N; i++ { sort.Ints(data) } } measures performance.

Production Best Practices

Graceful shutdown: signal.Notify(sigChan, os.Interrupt), <-sigChan triggers cleanup (close listener, drain in-flight requests). Timeout: ctx, cancel := context.WithTimeout(...), http.Request uses ctx (cancels if timeout exceeded). Circuit breakers: after N failures, fail fast (don't retry immediately). Retry logic: exponential backoff (1s → 2s → 4s). Health checks: /healthz endpoint returns 200 if healthy (k8s uses for liveness probes). Logging: structured logs (JSON, key-value pairs). Example: log.WithFields(log.Fields{"user_id": 123, "action": "login"}). Metrics: Prometheus metrics exported (/metrics endpoint). Counter (requests), gauge (memory), histogram (latency distribution). Observability: tracing spans track request journey (across services). Deployment: single binary → easy containerization (small Dockerfile, copy binary only). Blue-green deployment: two Kubernetes deployments, switch traffic between versions. Canary: gradually shift traffic (5% new version, 95% old). Rollback: instant (traffic returns to old version).