December 19, 2022

Типичная ошибка проведения технического интервью

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

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


В чём же тут проблема? Мы в данном случае банально проверяем помнит ли человек некоторые конкретные вещи или нет. Но смысла в этом совершенно никакого. Сегодня мы используем условный React, а завтра будем использовать условный Vue. Значит ли это, что те синьоры, которые писали на React, сразу становятся джуниорами во Vue? Думаю, нет. На самом деле, мы хотим понять, что человек умеет и как он решает задачи, выявить так называемый problem-solving skill.

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

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

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

/**
* Представим, что на одном из проектов нам потребовался DSL для решения бизнес-задачи. Наши пользователи - большие поклонники Lisp, поэтому синтаксис этого языка им более привычен, чем синтаксис JS.
* Парсер оригинального синтаксиса Lisp нам написать хоть и не так сложно, но все же для MVP это может быть неразумно, а вот простенький интерпретатор нам точно будет полезен.
*
* Что мы хотим получить:
* 1. Возможность объявлять функции таким образом: [defn, 'funcName', ['a', 'b'], ['sum', 'a', 'b']], где
*      defn - ключевое слово для определения функции
*      'funcName' - имя функции
*      ['a', 'b'] - перечисление аргументов функции
*      ['sum', 'a', 'b'] - тело функции (т. е. вызов функции sum с аргументами a и b)
* 2. Соответственно вызов функции должен быть таким ['funcName', 'a', 'b']
*
* Ниже уже реализован некоторый runtime и есть пример вызова interpret. Необходимо имплементировать interpret и defn.
* 
* P.S.
* Даже если не получится выполнять задание в полной мере (например, где-то застряли), все равно скидывайте в качестве решения то, что получилось.
*/

const defn = (functionName, args, body) => {
    // требуется реализация
}

const interpret = (...code) => {
    // требуется реализация
}

// Функция, используемая в runtime
const sum = (...args) => args.reduce((prev, curr) => prev + curr)

// Пример вызова функции interpret
const result = interpret(
    [defn, "sum3", ['a', 'b', 'c'], [sum, 'a', 'b', 'c']],
    ['sum3', 10, 20, 30]
)

console.log(result)
console.assert(result === 60)

Первой реакцией джуна, который увидит это задание, может быть «да я даже не знаю, что такое Lisp, и, тем более, никогда в реальной жизни не столкнусь с написанием интерпретаторов!». И он будет прав во всём. Но задание и не пытается проверить его навыки написания интерпретаторов Lisp, оно подталкивает его разобраться с проблемой, после чего окажется, что оно довольно простое, и что бояться было нечего.

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

В итоге, если вы просто задаёте конкретные вопросы про API вашего любимого фреймворка, то, конечно, можете всё равно нанять хорошего специалиста, пропустив при этом десят блестящих. Но зачем спрашивать то, что можно нагуглить за 5 минут? Вместо этого лучше узнать умеет ли человек решать задачи, с которыми, в том числе, он не сталкивался. Ведь именно это вам и нужно на самом деле. Это не значит, что не нужно задавать вопросы про особенности языка или фреймворка, задавайте, но относитесь к ним, в первую очередь, как к темам для обсуждения, а не как к вопросам с единственно верным ответом. И в случае, когда человек честно говорит, что никогда чего-то не использовал или не сталкивался с чем-то, у вас и будет шанс помочь ему проявить свои problem-solving skills.

Соискателям могу дать аналогичный совет. Не бойтесь говорить о том, что чего-то не знаете, размышляйте, задавайте интервьюеру вопросы. Интервью – это не экзамен в университете, где задает вопросы только преподаватель.