Go — Theory
Go — Theory (interview deep-dive)
Section titled “Go — Theory (interview deep-dive)”Goroutine scheduler — GMP model
Section titled “Goroutine scheduler — GMP model”- G = goroutine (user-space).
- M = OS thread (machine).
- P = processor (logical context, holds runnable goroutine queue). Count =
GOMAXPROCS(default = CPU count). - M:N scheduling — many G’s mapped onto few M’s via P.
- Each P has local run queue; global queue for overflow. Work-stealing between P’s.
- Goroutine starts ~2KB stack, grows by copying.
- Preemption: cooperative + async since 1.14 (signal-based at safe points). Long tight loops can starve scheduler pre-1.14.
Channels — semantics
Section titled “Channels — semantics”- Unbuffered (
make(chan T)): rendezvous — sender blocks until receiver ready (and vice versa). - Buffered (
make(chan T, n)): send blocks when full, receive blocks when empty. - Close: signals “no more values”. Receive from closed channel returns zero value +
ok=false. - Don’t close from receiver side. Close from single sender or via dedicated done channel.
- Panic: send on closed channel, double close.
Memory model & race detection
Section titled “Memory model & race detection”- Happens-before relationships: channel send happens before corresponding receive completes; mutex unlock happens before subsequent lock; goroutine start happens after
go f()line; goroutine completion has no general happens-before to caller (use sync). - Run with
-raceflag in test/dev to detect data races. - Atomic ops in
sync/atomicprovide ordering guarantees.
When to use what
Section titled “When to use what”| Need | Tool |
|---|---|
| protect map/slice | sync.Mutex |
| read-heavy shared state | sync.RWMutex |
| count / flag | sync/atomic |
| communicate work | channel |
| wait for goroutines | sync.WaitGroup |
| run-once init | sync.Once |
| bounded parallelism | semaphore via buffered chan or golang.org/x/sync/semaphore |
| temporary objects (pool) | sync.Pool |
Channels vs mutex
Section titled “Channels vs mutex”- Channel: when ownership transfers (data passed between goroutines), or when signaling. “Don’t communicate by sharing memory; share memory by communicating.”
- Mutex: when protecting access to shared state in place. Often simpler/faster for counters and caches.
- Atomics are orders of magnitude faster than channels for simple counters.
Common interview questions
Section titled “Common interview questions”- Goroutine leak scenarios: blocked send on full channel, blocked receive on never-closed channel, missing context cancellation. Always have an exit path.
- Why is
nilchannel useful? Receive/send on nil blocks forever — use inselectto disable a case dynamically. for rangeon channel stops when channel is closed.- Pointer vs value receiver consistency: don’t mix on same type. If any method needs pointer, all should be pointer.
- Interface holding nil pointer: classic gotcha —
var err *MyErr = nil; var e error = err; e != nilistrue. Interface stores (type, value); only nil-interface has both nil. - Slice append surprises:
append(s, x)may or may not modify underlying array depending on capacity. Always reassign:s = append(s, x). deferevaluation: args evaluated immediately, function runs at return. LIFO order.selectwith default: non-blocking send/receive. Without default, blocks until any case ready.
- Concurrent, tri-color mark-sweep with write barriers.
- Goal: low latency (<1ms STW typically). Tunable via
GOGC(% of live heap that triggers next GC; default 100). runtime/debug.SetMemoryLimit(1.19+) for soft cap.
Performance
Section titled “Performance”- Escape analysis: compiler decides heap vs stack.
go build -gcflags='-m'shows decisions. - Avoid unnecessary allocs: reuse buffers,
sync.Poolfor hot paths, string builder, preallocate slices. pproffor CPU/mem/goroutine/block/mutex profiles.- Benchmark:
go test -bench=. -benchmem.
Error handling philosophy
Section titled “Error handling philosophy”- Errors are values. Compose via wrapping (
%w). - Sentinel errors (
var ErrNotFound = errors.New("not found")) checked witherrors.Is. - Custom error types checked with
errors.As. - Don’t ignore errors —
_ = erris a smell.
Common patterns
Section titled “Common patterns”- Worker pool: N goroutines reading jobs from chan, sending results to result chan.
- Fan-out/fan-in: distribute work, merge results.
- Pipeline: stages connected by channels. Use context for cancellation.
- Rate limiting:
time.Tickorgolang.org/x/time/rate.Limiter. - Singleflight (
golang.org/x/sync/singleflight): dedupe concurrent identical calls. - errgroup (
golang.org/x/sync/errgroup): goroutines with cancellation on first error.
Testing
Section titled “Testing”testingstdlib.*testing.Tfor tests. Subtests viat.Run.- Table-driven tests idiomatic.
httptestfor HTTP handlers.testcontainers-gofor real DBs.- Race detector:
go test -race. - Fuzzing:
go test -fuzz=Fuzz(1.18+).