// кадр из фильма Темный рыцарь, прокатное удостоверение 221227808 от 02.12.2008
// кадр из фильма Темный рыцарь, прокатное удостоверение 221227808 от 02.12.2008

Привет, меня зовут Сергей Киселев, я управляю командой Development Platform в MWS Cloud Platform, а ещё давно провожу собеседования разработчиков и собираю команды. Умею делать алгоритмические секции без негатива от кандидатов, сам иногда собираю оферы, учу сына писать на C++. В этой статье расскажу, как мы построили процесс интервью для разработчика в MWS Cloud Platform. И почему важно на собеседовании проверять практические навыки.

В конце 2023 года мы начали найм большой команды для нового публичного облака от компании МТС. Цель — быстро нанимать опытных разработчиков. В итоге наняли более 200 разработчиков (C++, Golang, Java, Kotlin). Кандидат приходил к нам на одно техническое интервью, и по результатам мы были готовы проводить финальное собеседование в команду для разработчика уровня middle+. Для senior+ мы ещё проводили system design интервью, либо спрашивали о кандидате у доверенного эксперта.

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

Из моего опыта долгих собеседований

Как-то раз я потратил более 10 часов в течение девяти месяцев, чтобы устроиться на новое место работы. У меня было входное собеседование по Skype на час, где меня спрашивали базовые вещи. Мне дали домашку, на которую я потратил ещё полтора часа. Всё это продолжалось более двух недель, и я так и не дождался окончания процесса. У меня были предложения из других мест, и я отказался продолжать.

Немного погодя, меня всё же уговорили зайти в офис на техническое собеседование. Там я писал странный код на убитом корпоративном ноутбуке и что-то рассказывал, это заняло более трёх часов. И казалось, всё, но руководитель моего руководителя захотел понять, чем я дышу. Больше часа я отвечал на его вопросы. Ещё я тратил время на поездки в офис и разговоры с рекрутером.

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

Структура интервью

Joker: «You know what I noticed? Nobody panics when things go "according to plan." Even if the plan is horrifying»

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

Пример экспертизы senior-разработчика в виде векторной диаграмы, который мало пишет код
Пример экспертизы senior-разработчика в виде векторной диаграмы, который мало пишет код

На оценку каждого навыка мы тратим не более 15 минут. Важно успеть проверить ключевые вещи и не уходить глубоко. На всё у нас 90 минут, из которых надо выделить время на знакомство и пару вопросов от кандидата. Четыре темы ключевые: ревью кода, написание кода, concurrency, проектирование. Все эти темы важны в ежедневной работе наших разработчиков. Есть ещё бонусная штука — joker-вопрос от собеседующего, обычно это тема, знакомая всем участникам собеседования.

Почему именно эти темы

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

У многих наших собеседующих за плечами 2–3-этапные собеседования по алгоритмам. Они могли крутить деревья и обходить графы с закрытыми глазами. Поэтому идея устраивать лайв-кодинг нас не отпустила, и мы сошлись на разумном компромиссе — давать самые простые задачи.

Мы решили дать собеседующему свободу в выборе дополнительных тем. Это позволило разнообразить собеседования и тем снизить усталость собеседующих. Ещё это могло потешить ЧСВ кандидата, когда он рассказывал про свою экспертизу в рамках joker-темы. Это всё собеседующий фиксировал в отчёте, и мы смогли находить профильных экспертов для команд.

Ревью кода

Joker: «Never start with the head. The victim gets all fuzzy. He can't feel the next...»

Мы начинаем с код-ревью — этим занимаются все разработчики у нас в компании, и поэтому важно понять, есть ли тут совпадение по культуре. Код на ревью будет с вопросами, здесь нет правильных ответов, но есть маркеры, на которые кандидат обращает внимание, тут важна культура обсуждения замечаний к коду.

Пример одной из задач на ревью кода (мы даём чуть больше):

func (s *Service) Handle(data Data) *Result {
  for {
    result, err := s.DataService.Create()
    switch {
    case err == nil:
      return result
    case err.Again:
    default:
      log.Logger.Err(err)
      break
    }
  }
  return nil
}

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

Из опыта ревью кода на интервью

У меня было много кандидатов, которые воспринимали задачу на ревью как переписывание кода правильно. Даже после 2–3 замечаний ребята продолжали вносить изменения в код. Возможно, они просто были нетерпимы к плохому коду коллег и не могли это принять. Для них было важно всё переделать под свою картину мира.

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

