# Go (Golang) Skills ## Core Idioms - Accept interfaces, return structs. - Errors are values — check them explicitly. `if err != nil { return err }`. - Don't panic in library code. Reserve panic for truly unrecoverable situations. - Zero values are useful — `var m map[string]int` is nil but `var s []int` is usable. ## Error Handling - Wrap errors with context: `fmt.Errorf("failed to open %s: %w", path, err)`. - Sentinel errors: `var ErrNotFound = errors.New("not found")`. Check with `errors.Is(err, ErrNotFound)`. - Custom error types: `type ValidationError struct { Field, Message string }`. Check with `errors.As()`. - Never ignore errors: `_ = doSomething()` is a code smell. At minimum, log it. ## Concurrency - "Don't communicate by sharing memory; share memory by communicating." — use channels. - `go func()` launches goroutine. Always ensure goroutines terminate (context, done channel). - `sync.WaitGroup` to wait for goroutine completion. - `sync.Mutex` when channels are overkill (protecting a counter, map). - `context.Context` for cancellation, timeouts, and request-scoped values. Always first parameter. - `errgroup.Group` for parallel tasks with error propagation. ## Channel Patterns - `ch := make(chan T)` unbuffered (synchronous). `make(chan T, n)` buffered. - Fan-out: multiple goroutines read from one channel. - Fan-in: multiple channels merged into one via select. - Pipeline: chain of stages connected by channels. - `select` with `case <-ctx.Done():` for cancellation. - Close channels from sender side only. Never close from receiver. ## Interfaces - Interfaces are satisfied implicitly — no `implements` keyword. - Keep interfaces small: `io.Reader` has one method. `io.ReadWriteCloser` composes three. - Define interfaces where they're used, not where they're implemented. - `interface{}` (or `any`) is a code smell — prefer generics or specific interfaces. - Type assertions: `v, ok := i.(ConcreteType)`. Type switch: `switch v := i.(type) { ... }`. ## Generics (Go 1.18+) - `func Map[T, U any](s []T, f func(T) U) []U` — generic function. - Constraints: `comparable`, `~int | ~float64`, custom interface constraints. - Use generics for data structures and utility functions, not business logic. ## Project Structure ``` cmd/ myapp/main.go # entrypoint internal/ # private packages domain/ # business logic, no external deps repository/ # data access handler/ # HTTP handlers pkg/ # public library code ``` - `internal/` enforced by Go compiler — cannot be imported outside module. - One package per directory. Package name = directory name. ## Testing - `func TestFoo(t *testing.T)` — test functions. - Table-driven tests: `tests := []struct{ name string; input int; want int }{ ... }`. - `t.Run(name, func(t *testing.T) { ... })` for subtests. - `t.Parallel()` for concurrent test execution. - `testify/assert` for cleaner assertions. `testify/mock` for mocking. - `httptest.NewServer()` for HTTP integration tests. - Benchmarks: `func BenchmarkFoo(b *testing.B) { for i := 0; i < b.N; i++ { ... } }`. ## HTTP Server - `http.HandlerFunc` wraps functions as handlers. - Middleware pattern: `func Logging(next http.Handler) http.Handler`. - Use `chi` or `echo` for routing. Stdlib `http.ServeMux` improved in Go 1.22. - Always set timeouts: `srv := &http.Server{ReadTimeout: 5*time.Second, WriteTimeout: 10*time.Second}`. - Graceful shutdown: `signal.Notify` + `srv.Shutdown(ctx)`. ## Performance - `pprof` for CPU/memory profiling: `go tool pprof http://localhost:6060/debug/pprof/profile`. - `sync.Pool` for reducing GC pressure on frequently allocated objects. - Pre-allocate slices: `make([]T, 0, expectedLen)`. - String building: `strings.Builder` not `+` concatenation. - Avoid interface boxing in hot paths. ## Common Gotchas - Loop variable capture in goroutines (fixed in Go 1.22, but still common in older code). - Nil interface vs nil pointer: `var p *MyType = nil; var i MyInterface = p; i != nil` is TRUE. - Maps are not safe for concurrent access — use `sync.Map` or `sync.RWMutex`. - Slice append may or may not create a new backing array — never hold stale slice references. - `defer` evaluates arguments immediately, runs function at return. - `init()` runs before `main()` — avoid side effects, prefer explicit initialization.