Golang Interview Questions: 8 Answers & Concurrency Exercises

Practice Golang interview questions on slices, interfaces, channels, and cancellation with runnable examples, verified outputs, wrong answers, and follow-ups.

EmmaEmmaArticle Editing & Website Operations
Oct 24, 2026Multiple EmployersSoftware Engineer
Editorial cover titled Golang Interview Questions with a laptop code editor, channel and goroutine whiteboard sketch, and abstract concurrency paths

These eight Golang interview questions help backend and platform candidates explain slices, interfaces, channel ownership, cancellation, and errors. Predict each result before reading the answer, then practice the follow-up aloud. Two short scenarios cover dependency failures and shutdown. This is focused practice, rather than an exhaustive Go question bank.

Verification — October 24, 2026: The complete examples were executed locally with Go 1.26.4 on Windows amd64 using an AI-assisted tool workflow; Emma is the article editor. Outputs marked Verified run were observed in that environment. With cgo enabled and GCC 16.2.0 / MinGW-w64 14.0.0, four worker cancellation and completion tests each passed 20 runs under the race detector. A deliberately racy control program triggered a warning. This checks the exercised paths, not every possible interleaving. The exercises are original teaching practice, not employer questions. Language explanations were checked against the Go specification, pipeline guide, and race detector documentation.

SkillCopilotAI offers the optional practice tool mentioned below. The cover is an AI-generated editorial illustration, not a product screenshot.

To reproduce an example, save its complete Go code as main.go and run go run main.go. Run each example separately, since each declares its own main function. The worker pool's job order and count vary with scheduling.

Quick answer map

QuestionCommon trapOne-line answer
Q1 Slice append“Slices always copy”Shared backing array + spare capacity → append can mutate the other variable
Q2 Interface nil“Nil pointer in interface is nil”Typed nil keeps a type word; c == nil is false
Q3 Who closes“Any reader may close”Only the sending side (or one coordinator after all senders finish) closes
Q6 go test -race“Clean once means no races”Reports races only on paths that ran; evidence, not a proof
Q7 Defer args“Everything is evaluated at exit”Arguments are evaluated when defer is registered; closures see later mutations

What Go interviews usually probe

AreaWhat “good” sounds like
Language trapsSlice aliasing, nil maps, typed-nil interfaces, defer argument timing
ConcurrencyGoroutine lifecycle, channel ownership, select, context cancel, races
Correctness under failureTimeouts, partial work, idempotent retries (design talk)
TestingTable-driven tests, go test -race, when a clean race run is not proof

If you only memorize “goroutines are lightweight threads,” you will stall on the first follow-up. Practice exit conditions.

Part 1 — Core Golang interview questions

Q1. After this append, what is in a?

package main

import "fmt"

func main() {
	a := []int{1, 2, 3}
	b := a[:2]
	b = append(b, 99)
	fmt.Printf("a=%v b=%v\n", a, b)
}

Strong answer: b is a slice header into the same backing array as a, with length 2 and capacity 3. append still fits in capacity, so it writes 99 into index 2 of the shared array. Both a and b show 99.

Verified run (2026-10-24, go run, Go 1.26.4, Windows amd64):

a=[1 2 99] b=[1 2 99]

Wrong answer: “Slices always copy, so a stays [1 2 3].” That confuses array value semantics with slice headers.

Follow-up: “How do you force a copy so append cannot mutate a?”
Expect: a full copy (append([]int(nil), a[:2]...)) or a full-slice expression that sets capacity equal to length (a[:2:2]) before append.

Self-check cue: Say “header + backing array + capacity,” not just “reference type.”

Q2. Is this interface value nil?

package main

import "fmt"

type Closer interface{ Close() error }

type File struct{}

func (f *File) Close() error { return nil }

func main() {
	var f *File = nil
	var c Closer = f
	fmt.Printf("c==nil? %v (type=%T)\n", c == nil, c)
}

Strong answer: f is a nil *File. Assigning it to Closer stores a typed pair (type=*File, value=nil). The interface value itself is not nil, because its type word is set. Calling c.Close() can still be safe if the method tolerates a nil receiver; comparing c == nil is false.

Verified run (2026-10-24, go run, Go 1.26.4, Windows amd64):

c==nil? false (type=*main.File)

Wrong answer: “Any nil pointer assigned to an interface stays nil.” That classic trap breaks error returns like return err when err is a typed nil.

Follow-up: “How would you return a real nil error from a function that uses a concrete error pointer type?”
Expect: var err error = nil / bare return nil, not return (*MyError)(nil).

