Go

  • By Language
  • 40 questions

What this round is for

Go interviews concentrate on concurrency and on the handful of things that behave unexpectedly: slices and their backing arrays, nil interfaces, defer in a loop, and goroutines that never finish.

The language is small enough that a round can cover most of it, so depth matters more than breadth. Expect to be asked why a design decision was made, because Go's omissions are deliberate and interviewers like discussing them.

The questions

Answer each one out loud before you open it. An answer you have read is not an answer you can give under pressure, which is the only kind that counts.

Q1 Why does Go have no exceptions?

Show the answer Hide the answer

Because errors are values, returned and checked explicitly, so every failure path is visible in the source.

There is no invisible control flow and no question about which layer catches what. The cost is repetition; the benefit is that ignoring an error is a visible act.

What it is really testing. Whether you can argue the trade rather than just complaining about the repetition.

Q2 How should you add context to an error?

Show the answer Hide the answer

Wrap it with fmt.Errorf and the %w verb, so the chain can still be inspected.

errors.Is and errors.As then let a caller test for a specific error deep in the chain, which covers the cases exceptions would have handled.

What it is really testing. Whether you know the %w verb and why plain formatting loses information.

Q3 What is the difference between errors.Is and errors.As?

Show the answer Hide the answer

Is tests whether a specific error value appears in the chain; As finds an error of a given type and assigns it.

Comparing with == breaks as soon as anything wraps the error, which is why these exist.

What it is really testing. Whether you know why == on errors is fragile once anything wraps them.

Q4 What is a goroutine?

Show the answer Hide the answer

A function running concurrently, scheduled by the Go runtime onto a small number of operating system threads.

It starts with a tiny stack that grows as needed, so hundreds of thousands are ordinary where threads would exhaust memory.

What it is really testing. Whether you know it is not an operating system thread, and what follows.

Q5 What is a channel and what does the slogan about sharing mean?

Show the answer Hide the answer

A typed pipe between goroutines - share memory by communicating rather than communicating by sharing memory.

Instead of several goroutines locking a shared structure, one owns the data and others send it messages.

What it is really testing. Whether you can state the ownership idea rather than just describing a pipe.

Q6 What is the difference between a buffered and an unbuffered channel?

Show the answer Hide the answer

An unbuffered send blocks until a receiver is ready; a buffered one blocks only when the buffer is full.

Unbuffered channels synchronise the two goroutines, which is sometimes what you want and sometimes an unexpected deadlock.

What it is really testing. Whether you know unbuffered sends synchronise both sides.

Q7 What does closing a channel do, and who should close it?

Show the answer Hide the answer

It signals no more values; the sender closes, never the receiver.

Closing twice panics, and sending on a closed channel panics. Receiving from a closed channel returns the zero value immediately.

What it is really testing. Whether you know the panics - closing twice and sending after close.

Q8 What does select do?

Show the answer Hide the answer

It waits on several channel operations and proceeds with whichever is ready.

It is what makes timeouts, cancellation and fan-in natural to express rather than something assembled from primitives.

What it is really testing. Whether you reach for it for timeouts rather than hand-rolling one.

Q9 What is a goroutine leak and how does one happen?

Show the answer Hide the answer

A goroutine blocked forever on a channel nobody will use again.

They do not appear in any error, they accumulate, and the usual cause is a function returning early while a goroutine still waits to send.

What it is really testing. Whether you have looked for one. They are silent and accumulate.

Q10 What does the race detector do?

Show the answer Hide the answer

It instruments the program to report concurrent unsynchronised access to the same memory.

It only finds races on code paths that actually execute, so it is only as good as the test coverage you run under it.

What it is really testing. Whether you know it only finds races on paths that actually run.

Q11 What is a slice, really?

Show the answer Hide the answer

A header with a pointer, a length and a capacity, viewing an underlying array.

Passing a slice copies the header, not the data, which is why a function can modify the elements the caller sees.

What it is really testing. Whether you can describe the three-field header rather than calling it an array.

Q12 What surprising thing can append do?

Show the answer Hide the answer

If capacity allows it writes into the existing array; if not it allocates a new one and copies.

Whether an earlier slice sees the change therefore depends on capacity, which is invisible at the call site.

What it is really testing. Whether you know the behaviour depends on capacity, which is invisible.

Q13 How can a slice cause a memory leak?

Show the answer Hide the answer

