In this article
  1. Slices and arrays
  2. Maps
  3. Interfaces and errors
  4. Concurrency
  5. Strings and defer
  6. Follow-up questions
  7. Sources

Slices and arrays

What is a slice in Go, and how is it different from an array?

A slice is a descriptor of an array segment: it consists of a pointer to the array, the length of the segment and its capacity (Go blog, Go Slices, accessed October 2026). An array is the storage itself. A slice is a small header that refers to storage, which is why passing a slice to a function copies the header but not the elements. The method for whole coding rounds is in the coding interview guide.

Why it is asked: it shows whether you know a slice is not an array. Read the full explanation: Go Slices: usage and internals.

What is the difference between the length and the capacity of a slice?

The length is the number of elements the slice refers to. The capacity is the number of elements in the underlying array, beginning at the element the slice points to (Go blog, Go Slices, accessed October 2026). The capacity is the maximum value the length can reach. With s := make([]byte, 5) both are 5; after s = s[2:4] the length is 2 and the capacity is 3, and s = s[:cap(s)] makes the length 3.1

Why it is asked: reslicing questions are a standard way to test this model. Read the full explanation: The mechanics of append.

What does make do for a slice?

The make function takes a type, a length and an optional capacity. When the capacity is omitted it defaults to the length (Go blog, Go Slices, accessed October 2026). So make([]int, 3) has length and capacity three, while make([]int, 0, 10) is empty with room to grow, which avoids reallocation when you know roughly how many items will arrive.

Why it is asked: it checks that you can pre-size a slice deliberately. Read the full explanation: Go Slices: usage and internals.

Do two slices of the same array share memory?

Yes. Slicing does not copy elements, so two slices cut from one array see the same storage. In the Go blog example, d := []byte{'r','o','a','d'}, then e := d[2:] and e[1] = 'm' leaves d equal to "roam" (Go blog, Go Slices, accessed October 2026). Here e starts at index 2 with length 2 and capacity 2, while d has length 4 and capacity 4. Copy with copy or a fresh append when you need independence.

Two slices cut from one array share its storageFlow of two slice headers into one array of four bytes: d starts at index 0 with length 4 and capacity 4, e starts at index 2 with length 2 and capacity 2, and writing m through e turns road into roam.Slice dindex 0, len 4,cap 4Underlyingarrayr o a d; e[1]='m'gives r o a mSlice eindex 2, len 2,cap 2Diagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/golang-coding-interview-questions.html
Two slices cut from one array share its storage, so a write through one is visible through the other.CC BY 4.0 — source: Golang coding interview questions on slices, maps, errors, channels and defer
Text description of the diagram
  • A slice is a descriptor of an array segment.
  • Slice e starts at index 2 with length 2 and capacity 2.

Why it is asked: aliasing bugs are the classic Go slice trap. Read the full explanation: Go Slices: usage and internals.

What happens when you grow a slice past its capacity?

Reslicing beyond the capacity steps past the array and triggers a panic (Go blog, The mechanics of append, accessed October 2026); a slice cannot be grown beyond its capacity by reslicing. The append function is different: when the capacity is exceeded it allocates a new, larger array, copies the elements and returns a slice pointing at it, so the result must be assigned back: s = append(s, x). The exact growth factor is a runtime detail; do not quote one in an interview.

Why it is asked: forgetting to assign the result of append is a common bug. Read the full explanation: The mechanics of append.

What is the difference between a nil slice and an empty slice?

A nil slice is the zero value of the slice header: length 0, capacity 0 and a nil pointer (Go blog, The mechanics of append, accessed October 2026). A slice such as array[0:0] has length zero but a pointer that is not nil, so it is not a nil slice. Both have length 0, and append works on either, but s == nil is true only for the first.1 The literal []int{} is also non-nil, and encoding/json marshals a nil slice as null and an empty slice as [].

Why it is asked: it tests whether you know the header, not only the length. Read the full explanation: The mechanics of append.

Are slices passed by reference in Go?

No. In Go "the parameters of the call are passed by value to the function" (Go specification, Calls, accessed October 2026), and a slice argument is a copy of the header: pointer, length and capacity. The copy still points at the same array, so a function can change elements the caller sees, but appending inside the function changes only the function's own header unless it returns the new slice.

Why it is asked: "by reference" is the usual wrong answer. Read the full explanation: Go Slices: usage and internals.

Maps

Why does Go map iteration order change?

When you range over a map, the iteration order is not specified and is not promised to be the same from one iteration to the next (Go blog, Go maps in action, accessed October 2026). The runtime randomises it on purpose, so that programs do not come to rely on an order. Never depend on it, including in tests. If you need a stable order, maintain a separate data structure that specifies it.

Why it is asked: output that depends on map order is a common flaky-test cause. Read the full explanation: Go maps in action.

How do you print a map in sorted key order?