Q3. Who should close this channel?

package main

import "fmt"

func main() {
	ch := make(chan string, 2)
	go func() {
		defer close(ch) // sender closes
		ch <- "ok"
		ch <- "done"
	}()
	for v := range ch {
		fmt.Println(v)
	}
}

Strong answer: Only the sending side (or a single coordinator after all senders finish) should close. Receivers range until close. Closing twice, closing a nil channel, or sending on a closed channel panics.

Verified run (2026-10-24, go run, Go 1.26.4, Windows amd64):

ok
done

Wrong answer: “Any goroutine can close when it is done reading.” That races and panics under load.

Follow-up: “Two producers write to one channel. Who closes?”
Expect: a WaitGroup (or errgroup) among producers, then one closer; or a separate merger stage.

Part 2 — Golang concurrency interview questions

Q4. Design a tiny worker pool that respects cancellation

Prompt: Two workers pull jobs from a channel. A 50ms deadline should request cancellation of the pool—even if jobs remain.

The worker must observe cancellation while waiting for a job as well as during work. This runnable example also makes producer sends cancelable, so an unbuffered job channel cannot leave the producer blocked after workers exit.

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

func worker(ctx context.Context, jobs <-chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		case j, ok := <-jobs:
			if !ok || ctx.Err() != nil {
				return
			}
			timer := time.NewTimer(30 * time.Millisecond)
			select {
			case <-ctx.Done():
				timer.Stop()
				return
			case <-timer.C:
				if ctx.Err() != nil {
					return
				}
				fmt.Println("done job", j)
			}
		}
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()
	jobs := make(chan int)
	var wg sync.WaitGroup
	for w := 0; w < 2; w++ {
		wg.Add(1)
		go worker(ctx, jobs, &wg)
	}
produce:
	for i := 1; i <= 5; i++ {
		select {
		case <-ctx.Done():
			break produce
		case jobs <- i:
		}
	}
	close(jobs)
	wg.Wait()
	fmt.Printf("ctx.Err=%v\n", ctx.Err())
}

What interviewers listen for:

  • Cancellation is cooperative — workers must select on ctx.Done().
  • The job channel still has a clear owner (producer closes).
  • You can explain leftover jobs: canceled work is acceptable if the API documents it.

Evidence note: Job lines such as done job N are illustrative only—count and order depend on scheduling, so they are not a Verified run. The ctx.Err line below was observed once in the local test environment (Go 1.26.4, Windows amd64, 2026-10-24) after the 50ms deadline; treat it as a representative cancellation outcome, not a fixed job transcript.

Example cancellation outcome: Workers may finish jobs 1 and 2 before cancellation; timing and order vary. The deadline requests cooperative shutdown rather than guaranteeing that every goroutine stops at exactly 50ms. When multiple select cases are ready, cancellation has no automatic priority.

ctx.Err=context deadline exceeded

Real blocking I/O must receive the context too; a timer cannot interrupt an arbitrary function. See the official pipeline cancellation guide for ownership and exit-path reasoning.

Race-tested scope: The local tests covered cancellation while waiting on an open, empty channel; cancellation after receiving a job; an already closed channel; and completing buffered jobs normally. Each passed 20 repetitions with go test -race -count=20. No race was reported on those executions; that result is not a proof of correctness for untested paths or modified versions.

Wrong answer: “I spawn a goroutine per request with no limit and ignore context.” That leaks work and invites thundering herds.

Follow-up: “Buffered vs unbuffered jobs—what changes under backpressure?”
Expect: unbuffered blocks the producer on a busy worker (natural backpressure); large buffers hide overload until memory or latency blows up.

Q5. Mutex or channel for a shared counter?

Strong answer: For a single independent counter, consider atomic or a small Mutex; benchmark if performance matters. Use a channel when you are transferring ownership of data or coordinating stages (pipeline, fan-in). “Share memory by communicating” is a design preference, not a ban on locks.

Wrong answer: “Never use mutexes in Go.” You will fight the race detector and the standard library.

Follow-up: “Show how WaitGroup misuse causes a panic.”
Expect: an extra Done can panic with a negative counter. Calling Add inside a newly started goroutine can instead let Wait return before that goroutine registers; this does not necessarily panic.

Q6. What does go test -race actually guarantee?

Strong answer: It instruments memory accesses and reports races on paths that ran. A clean run is evidence, not a proof. Pair it with tests that force concurrent writers and readers.

Wrong answer: “If -race is clean once, the package cannot race.”

