Interview: language fundamentals
The questions that open almost every Go interview, and the answer that shows you have actually written Go.
Topic 94 of 98 · 5 min to read · 45 min to build
After this topic you can
- Explain zero values, slices versus arrays, and why
appendreturns a value. - Predict why an interface holding a nil pointer is not equal to nil.
- Explain value versus pointer receivers and what
deferevaluates and when.
These get asked because they separate people who read a tutorial from people who shipped something. In each case the short answer is the first line. Say it, then add the second line only if they want more. Rambling makes you sound unsure.
"Why would you choose Go?"
- Say: one static binary with no runtime to install, concurrency built into the language, fast compiles, and a standard library big enough that most services need almost no dependencies.
- Then the cost: more verbose error handling than exceptions, a small standard set of abstractions, and no built-in dependency injection or ORM, so you write more by hand.
- Do not say: "it is fast". So is everything else they are comparing it to, and it invites a benchmark argument you cannot win.
"What is a zero value, and why does it matter?"
- Every type has a defined default:
0,"",false,nilfor pointers/slices/maps/channels/funcs/interfaces. There is no undefined. - What you get from this:
var b bytes.Buffer,var mu sync.Mutexandvar wg sync.WaitGroupare all usable immediately, with no constructor. Designing your own types so the zero value works removes a whole class of "did you call New?" bugs. - The trap they are usually fishing for: a
nilmap reads fine but panics on write, so a map field in a struct needsmake.
"Slice versus array?"
the answer, in code
// Array: the length is part of the TYPE. [3]int and [4]int are different types.// Passing one copies the whole thing.var a [3]int// Slice: a three-field header — pointer, len, cap — over an array.s := []int{1, 2, 3}"Why does append return a value?"
- Because it may need to allocate a bigger array and copy. When it does, the old header is stale, so
appendreturns the new one and you must writes = append(s, x). - The follow-up: "what happens if two slices share a backing array and both append?" If there is spare capacity, both write into the same slot and one overwrites the other’s element with no error. One slice, one appender.
"How does Go handle errors, and do you like it?"
- An error is an ordinary value returned alongside the result. No exceptions and no hidden jumps: every failure point is visible in the code you are reading.
- Say what you actually do: wrap with
%wand context (fmt.Errorf("get user %d: %w", id, err)), useerrors.Isfor sentinels,errors.Asfor typed errors, and handle each error once: log it or return it, never both. - Admit the cost: it is more lines than try/catch, and a bare
return errsix layers deep gives you a useless message. Naming that scores better than defending it as perfect.
"What is the difference between a value and a pointer receiver?"
- A value receiver gets a copy, so mutation is lost. A pointer receiver can mutate.
- The rule: pointer if you mutate, if the struct is large, or if it contains a
sync.Mutex. If any method needs a pointer, make them all pointers for consistency. - The part that catches people: interface satisfaction uses the strict method set. The method set of
Thas only value-receiver methods;*Thas both. That is whyvar s Shape = User{}fails with "method has pointer receiver" and&User{}works.
"Explain interfaces in Go."
- Implicit satisfaction: any type with the methods qualifies, with no declaration. That is why your type can satisfy an interface written later by someone who never heard of it.
- "The bigger the interface, the weaker the abstraction."
io.Readerhas one method, which is why files, sockets, buffers and HTTP bodies all compose. - Accept interfaces, return structs, and define the interface in the package that *consumes* it, not the one that implements it. That keeps the interface as small as the caller’s need.
- Say this out loud: do not create an interface until there is a second implementation or a test that needs a fake. It signals you have maintained a Go codebase rather than ported Java patterns into one.
The nil interface question — expect it
they will show you this
func doWork() error { var e *MyError = nil return e}if err := doWork(); err != nil { // this DOES run. Why?}"What does defer actually do?"
- Schedules a call for when the function returns, LIFO, on every return path.
- Two things they probe: arguments are evaluated immediately, so
defer fmt.Println(time.Since(start))prints ~0; anddeferinside a loop does not run until the whole function ends. - The bonus point: a deferred closure can modify a named return value. That is how you turn a panic into an error, and how a transaction helper knows whether to roll back.
"When would you panic?"
- Only when the program’s assumptions are broken: a nil dependency at startup, an impossible switch branch,
regexp.MustCompileon a literal. - Never for anything a user can cause: a bad request, a missing row, a network failure. Those are errors.
- The one they like: a panic in a goroutine you started is not caught by a parent recover. It kills the whole process, so every long-lived goroutine needs its own
recover.
The short version
- Lead with one sentence, then stop. Add the cost or the trap only if they want more.
- Zero values,
appendreturning, shared backing arrays, and method sets are the four most-asked mechanics. - The nil-interface trap comes up often. Know both why it happens and the one-line fix.
- Saying "do not add an interface until there is a second implementation" marks you as someone who has maintained Go.
Remember: They are testing whether you have written Go or read about it. Naming the cost of a design decision is what tells them apart.
Build this before moving on
Say each answer above out loud, timed. If any takes more than 30 seconds you are including detail they did not ask for — cut it to the first line plus one.Then try these in your own editor
- 1Write the nil-interface trap from memory, run it, then fix it. You should be able to do this without looking.
- 2Explain value versus pointer receivers to someone non-technical in two sentences.
- 3List three things you dislike about Go and what you do about each. Interviewers ask, and "nothing" is the wrong answer.
Official documentation