Собеседование на позицию Golang разработчика

Вместо вступления

В статье я рассмотрю наиболее часто встречаемые вопросы на собеседованиях Golang разработчика. Эта выборка основана только на личном опыте по результатам моих интервью в компании Авито, Тинькофф, SberCloud, МТС Cloud, NVIDIA, Acronis, Mail.ru Cloud Solutions, VK Pay. Всё действие происходило летом 2021 года.

В зависимости от зрелости процессов отбора кандидатов все собеседования можно разделить на две группы:

  1. Собеседование состоит из трёх технических секций (знание языка, алгоритмы и архитектура) и одной или нескольких встреч с командами, в которые идет найм, с целью знакомства. Обычно такой подход практикуют компании, у которых большой поток кандидатов и они хотят стандартизировать этот процесс.
  2. Собеседование на "вольную тему". Здесь уже есть сильная зависимость от знаний и подготовки собеседующих людей, поскольку вопросы могут придумываться на ходу, а не браться из заранее заготовленного пула. Обычно такие собеседования длятся несколько часов и затрагивают все темы сразу (язык, базы данных, сети и т.д.).

На многих интервью требовалось писать код, решать алгоритмические задачи или обсуждать представленный фрагмент кода, объясняя что в нем не так и как можно исправить. Поэтому стоит заранее уточнить у HR процесс собеседования, чтобы быть готовым и иметь под рукой ноутбук.

В одном случае меня попросили решить тестовое задание, поскольку я не слишком хорошо прошел первую секцию на алгоритмы. Но в целом это исключение, поскольку немногие кандидаты готовы тратить на это время.

Общие вопросы

Вопросы по предыдущим местам работы. Причины ухода с текущего места, ожидания от будущего работодателя.

Рекомендую вспомнить не только свои основные достижения, но и проблемы, с которыми приходилось сталкиваться (например, нарушение сроков, некорректная реализация поставленной задачи, конфликты в коллективе и т.п.), как их удавалось решить и какие выводы были сделаны, чтобы не допустить повторения этих ситуаций в будущем.

Вопросы по soft skills. Например, опишите вашего идеального руководителя или кем вы видите себя через 5 лет.

Эта группа вопросов помогает понять насколько вы разделяете ценности компании, будет ли команде комфортно работать с вами и вашу мотивацию. Я встречал мало людей, которые целенаправленно задавали похожие вопросы и могли чётко объяснить, что они ожидают услышать и какие делают выводы из ответов кандидата.

Расскажите чем вам нравится язык Golang, какие есть недостатки?

Достаточно рассказать о своём опыте работы с языком. Можно сравнить с другими языками - везде есть свои плюсы и минусы.

Расскажите, что происходит, когда пользователь набирает в адресной строке браузера, например, google.ru.

Довольно заезженный вопрос, спросили только на одном собеседовании. Можно найти множество подробных ответов на этот вопрос (например). Я бы поверхностно описал весь процесс, уточнив какие темы наиболее интересно было бы услышать собеседнику.

Go: slices

Что такое slice? Расскажите внутреннее устройство.

Как работает функция append? Как происходит аллокация нового массива при использовании функции append?

Хорошее описание есть в документации: раз, два.

Сколько байт занимает в памяти структура slice?

type slice struct {
    len int   // 4/8 bytes
    cap int   // 4/8 bytes
    ptr *type // 4/8 bytes
}

Размер целочисленного типа и указателя зависит от платформы компиляции (32-bit, 64-bit).

Go: interfaces

Что такое интерфейс? Расскажите внутреннее устройство.

Нужно понимать что из себя представляет структура интерфейса, как выполняется её инициализация и что такое таблица методов. Здесь подробное описание.

Пример кода на понимание внутреннего представления интерфейсов.

Что будет выведено в консоль после выполнения кода?

package main

import "fmt"

func main() {
    err := do()
    if err != nil {
        fmt.Println("ERROR")
    } else {
        fmt.Println("NO ERROR")
    }
}

func do() error {
    var p *MyError = nil
    if false {
        p = &MyError{"error"}
    }
    
    return p
}

type MyError struct{
    msg string
}

func (e MyError) Error() string {
    return e.msg
}

Правильный ответ: ERROR, т.к. структура интерфейса будет инициализирована на строке 15.

Пример кода с интерфейсом и value/pointer receiver.

Какой результат выполнения этого кода?

package main

import "fmt"

type Bar interface {
	Get() string
}

func print(i Bar) {
	fmt.Println(i.Get())
}

type Foo struct {
	f string
}

func (foo *Foo) Get() string {
	return foo.f
}

func main() {
	foo := Foo{"hello"}
	print(foo)
}

Результатом будет ошибка компиляции:

cannot use foo (type Foo) as type Bar in argument to print:
Foo does not implement Bar (Get method has pointer receiver)

Если кратко: брать ссылку на временный скопированный объект (в Go всё передается по значению) не имеет смысла, поскольку это может приводить к изменению только временного объекта, а не исходного. Поэтому при преобразовании foo в тип интерфейса Bar возникает ошибка, т.к. компилятор находит только метод с pointer receiver. Более подробно можно прочитать здесь.