fmt.Println(m) already prints a map with its keys sorted, since Go 1.12: "Maps are now printed in key-sorted order to ease testing" (Go 1.12 release notes). When you must process entries in order, collect the keys into a slice, sort it with sort.Strings or slices.Sorted(maps.Keys(m)) (Go 1.23), then loop over the slice and look each key up. This is the "separate data structure" the Go blog recommends.

Why it is asked: it combines maps, slices and sorting in one small task. Read the full explanation: Go maps in action.

What happens when you read from or write to a nil map?

"A nil map is equivalent to an empty map except that no elements may be added" (Go specification, Map types, accessed October 2026). Reading returns the zero value, len is 0, ranging gives zero iterations and delete is a no-op. Assigning to an element of a nil map causes a run-time panic (Index expressions), so create the map with make(map[string]int) or a literal first.

Why it is asked: an uninitialised map field in a struct is a frequent crash. Read the full explanation: Go maps in action.

How do you count and group values with a map?

To count, write counts[k]++, since a missing key reads as zero. To group, append to the slice stored under the key: m[k] = append(m[k], v). Appending to a nil slice just allocates a new slice, so there is no need to check whether the key exists (Go blog, Go maps in action, accessed October 2026). Grouping the illustrative words go, map, slice, chan and defer by length gives groups of sizes 1, 1, 1 and 2 for lengths 2, 3, 4 and 5.1 Python programmers will recognise the pattern from undefined.

Why it is asked: counting and grouping are the core of many array and string problems, such as subarray sum equals k, which keeps a map of prefix-sum counts. Read the full explanation: Go maps in action.

Knowing it is half the job. Saying it is the other half.

Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.

Start Free Trial →

Interfaces and errors

How does Go represent errors?

Go programs express error state with error values. A function usually returns an error as its last result, a nil error denotes success and a non-nil error denotes failure, and calling code should handle errors by testing whether the error equals nil (A Tour of Go, Errors, accessed October 2026). The error type is a built-in interface with one method, Error() string.

Why it is asked: idiomatic error handling is the first thing a reviewer reads. Read the full explanation: A Tour of Go: Errors.

Why is my nil error value not equal to nil?

Under the covers, an interface value has two elements, a type T and a value V, and it is nil only if both are unset (Go FAQ, accessed October 2026). Storing a nil pointer of type *int inside an interface gives (T=*int, V=nil), which is not a nil interface. A nil interface is an interface value whose type and value are both unset. So a function that returns a nil *MyError as error makes the caller's err != nil check true. The fix is to return an explicit nil.

Why it is asked: it is the best-known Go gotcha and separates users from learners. Read the full explanation: Go FAQ.

Can a method be called on a nil value inside an interface?

Yes. An interface value that holds a nil concrete value is itself non-nil, and if the concrete value inside is nil, the method is called with a nil receiver (A Tour of Go, accessed October 2026). A well-written method checks for the nil receiver and handles it, rather than panicking on a field access.

Why it is asked: it follows from the nil-error question and checks the same model. Read the full explanation: A Tour of Go: interface values with nil underlying values.

Concurrency

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

A goroutine is a function running concurrently with others, and a channel is a typed pipe goroutines use to send values to each other. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and a receiver are ready. Otherwise it is buffered, and sends succeed without blocking while the buffer is not full, receives while it is not empty (Go specification, Channel types, accessed October 2026).

Why it is asked: it shows whether you can reason about blocking and deadlocks; concurrency is not parallelism, so talk about blocking, not speed. Read the full explanation: Go specification, Channel types.

What does "share memory by communicating" mean, and how do you find a data race?

The proverb is "Do not communicate by sharing memory; instead, share memory by communicating" (Effective Go, Concurrency). A data race is a write to a memory location concurrent with another read or write of that location, unless all accesses are atomic (Go memory model). The race detector, run as go test -race ./..., reports races only when running code actually triggers them (Go race detector article). Mutexes are the other tool.

Why it is asked: it tests whether you can name the tool and its limit, not just the rule. Read the full explanation: Effective Go, Concurrency.

How do you wait for goroutines to finish?

Use a sync.WaitGroup: call Add before starting each goroutine, have each goroutine call Done when it finishes, usually with defer, and call Wait in the caller to block until the counter returns to zero (package sync, accessed October 2026). Call Add before the go statement, not inside the goroutine, or Wait may return early. Do not copy a WaitGroup after first use; pass a pointer.

Why it is asked: forgetting to wait is the commonest goroutine bug. Read the full explanation: package sync.

Who closes a channel, and what does range do on one?

Only the sender should close a channel, to tell receivers no more values are coming. A for v := range ch loop receives values until the channel is closed and drained. Sending to or closing a closed channel causes a run-time panic, and so does closing a nil channel (Go specification, Close, accessed October 2026). A receiver that closes the channel risks a sender panicking.

Why it is asked: it separates people who have written producer-consumer code from those who have read about it. Read the full explanation: Go specification, Close.

