Lesson 31 · Senior Go backend engineering
Mock senior Go interview pack
A retrieval-first practice pack for turning the senior lessons into interview-ready answers.
Your win: run a realistic senior Go backend mock interview on yourself, score the answers honestly, and turn weak spots into a targeted revision list instead of vague anxiety.
How to use this pack
Do this out loud. Set a timer. Answer before looking at any notes. The point is not fluency in the moment alone — it is building storage strength by forcing retrieval under pressure.
Format
- Round 1 · Core language & runtime — 6 questions
- Round 2 · Backend architecture & concurrency — 6 questions
- Round 3 · Debugging, performance & operations — 6 questions
- Time target — 60–90 seconds per answer
- Rule — answer from memory first, then grade yourself with the checklist below
Strong answer checklist
A strong answer should usually include: (1) a clear mental model, (2) the key trade-off, and (3) a real backend consequence or example.
Round 1 · Core language & runtime
- Explain value semantics in Go. What is copied, and what only feels reference-like?
- When do you use a pointer receiver, and when is a value receiver better?
- What is the nil-interface gotcha, and why does it bite error handling?
- What is escape analysis, and why should a backend engineer care?
- How would you explain the GMP scheduler to another engineer?
- What do
GOGCandGOMEMLIMITchange?
Round 1 · Strong answer points
- Value semantics. Go passes values by copy. Arrays and structs are copied as values. Slices, maps, channels, functions, and interfaces are also values, but their headers point at shared runtime-managed data, so they feel reference-like. The senior warning is aliasing: copying a slice header does not copy the backing array.
- Pointer vs value receiver. Use a pointer receiver when you need mutation, want to avoid copying a larger struct, want a consistent method set, or nil has meaning. Use a value receiver for small immutable types where copy semantics are clear. The strong answer avoids saying “pointer is always faster.”
- Nil-interface gotcha. An interface is only nil when both its dynamic type and dynamic value are nil. A typed nil pointer stored inside an interface still makes the interface non-nil. That bites error handling because you can accidentally return a non-nil
errorthat wraps a nil concrete pointer. - Escape analysis. The compiler decides whether a value can stay on the stack or must move to the heap because something may outlive the current frame. Senior engineers care because extra heap churn becomes GC work and can matter on hot backend paths.
- GMP scheduler. Goroutines are G, OS threads are M, and logical processors are P. The runtime schedules many goroutines onto a smaller set of threads through available Ps. The useful answer is: goroutines are cheap because stacks are small and scheduling is in user space, but they are not free when you create too many.
GOGCandGOMEMLIMIT.GOGCchanges how much heap growth the runtime tolerates before collecting again — a space-versus-CPU trade-off.GOMEMLIMITis a soft memory budget, especially important in containers, so the GC works harder before the process gets too close to OOM territory.
Round 2 · Backend architecture & concurrency
- When would you use a mutex instead of a channel?
- What is bounded concurrency, and why does it matter?
- Why is
errgroupoften better thanWaitGroup? - How should
context.Contextflow through a backend service? - Why keep transport-specific errors at the edge?
- Describe the architecture style you see in this repo using Go-native terms.
Round 2 · Strong answer points
- Mutex vs channel. Use a mutex when the problem is protecting shared state. Use a channel when the problem is coordinating work or transferring ownership. The senior answer is pragmatic: choose the simpler mental model for the real problem, not the more “Go-looking” one.
- Bounded concurrency. It means putting an explicit cap on how much work may run at once. That matters because large inputs or traffic spikes should not automatically turn into unbounded goroutines, memory growth, and latency collapse.
errgroupoverWaitGroup.WaitGrouponly answers “have the goroutines finished?”errgroupgives you error propagation, shared cancellation, and often concurrency limits. That better matches real backend operations that should fail fast under one parent context.- How context should flow. Context is per-operation state. It should enter at the transport boundary, flow down call chains explicitly, and be used for cancellation, deadlines, and request-scoped metadata. It should not be stored on structs or long-lived objects because that creates lifecycle mismatch bugs.
- Transport errors at the edge. Domain and application code should stay reusable and not know about gRPC status codes or HTTP response details. Controllers/adapters should translate domain failures into transport semantics at the edge.
- Architecture style in this repo. A good answer is: thin transport adapters, application/use-case orchestration in services, consumer-owned interfaces as ports, concrete adapters for repos/clients, and bootstrap/constructor code acting as the composition root. In Go terms, it is explicit wiring with small interfaces rather than framework-heavy magic.
Round 3 · Debugging, performance & operations
- A service is using too much memory. What do you check first?
- How can a tiny slice keep a huge buffer alive?
- When is
sync.Pooluseful, and when is it the wrong tool? - Why can naive retries make an outage worse?
- You suspect a race or goroutine leak. What tools and symptoms do you look for?
- What does “measure, don't guess” mean in practical Go performance work?
Round 3 · Strong answer points
- Too much memory. Start by characterising the growth: steady leak, traffic-correlated burst, or retained buffers. Then inspect heap profiles, allocation profiles, recent hot paths, large caches, subslices, queue growth, and goroutine counts. The senior move is to look at evidence before touching runtime knobs.
- Tiny slice retaining huge buffer. A slice is just a header pointing to an underlying array. If you keep a small subslice of a huge byte buffer, the whole backing array stays live. The fix is often to copy the needed subset into a new slice when retention matters.
sync.Poolright and wrong uses. It helps when measurement shows allocation churn on a hot path and the objects are short-lived and safely reusable. It is the wrong tool for durable caches, shared ownership, or code that becomes much harder to reason about than the saved allocations are worth.- Why naive retries are dangerous. Retries can amplify load during partial outages. If many callers all time out and all retry immediately, they keep pressure on an already unhealthy dependency. Safe retries need limits, backoff, cancellation, and some thinking about idempotency.
- Races and goroutine leaks. Use the race detector for data races. For leaks, inspect goroutine counts, stack dumps,
pprof, blocked channel operations, missing cancellation paths, and long-lived background workers that never exit. Symptoms are rising goroutine count, stuck shutdowns, memory growth, and blocked stacks. - “Measure, don't guess.” Do not optimize from folklore. Benchmark the suspected code, profile CPU and heap behaviour, inspect allocation counts, and only then change code or runtime settings. A senior engineer can name the bottleneck and the expected effect of the change before making it.
Self-scoring rubric
| Score | Meaning |
|---|---|
| 0 | I could not explain it without notes. |
| 1 | I gave fragments, but the answer was incomplete or fuzzy. |
| 2 | I explained the main idea, but missed the trade-off or production consequence. |
| 3 | I gave a strong, clear answer with model + trade-off + backend consequence. |
After-action review
- Which 3 questions felt weakest?
- Was the weakness about terminology, mental model, or real-world consequence?
- Which lesson should you revisit before the next mock round?
Recovery map
Missed value semantics? Go back to Lessons 3–4. Missed context or cancellation? Revisit Lessons 17, 18, and 26. Missed runtime/memory? Revisit Lessons 23–27. Missed architecture answers? Revisit Lessons 6, 12, and 28. Missed retry/backpressure? Revisit Lesson 29.
Q1. The main rule of this mock pack is…
Retrieval first. Peeking too early builds familiarity, not durable recall.
Q2. A score of 3 means…
Strong senior answers are structured, not just technically correct.
What are the three ingredients of a strong senior Go answer?
recall, then click to reveal
A clear mental model, the important trade-off, and the backend or production consequence.
If you want, I can now run this as a live mock interview and grade each of your answers one by one. Ask me.
Sources. This pack synthesizes the course lessons, especially Lessons 3–4, 17–18, and 23–30, into retrieval practice.