Holding a small slice of a very large array keeps the whole array alive.

The fix is copying the part you need into a new slice so the large backing array can be collected.

What it is really testing. Whether you know the backing array stays alive. It is a real production leak.

Q14 What does defer do and when are its arguments evaluated?

Show the answer Hide the answer

It schedules a call for when the function returns; its arguments are evaluated immediately.

That catches people who defer a call reading a value they expect to change before the function ends.

What it is really testing. Whether you know arguments are evaluated at the defer, not at the call.

Q15 Why is defer inside a loop a problem?

Show the answer Hide the answer

Deferred calls run at function exit, not at the end of each iteration.

A loop opening a thousand files accumulates a thousand open handles. Putting the body in its own function fixes it.

What it is really testing. Whether you know deferred calls do not run per iteration.

Q16 What is context.Context for?

Show the answer Hide the answer

Carrying a cancellation signal, a deadline and request-scoped values through a call chain.

When a client disconnects, everything holding the context can stop, instead of continuing work nobody is waiting for.

What it is really testing. Whether you know it exists to stop work nobody is waiting for.

Q17 What are the conventions around Context?

Show the answer Hide the answer

First parameter, never stored in a struct, never nil, and values only for request-scoped metadata.

Using its values to pass optional arguments makes the function signature a lie, which is a tempting shortcut.

What it is really testing. Whether you know values are for metadata, not optional arguments.

Q18 How do interfaces work in Go?

Show the answer Hide the answer

A type satisfies an interface implicitly by having the right methods, with no declaration.

That lets you define an interface describing what your code needs, and existing types satisfy it without modification.

What it is really testing. Whether you know satisfaction is implicit and what that enables.

Q19 Why are Go interfaces usually small?

Show the answer Hide the answer

Because a one-method interface is easy to satisfy, easy to fake in a test, and composes well.

The guidance is to accept interfaces and return concrete types, so callers are not locked into an abstraction they did not ask for.

What it is really testing. Whether you know the accept-interfaces-return-structs guidance.

Q20 What is the empty interface and what replaced it?

Show the answer Hide the answer

interface{} accepts anything; the any alias and generics replaced most of its uses.

Before generics it was the only way to write a container over arbitrary types, at the cost of type assertions everywhere.

What it is really testing. Whether you know generics replaced most of its uses.

Q21 What is a nil interface versus an interface holding a nil pointer?

Show the answer Hide the answer

An interface has a type and a value; it is only nil when both are.

Returning a nil pointer as an error makes the interface non-nil, so err != nil is true with nothing wrong - a notorious trap.

What it is really testing. Whether you know the nil-pointer-in-interface trap. It catches almost everyone once.

Q22 How does Go do code reuse without inheritance?

Show the answer Hide the answer

Struct embedding promotes the embedded type's methods to the outer type.

It looks like inheritance but is delegation - there is no overriding and no virtual dispatch through a hierarchy.

What it is really testing. Whether you know embedding is delegation rather than inheritance.

Q23 What is the zero value and why does it matter?

Show the answer Hide the answer

Every type has a usable default - zero, empty string, nil - and variables are always initialised.

Designing types whose zero value is useful means callers need no constructor, which is why sync.Mutex works without initialisation.

What it is really testing. Whether you design types whose zero value is already usable.

Q24 What is the difference between a value receiver and a pointer receiver?

Show the answer Hide the answer

A value receiver gets a copy; a pointer receiver can modify the original.

Mixing the two on one type is confusing and affects which method set satisfies an interface, so consistency matters.

What it is really testing. Whether you know it affects which method set satisfies an interface.

Q25 What does the Go formatter configure?

Show the answer Hide the answer

Nothing - gofmt has no options, so every Go codebase looks the same.

Style debates are not resolved, they are prevented, and the time that would have gone into them is simply not spent.

What it is really testing. Whether you see unconfigurable formatting as a feature rather than a restriction.

Q26 What do go vet and the linters catch?

Show the answer Hide the answer

Mistakes that compile but are almost certainly wrong - bad format verbs, unused results, lock copying.

Copying a struct containing a mutex is a particularly nasty one, because the copy silently protects nothing.

What it is really testing. Whether you know about lock copying, which silently protects nothing.

Q27 How do Go modules handle dependencies?

Show the answer Hide the answer

go.mod declares requirements and go.sum records cryptographic hashes of the exact versions.

