Skip to content

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.

immutability

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.

slices-maps

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.

slices-maps

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.

slices-maps

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.

slices-maps

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.

capacityslices-maps

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.

slices-maps

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.

slices-maps

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.

forms

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