Go (Golang) Interview Questions & Tips for Senior Engineers

Go Interview Questions & Tips

By Mike Mroczka | Last updated: July 6, 2023

Go Interview Stats

We've hosted over 100k interviews on our platform. Go was the language of choice in those interviews 1% of the time, and engineers who interviewed in Go passed their interviews 51% of the time.

Below is a distribution of programming languages and their popularity in technical interviews as well as success rates in interviews, by language.

Go Idioms & Idiosyncrasies

Go, often referred to as Golang, is a statically typed, compiled language renowned for its simplicity, efficiency, and strong support for concurrent programming. Go was developed by Google, and its design makes it an excellent choice for concurrent and networked programming.

Go's distinct features and programming style allow developers to write clear and efficient code. Here are some of the key aspects of Go that are worth flexing in a technical interview:

Common Go Interview Mistakes

When interviewing in Go, there are a few common pitfalls to avoid:

Ignoring Errors

Go doesn't use exceptions for error handling and instead relies on an explicit error return value. Ignoring errors can lead to unpredictable behavior, and it's considered bad practice. Always handle errors right after they occur.

The most common way this crops up is when a candidate ignores the error returned by a function or method:

Go

res, _ := http.Get("http://example.com/")

In the above code snippet, the Get function returns two values - a Response and an error. Ignoring the error by using an underscore (_) is bad practice. It's better to handle the error appropriately:

Never ignore an error return value. Here is an example of handling errors correctly:

Go

file, err := http.Get("http://example.com/")
if err != nil {
    log.Fatal(err)
}
// continue processing with file

In the corrected example, the error is checked, and if it's not nil, the error is logged, and the application is terminated. This approach makes it easy to see when and where the problem occurred, making debugging easier.

Not Using Concurrency When Appropriate

In coding interviews, concurrency is a topic that does not tend to come up very often. If you're coding in Go and likely applying for a role in Go then expect concurrency as a possible question type. One of the primary benefits of using Go is its built-in support for concurrent programming via goroutines and channels. Ignoring these features and writing strictly sequential code can be a significant oversight and will prevent you from demonstrating your mastery of one of Go's key strengths.

Go's concurrency model, known as CSP (Communicating Sequential Processes), is one of the language's most powerful features. It allows multiple tasks to run independently of each other and provides a way to communicate between them without the need for locks or shared state, which can lead to complex bugs.

The go keyword in Go is used to launch a new goroutine, which is a lightweight thread of execution. Channels provide a way for these goroutines to communicate safely with each other.

Go

func printNumbers() {
    for i := 1; i <= 10; i++ {
        fmt.Println(i)
    }
}

func printLetters() {
    for i := 'a'; i <= 'j'; i++ {
        fmt.Println(string(i))
    }
}

func main() {
    go printNumbers()
    go printLetters()
    time.Sleep(time.Second)
}

In the above example, printNumbers and printLetters are executed concurrently. However, not all problems can or should be solved with concurrency, and its misuse can lead to problems like race conditions. Understanding when and how to use these features appropriately is an essential part of Go programming.

Misunderstanding Nil Interfaces

In Go, an interface value is nil only if both its type and value are nil. A common mistake is assuming that an interface holding a nil pointer would itself be nil, which is incorrect. Here's an illustration:

Go

type Foo struct {}
func (f *Foo) Bar() {}

var f *Foo // f is a nil pointer

var i interface{} = f

if i == nil {
    fmt.Println("i is nil")
} else {
    fmt.Println("i is not nil") // Output: i is not nil
}

In the above example, even though the f pointer is nil, the interface i is not nil because it has a type (*Foo). This is a subtle point that often trips up even experienced Go programmers. This mistake can lead to panics at runtime if you try to access a method on i assuming that it is nil.

Go

type Fooer interface {
    Bar()
}

var f *Foo // f is a nil pointer

var i Fooer = f

if i == nil {
    fmt.Println("i is nil")
} else {
    fmt.Println("i is not nil") // Output: i is not nil
}

i.Bar() // Runtime panic: nil pointer dereference

In the above example, we define an interface Fooer that has a Bar method. f is a nil pointer to Foo, and i is an interface of type Fooer which holds f. Although f is nil, i is not nil and calling the Bar method on i results in a runtime panic as f is nil.

Thus, when working with interfaces in Go, it's crucial to understand the distinction between a nil interface value and an interface value that holds a nil.

How to Demonstrate Go Expertise in Interviews

While there are many ways to demonstrate mastery over Go in an interview, show that you aren't a Java/C++ coder that happens to be using Go, you're a Go developer with full understanding of what makes Go a unique language.