What does select do, and how do you add a timeout?

A select statement waits on several send or receive operations. If one or more can proceed, a single one is chosen by a uniform pseudo-random selection; with a default case it never blocks, and without one it waits until a case is ready (Go specification, Select statements, accessed October 2026). For a timeout, add a case that receives from time.After(d) or from a context's Done channel.

Why it is asked: it tests whether you can avoid goroutines that block forever. Read the full explanation: Go specification, Select statements.

Does a goroutine started in a loop capture the loop variable?

Since Go 1.22 each iteration of the loop creates new variables, so go func() { use(i) }() sees its own i (Go 1.22 release notes, accessed October 2026). Before that all iterations shared one variable, so goroutines often saw the final value, and the usual fix was to copy it with i := i. The new behaviour applies to modules whose go.mod declares Go 1.22 or later.

Why it is asked: old answers online still describe the pre-1.22 trap. Read the full explanation: Go 1.22 release notes.

Strings and defer

Why does len of a Go string not count characters?

A Go string is a sequence of bytes, so len counts bytes, while range over a string yields runes decoded from UTF-8 (Go blog, Strings, bytes, runes and characters, accessed October 2026). A rune is Go's name for one Unicode code point. The string "héllo" has length 6 but five runes, because é takes two bytes.1 To reverse a Unicode string, convert it to []rune, reverse that slice and convert back.

Why it is asked: string reversal is a staple, and the byte-versus-rune trap is the point. Read the full explanation: Go blog, Strings.

In what order do deferred calls run?

Deferred calls run last-in, first-out after the surrounding function returns, and a deferred call's arguments are evaluated when the defer statement runs, not when the call runs (Go blog, Defer, Panic, and Recover, accessed October 2026). So a loop that defers fmt.Print(i) for i from 0 to 2 prints 210.1 Defer is used for cleanup such as closing a file or unlocking a mutex.

Why it is asked: it checks that you can trace a snippet exactly. Read the full explanation: Go blog, Defer, Panic, and Recover.

Follow-up questions

What are the common Golang coding interview questions?

The twenty-two above: slices and their header, maps and their order, nil handling, interface and error semantics, channels and select, strings and defer. Many Go rounds also use language-neutral problems such as the longest substring without repeating characters or merge intervals, where the Go-specific part is knowing slices and maps well. The method for those rounds is in the coding interview guide.

What does a Golang coding interview question look like?

Typically a short task in Go: transform a slice, count or group with a map, or predict what a snippet prints. A strong answer says the slice or map behaviour aloud, writes the error check, and tests an empty input, which for Go includes a nil slice and a nil map.

How do I practise Golang for a coding interview?

Run each snippet on this page in the Go playground or locally and change one thing at a time: the capacity, the key, the receiver. Then solve the standard problems in Go, not another language. If you also prepare in another language, compare with Python interview questions to see which behaviours differ.

Key takeaways
  • A slice is a header of pointer, length and capacity; slices of one array share storage.
  • Assign the result of append; reslicing beyond capacity panics.
  • Map iteration order is unspecified; sort the keys when you need order.
  • A nil *T stored in an error is a non-nil error: return a literal nil.
  • Reads of a nil map are safe, writes panic; the race detector reports data races that your tests actually trigger.

Sources

  1. Go Slices: usage and internals (go.dev blog) — The Go Programming Language, 26 Oct 2026
  2. Arrays, slices (and strings): The mechanics of 'append' (go.dev blog) — The Go Programming Language, 26 Oct 2026
  3. Go maps in action (go.dev blog) — The Go Programming Language, 26 Oct 2026
  4. Frequently Asked Questions: why is my nil error value not equal to nil? (go.dev) — The Go Programming Language, 26 Oct 2026
  5. A Tour of Go: Errors (go.dev) — The Go Programming Language, 26 Oct 2026
  6. A Tour of Go: Interface values with nil underlying values (go.dev) — The Go Programming Language, 26 Oct 2026
  7. The Go Programming Language Specification — The Go Programming Language, 26 Oct 2026
  8. Effective Go: Concurrency — The Go Programming Language, 26 Oct 2026
  9. The Go Memory Model — The Go Programming Language, 26 Oct 2026
  10. Data Race Detector (go.dev) — The Go Programming Language, 26 Oct 2026
  11. Package sync: WaitGroup (pkg.go.dev) — The Go Programming Language, 26 Oct 2026
  12. Strings, bytes, runes and characters in Go (go.dev blog) — The Go Programming Language, 26 Oct 2026
  13. Defer, Panic, and Recover (go.dev blog) — The Go Programming Language, 26 Oct 2026
  14. Go 1.22 Release Notes — The Go Programming Language, 26 Oct 2026
  15. Go 1.12 Release Notes — The Go Programming Language, 26 Oct 2026
  16. Go 1.23 Release Notes — The Go Programming Language, 26 Oct 2026