Go: multithreading

Что такое горутина? В чем отличие горутин от потоков операционной системы?

Этот вопрос тесно связан с планировщиком горутин (смотри ниже), поэтому рекомендую ознакомиться с его устройством. Также хорошо освежить в памяти, что такое context switching потоков ОС и то что в Go используется динамически расширяемый stack памяти под каждую горутину.

Как работает планировщик горутин?

Советую прочитать отличный цикл статей о планировщике Go.

Кооперативная многозадачность vs вытесняющая многозадачность.

В Go версии 1.14 было внесено важное изменение в работу планировщика, с которым можно ознакомиться в этой статье.

Какие вы знаете способы синхронизации между горутинами в Go?

Мьютексы (sync.Mutex и sync.RWMutex), каналы, атомики, sync.WaitGroup, sync.Once. Нужно понимать в каких случаях следует использовать sync.RWMutex.

Про особенность использования sync.Map стоит прочитать эту статью. А про устройство CPU caches посмотрите это видео.

Задача на многопоточность.

Обычно просили реализовать свой worker pool, который сможет принимать на вход задачи и обрабатывать их параллельно.

Go: channels

Что такое каналы в Go и как они устроены? Какие виды каналов вы знаете?

Читаем отличный доклад с одной из конференций.

Что происходит при записи или чтении в/из nil канала, закрытого канала, буферизированного/небуферизированного канала?

Можно ознакомиться в статье выше или здесь

Что такое select и как он работает?

Можно ознакомиться в статье выше. Порядок чтения каналов в select выбирается произвольно, но есть небольшой хак, с помощью которого можно сделать поведение детерминированным:

select {
case <-ch1:
    fmt.Println("read from ch1")
default:
    select {
    case <-ch2:
        fmt.Println("read from ch2")
    default:
    }
}

Но такой хак стоит использовать осторожно, т.к. можно уйти в полку по CPU.

Go: map

Что это за структура данных, какие ее преимущества?

Классическая хэш-таблица. Стоит рассказать о методе разрешения коллизий, переаллокациях при превышении load factor и алгоритмической сложности в нотации O-большое.

Что происходит при конкурентной записи в map?

Структура данных не является потокобезопасной, поэтому конкурентная запись может приводить к панике. Стоит рассказать о способах синхронизации доступа к map.

Какие типы данных могут быть использованы в качестве ключа map?

Любой сравниваемый тип:

As mentioned earlier, map keys may be of any type that is comparable. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==, and may not be used as map keys.

Go: обработка ошибок

Назовите два способа работы с ошибками в Go?

Возврат ошибки в виде интерфейса error и выкидывание panic. Паника - это не тоже самое, что и классические исключения в других языках, поскольку паника гарантированно завершает выполнение текущей функции.

Как можно обработать панику?

С помощью recover. Обратите внимание, что обработчик паники должен быть объявлен в той же горутине, где возникает паника.

Как можно определить место возникновения ошибки?

В случае небольших микросервисов и приложений достаточно подробного описания ошибки, чтобы понять место её возникновения. В остальных случаях можно добавлять стек вызова функций в описание ошибки.

Go: другое

Что такое контекст и в каких случаях его стоит использовать?

В документации хорошо об этом рассказано.

Как работает garbage collector?

Еще один отличный цикл статей по работе сборщика мусора.

Что такое escape analysis и как он работает?

Здесь важно понимать какие правила используются для определения необходимости перемещения объекта в heap. Читайте здесь.

Что такое defer?

Читаем документацию. Можно посмотреть статью о подводных камнях при использовании defer.

Базы данных

Эта тема довольно обширная, но вопросы обычно повторяются. Из основного:

  1. Что такое ACID?
  2. Какие виды изоляции транзакций вы знаете? На одном собеседовании попросили привести пример SQL запроса, который будет выполнен на уровне Repetable Read, но вернет ошибку на уровне Serializable (ответ).
  3. Назовите известные вам виды индексов. Отличия между hash и b-tree индексами. Как устроен b-tree индекс (хорошее видео на эту тему)?
  4. Построение индексов на запросы (стоит вспомнить что такое селективность). Пример запроса: SELECT a, b FROM table WHERE a = 0 AND b > 1 ORDER BY c
  5. Шардирование и репликация данных.

Сети

Достаточно знать особенности и отличия транспортных протоколов TCP и UDP. Один раз спросили каким образом происходит преобразование IP адреса в MAC адрес узла: рассказываем про протокол ARP и arping.

Архитектура

Здесь уже вариативность вопросов большая. Например, приходилось проектировать пользовательскую ленту Facebook, сервис сокращения ссылок с нагрузкой на чтение и запись в 300000 RPS. Обычно в разговоре затрагиваются темы проектирования отказоустойчивых, масштабируемых решений, способы хранения данных (репликация и шардирование), кэширование данных, мониторинги и логирование, API.

В интернете можно найти много видео и материалов на тему подготвки и прохождения архитектурных секций. Рекомендую их изучить, чтобы быть готовыми.