Код-ревью — это первый шаг к улучшению кода. Когда ты его принимаешь таким, какой он есть, и пытаешь понять, как сделать его лучше, но не стоит фазу анализа смешивать с рефакторингом. Можно запутаться, не поняв, что делает этот код, и потерять значимую бизнес-логику. Здесь чаще всего случаются ошибки у наших кандидатов.

На ревью кода мы проверяем, что кандидат:

  • может уточнить, что от него требуется, и договориться об ожиданиях;

  • видел подобный код ранее и знает про потенциальные проблемы;

  • умеет читать чужой код и замечать проблемы;

  • может писать замечания к чужому коду.

Написание кода

Joker: «In... you see, in their last moments, people show you who they really are. So in a way, I know your friends better than you ever did».

Многие компании практикуют лайв-кодинг при оценке кандидатов. Иногда это превращается в суровые алгоритмические секции на много часов. Не все кандидаты к этому готовы, но мы решили не отказываться от написания кода на собеседовании. Мы даём одну простую (leetcode easy) задачу и оцениваем, как кандидат пишет код. Если читаешь и пишешь код каждый день, то точно помнишь, как пишутся операторы, циклы и сигнатуры функций.

Пример одной из задач с решением (мы даём чуть сложнее, кандидат пишет только код внутри функции):

// Find Words Containing Character
// You are given a 0-indexed array of strings words and a character x.
// Return an array of indices representing the words that contain the character x.
// Note that the returned array may be in **any** order.
public List<Integer> findWordsContaining(String[] words, char x) {
	List<Integer> result = new ArrayList<>();
	int index = 0;
	for (String word: words) {
		if (word.indexOf(x) >= 0) {
			result.add(index);
		}
		index++;
	}
	return result;
}

Задача на код — это не про алгоритмы, а скорее про написание обычного кода. Мы специально не даём сложные алгоритмические задачи. Но если кандидат попросит сложнее, то наш собеседующий может пойти ему навстречу и дать что-то посложнее. Основная цель — понять, как кандидат работает с новым кодом. Это именно то, что делают наши разработчики каждый день.

Из опыта написания кода на интервью

У меня есть одна из любимых задач на проверку строк на одно исправление (замена одного символа — xor-вставка/удаление одного символа). Я знаю более пяти различных способов решения этой задачи, и что мне только не сдавали! Конечно же, я решал эту задачу сам и даже написал тесты. Считаю, что нельзя давать кандидату задачу, которую ты сам не потестил. Кстати, именно так мне удалось отсечь задачи, которые было невозможно решить за 15 минут, поэтому наши задачи точно помещаются в отведённое время — проверено на себе.

У задачи есть решение в лоб с копипастой двух разных веток решения — проверка одинаковых строк и строк, отличающихся на один символ. Когда я проводил собеседование, то обратил внимание, что кандидаты не хотят писать много кода. Они пытаются всё решить элегантно и просто. Тем они сами себе роют яму и запутываются в своём решении. Всё как в обычной жизни, когда ты усложняешь решение и выходишь за временные рамки, которые были даны на решение этой задачи. Иногда приходилось ограничивать полёт фантазии ради поддерживаемости кода.

На написании кода мы проверяем, что кандидат:

  • может придумать и объяснить способ решения;

  • не усложняет задачу и учитывает время на решение;

  • может оперативно написать простое решение и не путается в своём коде;

  • проверяет своё решение самостоятельно и не перекладывает тестирование на собеседующего.

Concurrency

Joker: «You see, I'm a guy of simple taste. I enjoy... dynamite... and gunpowder... and gasoline».

В современных языках и подходах прячут сложность concurrency за синтаксисом или библиотеками. Но опытный разработчик всё равно должен понимать, как это устроено внутри и когда абстракция начинает течь. Часто дебаг проблемы затрагивает множество слоёв, и без понимания основ разработчик оказывается беспомощен. Поэтому мы даём задачу на concurrency и просим написать код для обсуждения.

Пример довольно спорной реализации deadlock на go channels (код пишет кандидат):

ch1 := make(chan string)
ch2 := make(chan string)
go func() {
	println(<-ch1)
	ch2 <- "message2"
}()
println(<-ch2)
ch1 <- "message1"

// > fatal error: all goroutines are asleep - deadlock!

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

Из опыта задач по concurrency

Я начал писать код на java с 1.4 версии и до сих пор помню немного безумный синтаксис, уведомления других потоков. На каждом собеседовании спрашивали про правильное приготовление wait/notify. Некоторые любили поговорить про spurious wakeup и starvation. Это делали, чтобы проверить senior-уровень. Порог входа был высок, и сложно было заскочить в профессию просто так.