Follow-up: “How do you reproduce a flaky race locally?”
Expect: more -count, stress tests, and sometimes GOMAXPROCS variation—not only re-running the happy path.

Part 3 — Errors, defer, and API-shaped questions

Q7. Wrap, errors.Is, and when to panic

Strong answer: Return error for expected failure. Wrap with %w to keep the chain. Use errors.Is / errors.As at boundaries. Reserve panic for “this program is wrong,” and recover only at a supervisor edge (HTTP server, worker loop)—not sprinkled through business logic.

Wrong answer: “Panic instead of returning errors; it is cleaner.”

Follow-up: “Defer: are arguments evaluated now or at function exit?”
Expect: arguments evaluated when defer is registered; closures see later mutations.

package main

import "fmt"

func demonstrateDefer() {
	n := 1
	defer fmt.Println("argument:", n) // captures 1 now
	defer func() { fmt.Println("closure:", n) }()
	n = 2
}

func main() {
	demonstrateDefer()
}

Verified run (2026-10-24, go run, Go 1.26.4, Windows amd64; LIFO order):

closure: 2
argument: 1

Q8. Interface design for testability

Strong answer: Define small interfaces in the consumer package, for example type UserStore interface { Get(ctx context.Context, id string) (User, error) }. Return concrete types from constructors. This keeps mocks tiny and avoids “enterprise” interface bloat.

Wrong answer: “Export a 20-method interface in the producer package for every struct.”

Follow-up: “Value vs pointer receivers—when does a value fail to satisfy an interface?”
Expect: pointer-receiver methods are not in the method set of T, only *T.

Part 4 — Mini scenarios (oral and whiteboard)

Scenario A — Slow dependency

“Your handler fans out to three services. One hangs. What do you do?”

Outline answer (say it in this order):

  1. Derive a child context with a deadline from the request context and pass it to every outbound call.
  2. Set HTTP/client timeouts that are shorter than the parent deadline so you fail before the handler itself times out.
  3. Prefer errgroup.WithContext (or equivalent) so one hard failure cancels siblings instead of leaking goroutines.
  4. Label each dependency required vs optional, and state fail-open vs fail-closed before coding.
  5. Log which dependency canceled the group and whether partial results are returned.

Wrong answer: “Wait forever; the load balancer will save us.”

Scenario B — Shutdown

“Kubernetes sends SIGTERM. What must stop?”

Outline answer (say it in this order):

  1. Stop admitting new work (http.Server.Shutdown or equivalent) while finishing in-flight requests up to a budget.
  2. Cancel the root worker context so pools exit cooperatively on ctx.Done().
  3. Decide drain vs abandon for leftover jobs from the SLA—not from hope.
  4. Stop admitting jobs and coordinate all senders finishing before the single channel owner closes jobs. If workers exit through cancellation, blocked producer sends must also observe cancellation; do not close a channel while another goroutine can still send.
  5. Keep the shutdown timeout at least as long as the longest acceptable request, and verify no goroutine is blocked on an unbuffered send after cancel.

Self-check before the interview

Say these out loud without notes:

  1. Why can append mutate another slice variable?
  2. Why is a typed-nil interface not nil?
  3. Who closes a channel when there are two senders?
  4. Name three cooperative cancel points in a worker loop.
  5. What does a clean go test -race not prove?
  6. When would you pick Mutex over a channel?
  7. How do you keep an HTTP client from ignoring the request deadline?

If any answer is fuzzy, re-run the matching snippet and explain the output—or rehearse the same questions in a timed mock.

Frequently asked questions

Are these real company interview questions?

No. They are original teaching exercises built around common Go language and concurrency traps. Use them to practice explanation quality, not to memorize a leaked bank.

Is a clean go test -race enough?

No. It only reports races on paths that executed (see Q6 above). Combine it with concurrent tests and stress runs when the design shares memory.

Should I search “Go” or “Golang” interview questions?

Go is the language name; “Golang” is a common search term for it. The same slice, interface, and concurrency concepts apply to both queries.

Can I use an AI assistant during a live interview?

Only if your employer’s rules allow it. Assistants are useful for practice explanations beforehand. Using unauthorized help in a graded assessment can violate policy.

Continue preparing

For platform-style timed coding screens, see the HackerRank OA preparation guide and the CodeSignal OA guide. For choosing practice tools ethically before interview day, see best AI interview practice tools.

Official Go references

Ready to ace your next technical interview?

Join thousands of candidates who landed their dream jobs with SkillCopilotAI.