Go (Golang) has become a primary language at a growing number of Indian product companies: Zerodha (almost exclusively Go), Razorpay (Go for payment-critical services), Flipkart, Swiggy, and several Indian fintech startups. If you are applying to these companies, knowing Go well is essential. This guide covers the Go concepts most commonly tested in Indian technical interviews, from beginner Go questions to advanced concurrency patterns.
Go fundamentals most tested in Indian interviews
Q: What are the main differences between Go and Java/Python? Go compiles to a single statically linked binary: no JVM, no interpreter. No exceptions (error handling via explicit return values). No class-based inheritance (interfaces + composition). Garbage collected but with low-latency GC. Goroutines (much lighter than OS threads: a Go programme can have millions). Built-in tooling: go fmt, go test, go vet, go mod.
Q: What is a goroutine and how does it differ from a thread? A goroutine is a lightweight execution unit managed by the Go runtime, not the OS. Initial stack size: 2KB (vs OS thread ~1–8MB). The Go scheduler multiplexes goroutines onto OS threads (M:N scheduling) with GOMAXPROCS threads (default: number of CPU cores). Starting a goroutine: `go functionName()`. Very cheap: launch hundreds of thousands concurrently.
Q: What is a channel in Go? A channel is a typed conduit for communication between goroutines. Channels are the idiomatic alternative to shared memory with locks. `ch := make(chan int)` (unbuffered: sender blocks until receiver is ready). `ch := make(chan int, 100)` (buffered: sender blocks only when buffer is full). Closing a channel: `close(ch)`: receiver gets zero value and ok=false after channel is drained.
Q: What is the select statement? Go's `select` blocks until one of multiple channel operations can proceed, then executes that case. If multiple cases are ready, one is chosen at random. `select { case v := <-ch1: ... case ch2 <- x: ... default: ... }`. The default case prevents blocking: runs if no case is immediately ready.
Go interfaces and composition
Q: How do Go interfaces work (implicit implementation)? In Go, a type implements an interface by implementing all its methods: no `implements` keyword. This is implicit satisfaction. Example: any type with a String() string method satisfies the fmt.Stringer interface. Enables duck typing: if a type has the right methods, it fits the interface.
Q: What is the empty interface and when do you use it? `interface{}` (or `any` since Go 1.18) accepts any type. Used when you truly don't know the type at compile time (JSON unmarshalling, fmt.Println). Avoid over-using it: it removes type safety and requires type assertions or type switches at runtime.
Q: What is a type assertion vs a type switch? Type assertion: `v, ok := i.(string)`: checks if interface value i holds a string. If ok is false, v is zero value (safe form). The unsafe form `v := i.(string)` panics if i is not a string. Type switch: `switch v := i.(type) { case string: ... case int: ... }`: checks multiple types cleanly.
Q: What is embedding in Go? Go uses composition over inheritance. You can embed a type inside a struct to 'inherit' its methods. `type AdminUser struct { User; adminLevel int }`: AdminUser has all User's methods promoted to it. Unlike Java inheritance, there is no polymorphism via embedding: only method promotion.
Go concurrency patterns and common pitfalls
Common concurrency patterns in Go interviews:
Worker pool: Fan out work from a channel to N goroutines. Common pattern for parallelising CPU-bound or I/O-bound work without uncontrolled goroutine growth.
Done channel / context cancellation: Signal goroutines to stop using a `done chan struct{}` or context.Context. Critical in production: goroutines that never stop are goroutine leaks.
Sync primitives: `sync.Mutex` for mutual exclusion over shared data. `sync.RWMutex` for read-heavy workloads (multiple concurrent readers, exclusive writers). `sync.WaitGroup` to wait for a group of goroutines to finish. `sync.Once` for one-time initialisation (singleton pattern in Go).
Common interview pitfalls:
- Race condition with a shared variable and goroutines: detect with `go test -race`. Fix with sync.Mutex or atomic operations.
- Goroutine leak: goroutine blocks on a channel that nobody writes to. Fix with context cancellation or buffered channels.
- Loop variable capture: `for i := range items { go func() { fmt.Println(i) }() }`: all goroutines may print the same value. Fix: pass i as an argument to the goroutine function.
- Forgetting to drain a channel before closing: `close(ch)` while a sender is still writing causes a panic.
Frequently asked questions
Practice these questions on HireStepX