Оказалось, что задачи по concurrency выявляют умных людей, но не лучших разработчиков. Начинающему мидлу не надо всё это знать. Эти знания скорее повредят его карьере, как и бездумное использование чужого опыта. Но как один из цветов спектра — задачи по concurrency показывают один из срезов экспертизы. И без этого невозможно полноценно оценить кандидата.

На concurrency-секции мы проверяем, что кандидат:

  • имеет базовые знания про concurrency и как это работает на современных операционных системах;

  • понимает абстракции внутри языка программирования (atomic, cas, barrier, semaphore, channel, etc);

  • осознаёт, как работает взаимодействие между потоками и как это реализуется в языке программирования;

  • есть опыт решения проблем с concurrency.

Проектирование

Joker: «All you care about is money. This town deserves a better class of criminal... and I'm gonna give it to them».

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

Пример задачи на проектирование с вопросами для фасилитации (кандидату вопросы не показываем):

## Задача:
Вы написали идеальный CRUD-сервис с rest api. У вас есть база данных для модели приложения. Как будет выглядеть добавление нового метода на ваш бэкенд? Требуется расписать последовательность шагов по добавлению нового метода. На что стоит обращать внимание? Как вы поймёте, что всё работает у пользователя?

## Вопросы:
* Как вы будете выполнять сложные вычисления на бэкенде, которые запустятся после вызова нового метода?
* Будет ли блокироваться поток обработки запроса пользователя? 
* Как вы будете тестировать свой код?
* Как поймёте, что всё работает у пользователя? А если пользователей много?
* Как устроен мониторинг работоспособности вашего метода? 
* Какие причины могут привести к проблемам работы нового метода? 
* Как правильно ограничить доступ новых пользователей к новой фиче?
* ...

За 15 минут мы можем оценить готовность кандидата пройти полноценное интервью по системному дизайну. Это интервью проводят уже наши техлиды, и там мы проверяем готовность выйти за пределы написания кода. Но и на секции по проектированию мы получаем важную информацию о навыках кандидата по проектированию и поддержке своего сервиса. Эта секция важна для оценки потенциала middle-разработчиков. А опытным senior-кандидатам мы предлагаем по итогам этой секции пройти собеседование на system design. 

История появления темы по проектированию на собеседованиях

Эксплуатация сервисов — это ещё одна тема, которой касались не все разработчики. Где-то даже берегут разработчиков от прода, прикрываясь задачами комплайнса. Но я считаю, что разработчик обязан знать, как работает его код на проде, уметь чинить, если что-то сломалось или работает не так. По моему мнению, именно разработчики дали новую жизнь эксплуатации в индустрии.

Помню как много лет назад в России стали появляться собеседования по System Design. Как все снова начали обсуждать таблицы latency после книги System Design Interview от Alex Xu. Особенно подогревали этот интерес "варяги" помогающие строить индустрию разработки в России. Я считаю это важным шагом к осознанию как правильно строить поддерживаемые и надежные сервисы.

DevOps-практики подвинули "устаревших" системных администраторов, и в новой парадигме всё стало кодом. Очень сильно на индустрию повлияла знаменита книга Site Reliability Engineering от Google. Всё это происходило последние десять лет, и я рад, что был частью этого. Я все это вижу делая конференцию по DevOps в России — DevOops.

На секции по проектированию мы проверяем, что кандидат:

  • поддерживал и/или дежурил за свой сервис в проде;

  • понимает, как диагностировать сложные проблемы и оценивать надежность;

  • знает, что такое observability: логи, трейсы, метрики, алерты;

  • имеет потенциал экспертизы выше senior и сможет пройти наше system design интервью.

Дополнительные темы

Joker: «See, madness, as you know, is like gravity. All it takes, is a little push!..»

Если остаётся время и кандидат готов ещё поговорить о своём опыте, то можно затронуть другие вопросы. Это может быть тема, важная для собеседующего, тогда он задаёт прицельные вопросы, чтобы вычислить кандидатов, которые «в теме». Например, понимание, как как писать и оптимизировать SQL-запросы. Или готовить k8s и писать для него операторы. Возможно, это будет глубокое погружение в системное программирование.

В дополнительное время я прошу кандидата рассказать про свой опыт, в какой-то степени это joker-тема от кандидата. Ведь он рассказывает тему, в которой считает себя экспертом. Не обязательно, чтобы собеседующий разбирался в этой теме, тут важно оценить, насколько кандидат умеет объяснять. И вдруг окажется, что именно его экспертиза нам очень нужна и мы бы про не узнали иначе.