Minimal version selection picks the lowest version satisfying all requirements, which makes builds reproducible without a separate lock step.

What it is really testing. Whether you know minimal version selection differs from a lock file.

Q28 What does it mean that Go produces a static binary?

Show the answer Hide the answer

The output includes the runtime and dependencies, so deployment is copying one file.

For containers that means an almost empty image, which is a genuine operational advantage rather than a stylistic one.

What it is really testing. Whether you connect it to container image size and deployment.

Q29 How do generics in Go differ from other languages?

Show the answer Hide the answer

They are deliberately constrained - no specialisation, no higher-kinded types, no operator constraints.

The design prioritised comprehensibility and compile speed, and the culture treats clever abstraction as a cost to justify.

What it is really testing. Whether you know the design was deliberately limited and why.

Q30 What is a type constraint?

Show the answer Hide the answer

An interface describing what a type parameter must support, including type sets.

comparable is the built-in constraint for types usable with == , which is what a generic map key needs.

What it is really testing. Whether you know comparable exists and what needs it.

Q31 How does Go handle testing?

Show the answer Hide the answer

A test file with functions taking testing.T, run by the built-in tool with no external dependency.

Table-driven tests are the idiom, and the tool also runs benchmarks, examples that are compiled, and coverage.

What it is really testing. Whether you know testing needs no external dependency.

Q32 What is a table-driven test?

Show the answer Hide the answer

A slice of cases iterated by one test function, usually with subtests.

It makes adding a case one line rather than one function, and subtests let a single case be run in isolation.

What it is really testing. Whether you use subtests so one case can be run alone.

Q33 How do you benchmark Go code?

Show the answer Hide the answer

A function taking testing.B that loops b.N times, run by the test tool.

The framework chooses the iteration count, so you must not assume it. Reporting allocations alongside time is usually more informative.

What it is really testing. Whether you know b.N is chosen by the framework.

Q34 What does the garbage collector optimise for?

Show the answer Hide the answer

Low pause times rather than maximum throughput.

Pauses are typically sub-millisecond, which is why Go suits network services but is not suitable for hard real-time work.

What it is really testing. Whether you know it optimises for latency rather than throughput.

Q35 How do you share state safely between goroutines?

Show the answer Hide the answer

A channel, or a mutex, or the atomic package - in roughly that order of preference.

sync.Map exists for a narrow case of heavy concurrent reads; a plain map with a mutex is usually clearer and fast enough.

What it is really testing. Whether you have an ordering rather than reaching for sync.Map by default.

Q36 What does sync.WaitGroup do?

Show the answer Hide the answer

It waits for a known number of goroutines to finish.

Add must be called before starting the goroutine, not inside it, or Wait can return before the goroutine is counted.

What it is really testing. Whether you know Add must happen before the goroutine starts.

Q37 What is sync.Once for?

Show the answer Hide the answer

Guaranteeing an initialisation runs exactly once, even under concurrent access.

It replaces the double-checked locking pattern that is easy to get subtly wrong by hand.

What it is really testing. Whether you know it replaces a pattern that is easy to get wrong.

Q38 Why does Go not have a ternary operator?

Show the answer Hide the answer

Deliberate omission - the language prefers an explicit if even at the cost of extra lines.

It is a small example of the general philosophy: fewer ways to express a thing, so unfamiliar code reads predictably.

What it is really testing. Whether you can explain the philosophy rather than treating it as a gap.

Q39 What is the difference between a panic and an error?

Show the answer Hide the answer

An error is an expected failure returned to the caller; a panic unwinds the stack and should be rare.

recover can stop a panic at a boundary, which libraries use to avoid taking down a caller, but using panic for control flow is not idiomatic.

What it is really testing. Whether you know panic is not the error mechanism in Go.

Q40 Where is Go a good fit and where is it not?

Show the answer Hide the answer

Strong for network services, CLIs and infrastructure; weaker where the domain wants rich abstraction.

The garbage collector rules out hard real-time, and the deliberately small vocabulary can feel verbose for complex business rules.

What it is really testing. Whether you can name a weakness honestly rather than advocating uncritically.

Two things worth doing in the room

  • When asked about error handling, acknowledge the repetition and then say what it buys. A defensive answer suggests you have not thought about the trade.
  • For any concurrency question, mention how the goroutine ends. Starting one is easy; the interesting part is cancellation and cleanup.