Go (Golang) is one of the fastest-growing backend languages in India's tech ecosystem. Companies like Google India, Razorpay, CRED, Swiggy, and a growing number of fintech and cloud-native startups use Go for high-performance backend services. This guide covers the complete Go developer interview preparation path for 2026.
Go concurrency: goroutines, channels, and select
Go concurrency interview questions:
1. 'What is a goroutine and how does it differ from a thread?' A goroutine is a lightweight concurrent function managed by the Go runtime. Launched with 'go f()'. Goroutines start with a 2KB stack (grows dynamically). You can run millions of goroutines; they are multiplexed onto OS threads by the Go scheduler. OS threads are heavier (1-8MB stack, OS-managed context switch).
2. 'What is the difference between a buffered and unbuffered channel?' Unbuffered channel: 'ch := make(chan int)'. The sender blocks until a receiver reads (synchronous rendez-vous). Buffered channel: 'ch := make(chan int, 5)'. The sender blocks only when the buffer is full (up to 5 items can sit without a receiver). Use unbuffered for synchronisation; buffered for producer/consumer with known throughput.
3. 'What does the select statement do in Go?' select waits on multiple channel operations simultaneously and executes the first case that is ready. With a default case, select is non-blocking (immediately executes default if no channel is ready). Common uses: fan-in (receive from multiple channels), timeout ('case <-time.After(5 * time.Second)'), cancellation ('case <-ctx.Done()').
4. 'What is a sync.WaitGroup and when would you use it?' WaitGroup waits for a collection of goroutines to finish. 'wg.Add(N)' before launching N goroutines; 'defer wg.Done()' in each goroutine; 'wg.Wait()' blocks until all Done() calls are received. Use when you need to wait for all goroutines to complete before proceeding.
5. 'What is sync.Mutex and when should you prefer it over channels?' Mutex guards shared state (a counter, a map, a cache). Prefer channels when you are passing data between goroutines (goroutines communicate by sharing memory vs share memory by communicating). Prefer Mutex when multiple goroutines need read/write access to the same data structure. RWMutex: allows multiple concurrent readers (RLock/RUnlock) but exclusive writers (Lock/Unlock).
Go interfaces, error handling, and common patterns
Go interfaces and error handling interview questions:
1. 'How do interfaces work in Go?' Interfaces are satisfied implicitly: any type that implements all methods in an interface satisfies it with no explicit declaration. 'type Stringer interface { String() string }'. The empty interface 'interface{}' (or 'any' in Go 1.18+) accepts any value. Interface values hold (type, value) pairs; a nil interface is not the same as an interface holding a nil pointer.
2. 'How do you do type assertion and type switch in Go?' Type assertion: 'v, ok := i.(ConcreteType)'. If ok is false, the assertion failed; without ok, the program panics. Type switch: 'switch v := i.(type) { case string: ... case int: ... default: ... }'. Use type switches to branch on the dynamic type of an interface value.
3. 'How does Go handle errors?' Errors are values implementing 'type error interface { Error() string }'. Convention: return error as the last return value; caller checks 'if err != nil'. Wrapping: 'fmt.Errorf("context: %w", err)' wraps an error (use %w not %v to allow unwrapping). Unwrapping: 'errors.Is(err, target)' checks if any error in the chain is target; 'errors.As(err, &target)' unwraps and type-asserts. Sentinel errors: package-level 'var ErrNotFound = errors.New("not found")' for comparison with errors.Is.
4. 'What is defer in Go?' Deferred calls run in LIFO order when the surrounding function returns (including on panic). Common uses: close files ('defer f.Close()'), unlock mutexes ('defer mu.Unlock()'), recover from panics ('defer func() { if r := recover(); r != nil { ... } }()'). Gotcha: defer captures variables by reference, not by value; use a local copy inside the defer if you need the value at the time defer was called.
Go backend system design and common interview questions
Common Go backend interview questions:
1. 'What is the difference between a slice and an array in Go?' Array: fixed length, value type (entire array is copied on assignment). Slice: dynamic length, reference type backed by a contiguous segment of an underlying array; 'make([]int, len, cap)'. append() may reallocate when capacity is exceeded; the returned slice must be used. Three components of a slice: pointer to underlying array, length, capacity.
2. 'How do you write a goroutine-safe cache in Go?' Option 1: 'sync.RWMutex' protecting a map (multiple readers, one writer). Option 2: 'sync.Map' for maps with concurrent reads and writes (keys are not known in advance; workloads with many keys but infrequent writes). Option 3: a dedicated goroutine serialising all reads and writes via channels (cleaner ownership but adds latency per operation).
3. 'How does Go's HTTP server handle concurrency?' 'net/http' starts a new goroutine for each incoming request automatically. The handler function runs concurrently with other requests; shared state must be protected with mutexes or atomic operations.
4. 'What is Go's module system?' 'go.mod' declares the module path and its dependencies with semantic version pinning. 'go.sum' has cryptographic checksums of all dependency versions (prevents tampering). 'go get module@version' adds/upgrades. 'go mod tidy' removes unused dependencies and adds missing ones. Replace directive: 'replace module => ./local/path' for local development with an unreleased fork.
5. 'What is the Go garbage collector?' Concurrent tri-color mark-and-sweep GC. Runs concurrently with the application (most of the time); brief stop-the-world pauses for stack scanning. GOGC environment variable controls GC frequency (default 100: GC triggers when heap doubles). 'runtime/pprof' and 'go tool pprof' for profiling memory allocations and GC pressure.
Frequently asked questions
Explore more