Как я собеседую людей в свою команду

Команда Development Platform работает на стыке инфраструктуры и решения конкретных проблем. Довольно часто мы берём шефство над задачами посредине, когда нет явного ответственного за этот кусок. Например, мы отвечаем за cli, api-gateway, status board и много за что ещё. На этих проектах мы тестируем наши инфраструктурные решения.

Для меня есть три критерия для принятия решения о найме: умение видеть общие решения, желание внедрять общие решения и интерес к устройству чужих библиотек. И самое непростое тут — желание разобрать чужое решение и понять, почему оно не работает. Возможно, даже принести код с исправлением ошибки на ревью автору. Именно поэтому я люблю joker-вопрос. Одна из моих любимых тем — кодогенерация. Всё это очень хорошо показывает фокус интереса кандидата, а нам позволяет нанять увлечённого разработчика.

Обсуждая дополнительные темы, мы проверяем, что кандидат:

  • чем-то интересуется, а не просто пишет код по указке техлида;

  • имеет экспертизу в какой-то области и знает, как там всё устроено;

  • может рассказать и понятно объяснить свою тему;

  • применял этот опыт в проде и осознаёт стоимость поддержки.

Обратная связь

Joker: «I believe whatever doesn't kill you simply makes you… stranger!»

По внутренней оценке, только половина всех кандидатов успешно проходит наше техническое интервью. Это хорошие показатели, и они получаются из-за качественной предварительной оценки готовности кандидата пройти интервью. До интервью опытный разработчик оценивает кандидата по итогам скрининга и резюме. Мы стараемся не тратить время кандидатов и собеседующих впустую.

Про досрочное завершение собеседования

Есть мнение, что надо всё равно доводить собеседование до конца, даже если в процессе мы уже поняли, что кандидат нам не подходит. Иногда даже на это завязана премия собеседующего. Мы с таким подходом мы не согласны и не практикуем его. У нас есть договорённость заканчивать собеседование досрочно, если кандидат услышит и собеседующий готов. Но при этом надо озвучить честную мотивацию досрочного завершения.

Кто-то стесняется дать обратную связь сразу. Есть риск словить негатив — это всегда неприятно, но по мне это важный элемент взросления любого человека — уметь говорить негативную обратную связь. Я завершал досрочно собеседования и называл причины, но оставляю этот выбор за собеседующим, он может принять такое решение, если посчитает его верным.

Если кандидат ведёт себя адекватно на собеседовании, то ему может быть озвучена обратная связь. Это происходит в конце встречи либо через рекрутера. Были случаи, когда к нам возвращались спустя время после проработки обратной связи. Либо приходили к нам спустя полгода-год после отказа на повторное собеседование и мы им давали офер.

Про обратную связь:

У меня в команде работает разработчик, которому мы отказали в найме весной 2024-го. Тогда я ему дал развёрнуто причины и объяснил, почему мы его не готовы взять. В тот момент нанимали только опытных людей и он не прошёл по экспертизе. Он попросил совета, что ему надо подтянуть, и ушёл.

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

Что дальше

Joker: «Now... our operation is small... but there is a lot of potential... for aggressive expansion. So which of you fine gentlemen would like to join our team?»

Мы продолжаем нанимать в MWS Cloud Platform, вакансии в команду можно посмотреть на карьерном сайте MWS. Сейчас облако готовится к выходу в паблик, скоро все смогут сравнить наше облачное решение с рынком и выбрать своего дилера. Я уже его осторожно потестил в превью, и мне пока всё очень нравится по производительности и надёжности (с поправкой на превью).

На Хабре я уже писал о принципах построения Development Platform команд. Чуть позже я планирую отдельно рассказать о наших system design собеседованиях, процессе найма и перформанс-ревью. О том, как они работают вместе и дополняют друг друга. Возможно, затрону тему отличия нашего архитектурного интервью и расскажу детальнее про наши критерии оценки.

Если вы собеседовались к нам MWS Cloud Platform на разработчика и реальность оказалось не такой, то приносите мне обратную связь (комментарий, личка, телеграм). Спасибо, что прочитали, и до связи в другой раз!

// кадр из фильма Темный рыцарь, прокатное удостоверение 221227808 от 02.12.2008
// кадр из фильма Темный рыцарь, прокатное удостоверение 221227808 от 02.12.2008

Комментарии (0)