Go Developer interview questions
100 real questions with model answers and explanations for Junior Go Developer candidates.
See a Go Developer resume example →Practice with flashcards
Spaced repetition · Hunter Pass
Questions
Go's predeclared basic types fall into boolean, numeric, and string categories.
- bool represents true or false values.
- Numeric types include signed and unsigned integers, floating-point numbers, and complex numbers.
- string represents an immutable sequence of bytes.
Why interviewers ask this: These categories describe the fundamental non-composite values available without defining custom types.
Both forms declare variables, but they differ in scope rules and type syntax.
- var works at package or function scope and may include an explicit type, an initializer, or both.
- A var declaration without an explicit type infers it from the initializer, while one without an initializer uses the type's zero value.
- := works only inside functions and always infers the declared variables' types.
- A multi-variable := may reuse variables from the same block only when at least one non-blank variable is new.
Why interviewers ask this: The short form is convenient for local inference, while var supports every declaration scope and explicit typing.
A zero value is the default value assigned when storage is created without an explicit initializer.
- Boolean values default to false, numeric values to 0, and strings to the empty string.
- Pointers, slices, maps, functions, channels, and interfaces default to nil.
- Every element of an array starts at the zero value of its element type.
- Every field of a struct starts at the zero value of its field type.
Why interviewers ask this: Zero values make every declared variable valid for its type even before an explicit assignment.
Go constants may be untyped or explicitly typed, and iota helps generate related constant values.
- An untyped constant keeps an abstract constant kind until a context requires a concrete type.
- A typed constant has the explicit type written in its declaration and must be representable by that type.
- Constant values are fixed at compile time and cannot be changed after declaration.
- Within a const block, iota starts at 0 and increases by 1 for each successive constant specification.
Why interviewers ask this: This model preserves constant precision while allowing concise declarations of typed values and enumerations.
An explicit Go conversion applies the target type as T(value).
- Go does not implicitly convert between different numeric types such as int32 and int64.
- The source value must be convertible to the target type under the language rules.
- Converting a floating-point value to an integer discards its fractional part.
- Narrowing or changing numeric representations can lose range or precision.
Why interviewers ask this: Explicit conversions make changes of representation visible and force the programmer to consider possible data loss.
A Go string stores a read-only sequence of bytes whose contents cannot be changed in place.
- A string may contain arbitrary bytes and is not required to contain valid UTF-8.
- Indexing a string returns the byte at that position.
- Slicing a string selects a byte range and produces another string value.
- Changing text requires constructing a new string, often through a []byte or []rune conversion.
Why interviewers ask this: Immutability prevents direct byte updates while the byte-based representation keeps string storage simple.
byte and rune are predeclared aliases used for two different views of textual or binary data.
- byte is an alias for uint8 and represents one raw byte.
- rune is an alias for int32 and conventionally represents one Unicode code point.
- A single-quoted character literal produces a rune value.
- String indexing works with bytes, while range over a string decodes UTF-8 code points as runes.
Why interviewers ask this: Choosing byte or rune states whether code is operating on raw encoded data or Unicode code points.
For a string, len reports bytes rather than decoded Unicode code points.
- ASCII characters occupy one byte, so byte and rune counts match for ASCII-only strings.
- utf8.RuneCountInString counts the runes produced by UTF-8 decoding.
- Converting to []rune and taking len also counts decoded runes but creates a rune slice.
- A rune count is not always the count of user-perceived characters because one visible symbol may contain multiple code points.
Why interviewers ask this: Separating byte length from rune count avoids incorrect assumptions about variable-width UTF-8 text.
A Go function can declare and return multiple values in a fixed order.
- The function signature lists all result types, and each return supplies matching values.
- A call with multiple results can assign them to the same number of variables.
- The blank identifier _ discards a value without creating a usable binding.
- Every other local variable must be used, so _ can explicitly ignore an unwanted result.
Why interviewers ask this: Multiple results express related outputs directly, while the blank identifier permits intentional omission.
A named type declaration creates a distinct type, while a type alias gives another name to an existing type.
- type UserID int defines UserID as a new named type with int as its underlying type.
- Values of UserID and int generally require an explicit conversion when moved between those types.
- type UserID = int makes UserID identical to int for type identity and assignment.
- An alias does not create a separate type or a separate underlying representation.
Why interviewers ask this: Named types provide distinct type identity, whereas aliases preserve the identity of their target type.
An array has a fixed length in its type, while a slice is a flexible view of an underlying array.
- The length is part of an array type, so [3]int and [4]int are different types.
- Assigning an array copies all of its elements.
- A slice can change its length within its capacity and can grow with append.
- Assigning a slice copies its descriptor, so both slices can refer to the same elements.
Why interviewers ask this: Arrays store their elements directly, whereas slices describe access to elements in a backing array.
A slice is described by a pointer to a backing array, a length, and a capacity.
- The pointer identifies where the slice's elements begin.
- The length is the number of elements currently accessible through the slice.
- The capacity is the number of elements available from the slice start to the end of the backing storage.
- Copying a slice value copies this descriptor rather than the underlying elements.
Why interviewers ask this: The three-part slice descriptor lets Go provide a lightweight view over array storage.
A nil slice has no backing storage reference, while an empty non-nil slice is initialized but contains no elements.
- Both can have length and capacity equal to zero.
- Only the nil slice compares equal to nil.
- append, len, cap, and range work with both kinds of slice.
- make([]T, 0) and []T{} produce empty non-nil slices.
Why interviewers ask this: The distinction matters when code or serialization needs to preserve nil separately from an initialized empty value.
The make function creates an initialized slice with a specified length and an optional capacity.
- make([]T, length) creates a slice whose capacity is at least its length.
- make([]T, length, capacity) sets both values, and capacity cannot be smaller than length.
- All elements within the initial length contain the zero value of T.
- make returns a slice value, not a pointer to a slice.
Why interviewers ask this: Using make prepares backing storage and a slice descriptor in one operation.
append returns a slice containing the original elements followed by the new elements.
- If capacity is sufficient, append can reuse the existing backing array.
- If capacity is insufficient, append allocates new backing storage and copies the elements.
- The returned slice must be used because its pointer, length, or capacity may differ.
- When storage is reused, other slices sharing that array may observe element changes.
Why interviewers ask this: Possible reallocation is why the result of append is normally assigned back to the slice.
A subslice normally shares the same backing array as the original slice.
- Changing a shared element through either slice is visible through the other.
- The subslice length is determined by the selected index range.
- Its capacity usually extends from the new start position to the original capacity limit.
- A full slice expression can set a smaller capacity limit for the subslice.
Why interviewers ask this: Shared backing storage makes slicing inexpensive but allows aliases to affect the same elements.
copy transfers elements from a source slice into an existing destination slice.
- It copies min(len(dst), len(src)) elements.
- It returns the number of elements copied.
- It does not grow the destination slice or change its length.
- It produces the correct result even when the source and destination overlap.
Why interviewers ask this: The copy function is the standard way to duplicate slice elements into allocated destination space.
A map key type must support equality comparison with == and !=.
- Booleans, numbers, strings, pointers, channels, interfaces, arrays, and structs can be keys when their values are comparable.
- A struct is comparable only when all of its fields are comparable.
- Slices, maps, and functions cannot be map keys.
- Each key identifies at most one value in the map.
Why interviewers ask this: Map lookup depends on comparable keys so Go can determine whether two keys are equal.
Reading an absent map key returns the zero value of the map's value type.
- A direct lookup has the form value := m[key].
- The comma-ok form value, ok := m[key] also reports whether the key exists.
- ok is false for an absent key and true for a present key.
- The boolean distinguishes an absent key from a present key storing a zero value.
Why interviewers ask this: The comma-ok form removes ambiguity when a valid stored value can equal the type's zero value.
A nil map can be read but must be initialized before storing entries.
- Reading a nil map returns the value type's zero value.
- Assigning an entry to a nil map causes a runtime panic.
- make(map[K]V) creates an initialized map that accepts assignments.
- delete(m, key) removes a key and is safe for absent keys and nil maps.
Why interviewers ask this: Map initialization is required for writes, while reads and deletions are defined safely for a nil map.
Locked questions
- 21
What is a struct in Go?
structs - 22
How do struct literals work, and what is a struct's zero value?
structs - 23
How does a method differ from a function in Go?
- 24
What does a value receiver mean for a Go method?
- 25
What does a pointer receiver mean for a Go method?
- 26
What automatic address and dereference adjustments can Go make for method calls?
- 27
What are pointers, & and * in Go?
- 28
How are arguments passed in Go, including pointer arguments?
- 29
What are struct embedding and promoted members in Go?
structsnlp - 30
How do uppercase and lowercase identifiers control export in Go?
- 31
How do Go interfaces work, and what does implicit interface satisfaction mean?
typesinterfaces - 32
Why are small interfaces preferred in Go?
typesinterfaces - 33
What is the difference between a nil interface and an interface containing a typed nil value?
typesinterfaces - 34
How does the comma-ok form of a type assertion work in Go?
forms - 35
What is a type switch in Go?
- 36
What is Go's built-in error interface?
typesinterfaces - 37
How do errors.New and fmt.Errorf create errors in Go?
- 38
How do error wrapping with %w and errors.Is work?
error-handling - 39
How do errors.As and custom error types work together?
error-handling - 40
How should Go code check and return errors idiomatically?
- 41
In what order do deferred calls run, and when are their arguments evaluated?
decision-makingerror-handling - 42
What is panic used for in Go?
error-handling - 43
Under what conditions does recover stop a panic?
error-handling - 44
How do packages and imports work, and what is special about package main?
- 45
What does go.mod define, and how do semantic versions affect module import paths?
- 46
What is an init function for, and in what basic order does package initialization occur?
- 47
What is a goroutine, and what does a go statement do?
concurrency - 48
How does an unbuffered channel synchronize goroutines?
concurrency - 49
How do capacity, blocking, and close ownership work for buffered channels?
ownershipcapacityconcurrency - 50
How do comma-ok receives, range over a closed channel, and select work?
concurrency - 51
A function appends to a slice but the caller does not see the new elements. How would you fix it?
slices-maps - 52
This code writes to a nil map and panics. What change would you make?
error-handling - 53
A helper opens a file and sometimes leaks file descriptors. How would you correct it?
descriptors - 54
You must build a list of pointers to values produced in a loop. How would you avoid every pointer referring to reused storage?
- 55
A function accepts an interface and receives a typed nil pointer, but an if value == nil check fails. How would you handle this?
typesinterfaces - 56
A method should update a struct field, but the value remains unchanged after the call. What would you inspect and change?
structs - 57
You need to copy a slice before modifying it so the original remains unchanged. What code would you use?
slices-maps - 58
A goroutine updates a map while an HTTP handler reads it. How would you fix the race?
concurrencyhttp - 59
Several goroutines increment the same request counter and the final value is too low. What would you change?
concurrency - 60
A program starts worker goroutines but exits before they finish. How would you wait for them?
concurrency - 61
A goroutine waits forever to send a result after its caller times out. How would you prevent the leak?
concurrency - 62
A consumer ranges over a channel forever after all jobs are processed. What is missing?
concurrency - 63
Two goroutines can both close the same channel and the program sometimes panics. How would you redesign it?
concurrencyerror-handling - 64
You need to receive a result but stop waiting after 500 milliseconds. How would you write this?
- 65
A select loop has a default case and now consumes a full CPU core while idle. How would you fix it?
- 66
You are implementing a fixed-size concurrent job processor. How would you keep it from starting one goroutine per job?
concurrency - 67
A pipeline stage stops early on error while an upstream stage keeps sending. How would you avoid blocked goroutines?
concurrencyci-cd - 68
A buffered channel was added to fix a deadlock, but the code still hangs for larger inputs. What would you do?
lockingconcurrency - 69
Multiple goroutines append errors to the same slice. How would you collect them safely?
concurrencyslices-maps - 70
A cache uses RWMutex, but a method takes RLock and then tries to update the map. How would you correct it?
caching - 71
A function returns fmt.Errorf("load user: %v", err), and errors.Is no longer recognizes the cause. What would you change?
error-handling - 72
A repository returns sql.ErrNoRows to an HTTP handler. How would you handle it cleanly?
sqlhttp - 73
A cleanup step fails after the main operation already failed. How would you return useful error information?
- 74
A function logs an error and also returns it, causing duplicate log lines at every layer. What policy would you use?
- 75
An HTTP request keeps running after the client disconnects. How would you connect cancellation to the work?
resiliencehttp - 76
You add a timeout with context.WithTimeout inside a function. What cleanup is required?
resilienceconcurrency - 77
A background goroutine stores a request context and uses it long after the handler returns. How would you redesign it?
concurrency - 78
A JSON endpoint accepts unknown fields silently, hiding client typos. How would you make decoding stricter?
endpoints - 79
A JSON response exposes a password hash because the whole user struct is encoded. What would you change?
passwordsstructs - 80
A JSON field must distinguish missing, false, and true in a PATCH request. How would you model it?
- 81
An HTTP handler writes an error body after it has already written a 200 response. How would you structure it?
httpstructs - 82
A handler reads an unlimited request body and can exhaust memory. What safeguard would you add?
memory - 83
An HTTP client call sometimes hangs indefinitely. How would you make it safe?
http - 84
Your service calls another API and receives a non-2xx response. What should the code do before decoding success data?
fan-outapi - 85
A handler needs a user ID from the path and currently trusts any string. How would you validate it?
validation - 86
You need middleware that records request duration without breaking the handler. How would you implement it?
middleware - 87
A table-driven unit test stops at the first failed case. How would you improve its diagnostics?
unit - 88
Tests pass individually but fail together because they share a package-level map. How would you fix them?
testing - 89
How would you test an HTTP handler without starting a real server?
http - 90
A function depends directly on a concrete database client and is hard to unit test. What small refactor would you make?
refactoringdatabaseunit - 91
A test compares errors by their full text and breaks after context is added. How would you make it stable?
concurrency - 92
A concurrent test is flaky because it sleeps for 50 milliseconds and assumes work is done. What would you replace the sleep with?
flakyconcurrency - 93
How would you test that a function stops when its context is canceled?
concurrency - 94
A parsing function panics on malformed user input. How would you change the implementation and test?
error-handling - 95
A test needs a temporary output file. How would you avoid leaving files behind?
- 96
A function launches goroutines in a loop and each one must process its own input. How would you make that intent explicit and testable?
concurrency - 97
A producer may send no values, but the consumer must still terminate. How would you test and implement this edge case?
- 98
An endpoint returns JSON but occasionally emits a partial response when encoding fails. How would you reduce that risk?
endpoints - 99
A service retries every failed HTTP request, including POST requests and 400 responses. How would you make the retry logic safer?
resiliencehttp - 100
You inherit a small Go API with suspected races and weak tests. What verification sequence would you use before changing behavior?
ownershipapitesting