
Когда мы говорим о кодинг-интервью, у многих начинаются флешбеки от разворачивания дерева до домашних заданий на 14 часов.
Я развлекаюсь интервьюингом больше десяти лет, лет пять вёл SRE Interview Club для собратьев по пейджеру и набрал небольшую базу любимых вопросов — которые задаю по сиюминутному желанию, в зависимости от фазы луны: они все работают для любых ситуаций.
При этом неважно, на каком языке собирается кодить кандидат (да, видел даже перл от менеджера, кто уже десятилетие к тому моменту не брал в руки шашку), и неважно, на какой уровень его собеседовать.
В теории, все мои вопросы давно разобраны, и в гугле были внесены в список забаненных вопросов — но никогда претензий ко мне не поступало, так как сигнал ясный и чёткий — а ради этого всё затевалось. :)
Давайте рассмотрим один из моих любимых вопросов: доставайте свои вайтборды или блокнотики, начинаем кодить на доске!
Задача
Представьте, что мы решили выпустить новый продукт на рынок: робот-пылесос. Инженеры сделали хорошую платформу, дизайнеры — красивый дизайн, у нас своё облако и прекрасный фреймворк, так что пользователю просто надо поставить робота дома, где ему удобно, и нажать на кнопку. Робот свяжется с облаком, где будет запущена функция, аргументом которой будет интерфейс к физическому роботу.
Грубо говоря, это выглядит вот так:
class Robot:
def Move(self) -> bool:
"""Moves robot 1 step ahead. Returns True on success, False if hits obstacle."""
def TurnLeft(self):
"""Turns robot in place counter-clockwise 90 degrees."""
def TurnRight(self):
"""Turns robot in place clockwise 90 degrees."""
def DoClean(robot: Robot):
...
Наши роботы уже запакованы и отгружены, платформа отлажена, одна мелочь: функцию DoClean
так никто и не написал. Ваша задача: написать какое-нибудь решение, чтобы роботы перестали быть пустой железкой. Мы всегда можем её поменять потом, сделать лучше и т. п., но сперва надо, чтобы уже стало чисто в комнатах.
Фаза 0: адаптация задачи
Этот вопрос — один из тех, где практически отсутствует адаптация в форме задания вопроса. Всё, что нужно — держать под рукой достаточно шаблонов под разные языки программирования, дабы скопипастить в онлайн-редактор версию на языке по выбору. (Если человек сеньор с десятком языков -- я обычно копирую какой-либо другой язык, нежели он выбрал для интервью, но на языке из списка его опыта.)
Фаза 1: обсуждение проблемы
Те, кто ходят на интервью как на работу, знают, что перед решением любой задачи её надо обсудить. Кто не знает, всё равно задаст эти вопросы раньше или позже. Если нет — я буду подсказывать по ходу, поскольку время ограничено, а я хочу видеть рассуждения.
Вопросы, которые так или иначе влияют на решение, и чем больше их задано до начала кодинга, тем лучше:
Где именно в комнате расположен робот?
— Где угодно, мы не управляем пользователем.А что такое «комната» — это прямоугольник? Или выпуклый многоугольник? Есть ли препятствия?
— В общем‑то «что угодно», мы не знаем, где пользователь поставит робота. Препятствия могут быть, мы не контролируем их.А препятствия двигаются?
— Отличный вопрос... для первой версии, решим, что нет, но надо будет подумать обязательно.А многоугольник замкнутый? Что, если пользователь оставит дверь квартиры открытой?
— Если пользователь оставит дверь открытой, то у него будет очень чистый микрорайон.То есть ограничения на размер комнаты нет?
— Мы не хотели бы его ограничивать.А что на тему прямоугольности углов?
— Первая модель прошивки умеет только ходить по прямой и поворачиваться на 90 градусов, чистим как можем исходя из этого.А если препятствие на половине шага?
— Робот отойдёт назад, если наедет на препятствие, и мы получим False.Может ли робот застрять и не смочь повернуться на месте?
— Хороший вопрос... надо будет учесть для следующей версии прошивки.
В принципе, ответов должно хватить для того, чтобы стало понятно, что же надо.

Фаза 2: попытка в MVP
Почти все кандидаты (зачастую даже те, кто задал все вопросы и вроде как даже поняли задачу) начинают решать задачу с попытки построения stateless алгоритма. Подозреваю, что это вопрос инерции мышления (о, задача про лабиринт, там вроде ничего не надо, по стенке идём).
Пишется что-то типа:
def Clean(robot: Robot):
while True: # потом поправлю
if not robot.Move():
robot.TurnRight()
Ой, нет, так не пойдёт же. Наверное, надо сперва дойти до угла, а там уже делать:
def Clean(robot: Robot):
while robot.Move(): # Идём до стены
pass
robot.TurnRight()
while robot.Move(): # Идём до другой стены
pass
robot.TurnRight()
# Теперь мы в углу, и смотрим вдоль стены
while True: # потом поправлю
while robot.Move():
pass # Идём до стены напротив
robot.TurnRight()
if not robot.Move():
break # Мы в углу!
robot.TurnRight()
while robot.Move():
pass # Идём до верхней стены
robot.TurnLeft()
if not robot.Move():
break # Мы в углу!
robot.TurnLeft()
# Готово, мы опять у верхней стены смотрим вниз
Где-то в районе этого момента, если кандидат ещё сам не догадался, я подсказываю, что в комнате таки есть препятствия. И что комната не обязательно прямоугольная. А даже если и прямоугольная — не факт, что робот изначально стоял повёрнутым строго вдоль стен. В общем, полагаться на индукционный алгоритм нельзя. Для каждой попытки усложнить его легко составить контрпример, при котором вся эвристика развалится.
Так что где‑то через 10 минут подталкиваем к следующей фазе, если контрпримеров не хватило.
Фаза 3: рекурсия — см.рекурсия
Окей, раз мы не можем stateless — решим проблему иначе. Комната — это прямоугольник N на M. Пусть N и M будут по тысяче. Или по 10 тысяч. Машины мощные, памяти много, что нам, жалко что ли? Нет, конечно. Начинаем с координаты [0,0]. (Пауза 2–3 секунды.) А, нет, начнём с [N/2, M/2]. Будем считать, что «вперёд» для начала — это «смотрим вверх». И просто рекурсивно зальём всё поле: для каждой клетки пробуем сходить вверх, влево, вправо, вниз. Если встретили стену — стоп. Если встретили место, где уже были — стоп. Иначе запоминаем, что уже были, и рекурсивно повторяем. Прекрасное решение, идеально для MVP, гарантирует решение «всё, что доступно, — обработано».
Итак, стремимся к вот такому:
state = set()
def Clean(robot: Robot, x=0, y=0):
state.add((x,y))
if (x,y-1) not in state and robot.Up():
Clean(robot, x, y-1)
if (x,y+1) not in state and robot.Down():
Clean(robot, x, y+1)
if (x-1,y) not in state and robot.Left():
Clean(robot, x-1, y)
if (x+1,y) not in state and robot.Right():
Clean(robot, x+1, y)
Когда кандидат пишет такое, я внутренне радуюсь! Потому что зачастую пишут другое:
def Clean(robot: Robot):
state = dict()
while True:
for range(4):
if robot.Move():
...
robot.TurnLeft()
И долго-долго потом пытаются не запутаться в положении. Единственный способ: подсказать, что Robot пусть и умеет только вверх-вниз-влево-вправо, ничто не мешает инкапсулировать его в класс-помощник, который будет отслеживать положение и направление, а также предоставит требуемые методы.
Как только человек добрался до этого места, дальше обычно всё идёт легко, класс-обёртка набрасывается за минуту, основной код быстро истощается до кода подобного примеру вверху, и, можно сказать, дело сделано.
Только уточняем у него, почему вместо очистки всего, если запустили из середины, мы получаем непонятный бесконечный цикл. Приводим пример маленькой комнаты, человек проходит вручную по коду...
И исправляет.
Обычно, для исправления правится функция так, чтобы по выходу из Clean робот всегда оставался там же, где был до входа, возможно, даже сохраняя направление -- но направление, в общем случае, сохранять не надо, просто вместо Up/down/left/right делаем методы North/South/West/East и радуемся очевидности такого подхода.
Фаза 4: делаем лучше
Редко когда на этом месте хватает времени на большее, чем обсудить, но спросить, какая сложность у алгоритма, обязательно. А потом сделаем намёк: а по памяти? А почему тогда попытка почистить комнату 50×50 роботом вызывает исключение? (Для питона, для других языков подводим немного другим маршрутом к тому же вопросу). В общем да, рекурсия -- это обход в глубину, а глубина прямо пропорциональна числу клеток, а у питона глубина рекурсии по умолчанию всего 1000, так что уже от 32×32 и выше идёт переполнение.
А ещё, можно вспомнить, что препятствия не обязательно занимают всю клетку: возможна просто тонкая стена, когда из [0,0] в [0,1] пройти нельзя, но из [1,1] в [0,1] — можно. Что примечательно: эта проблема проявляется не во всех реализациях, которые обычно порождаются к этому моменту собеседования. Например, решение выше — иммунно к нему.
Можно ещё спросить: а как бы так избавиться от многократного прохода по одному и тому же месту? Батарея не резиновая. Конечно, мы говорили, что оптимизацией займёмся потом, но вдруг что-то можно сделать уже сейчас?
В целом, надо подбросить ограничений так, чтобы человек задумался и перешёл от brute force к интеллектуальному обходу.
Разумеется, первый шаг: переписываем рекурсию в цикл:
def Clean(robot: Robot):
state = [(0,0)]
while state:
x,y = state.pop(0)
robot.MoveTo(x,y)
...
В этот момент соображаем, что нам надо MoveTo
, а раз MoveTo
, то надо поиск по карте (как ещё добраться?), значит, надо отслеживать не просто карту, но и состояние связей (см. выше про тонкие стенки), добавить поиск ближайшей клетки, в которой ещё не были, и так далее.
Всё это -- очень весело и позволяет занять произвольное оставшееся от интервью время, даже если собеседование не в геймдев (а если в геймдев, то А* должен от зубов отлетать, ни в коем случае не пользоваться библиотеками!).
Фаза 5: закругляемся
Вне зависимости от того, как продвигается кандидат, в начале интервью я всегда говорю, что вопрос бесконечный, что закончим мы по таймауту и неуспевание сделать что-бы-там-ни-делалось-в-этот-момент — не говорит ни о чём.
Поэтому строго по таймауту: прерываем интервью. Таймаут подобран так, чтобы всегда было минут 5 у кандидата на вопросы в отместку.
В случае проведения двойного интервью (кодинг + системный дизайн) — самое время рассказать анекдот про сисадмина.
После чего переходим к system design:
Отлично, а теперь давайте задизайним систему, которая позволит нам обслуживать этих самых роботов в такой форме, что на роботе просто нажимается кнопка, всё делается в облаке, где запускается указанная функция Clean.
Фаза 6: отчет и аналитика
В процессе интервью я делаю регулярные снапшоты кода после каждого чекпоинта — паузы на размышления, точки «я сделяль» или перед каждым хинтом. Я дополнительно стараюсь записывать слова кандидата максимально дословно, плюс записываю, какие хинты я сделал и в какой форме был подан вопрос. Полностью писать свои слова времени нет, для этого использую сжатую форму — сразу после интервью это распаковывается по свежим следам.
Отчёт откладывается на 1–2 дня, после чего перечитывается и анализируется максимально новыми глазами.
Оцениваются:
Аналитика: о чём спрашивал кандидат вначале, как разрешал противоречия, какие допущения заявлял explicit, что оставлял implicit;
Архитектура: как структурировался код, в каком порядке конструировалось решение (снизу вверх или сверху вниз);
Надежность: были ли попытки сделать код тестируемым, пытался ли кандидат вообще тестировать код, строил ли инварианты;
Читаемость: была ли попытка сделать код читаемым, были ли комментарии, разбивка на функции, именование функций;
Адаптируемость: как менялись мысли, когда менялись требования к задаче или когда скрытые ранее ограничения приводили к необходимости выкинуть текущее решение.
НЕ оцениваются:
Отступы,
ijkl
внутренние переменные,CamelCase
vssnake_case
vsTHIS_IS_THE_BEST_JAVA_VARIABLE_BY_SOME_ONE_WHO_ONLY_HEAR_ABOUT_JAVA_ONE_IN_THEIR_LIFE_BUT_THEY_CANT_STOP_GIGGLE_ABOUT_IT
.Компилируемость кода.
Порядок аргументов системных функций.
Существование системных функций.
Сортировка импортов.
Сами импорты.
Другие вопросы
Что ещё можно спросить (вместо обхода матрицы)?
По сути, я спрашиваю что-нибудь из:
как просуммировать листья произвольного дерева за O(1) по памяти;
как найти узел в дереве по произвольному условию;
как сделать LRU-кеш;
как сделать связный список.
И ни один из этих вопросов не задаётся так, как он есть на самом деле. Всегда в форме не настоящей задачи, но которая могла бы быть и настоящей. Потому что понимание задачи — 80% задачи. Кодинг же — всего лишь вторые 80%...
Комментарии (0)
Politura
19.09.2025 20:56Неправильный у вас робот, правильный должен уметь на любой угол поворачивать. Это с завода 90, а по факту будет вправо 87, влево 92. А через пол года вправо 80, влево 85.
mSnus
19.09.2025 20:56И что даёт такое собеседование, кроме чувства глубокого собственного превосходства для интервьюера?
datacompboy Автор
19.09.2025 20:56Показывает, умеет ли думать кандидат. Всё остальное можно нагуглить.
randomsimplenumber
19.09.2025 20:56Разработка api для нех и как просуммировать листья произвольного дерева рядом в одном собеседовании на одну вакансию - вы серьезно?
datacompboy Автор
19.09.2025 20:56"Что еще спросить" -- это другие вопросы. Конкретно этот вопрос -- "напишите обход матрицы".
user-book
19.09.2025 20:56задача какая-то наркомания
если это оторванное от реальности то слишком много каких-то заморочек и фокусировки на ненужных мелочах
если это реальный кейс для фирмы что роботов таких делает то у робота куча периферии и его позиционируешь от док-станции и вообще пофиг на геометрию или размеры, лишь бы хватило заряда и пол был ровным.
и даже если надо чистить "оптимально" все равно проще в первый раз прогнать черепашку что бы карту построила а потом уже по ней "оптимизировать" а не пытаться рожать сферических коней в ввакумеdatacompboy Автор
19.09.2025 20:56Это вопрос "напишите обход матрицы". Вариант "прогнать черепашку чтобы карту построила" -- идеален для мвп, потому что пока строится карта -- всё еще и почищено.
rpc1
19.09.2025 20:56а сколько времени дается на эту задачу с учетом дополнительных вопросов про LRU кэш и обхода деревьев?
datacompboy Автор
19.09.2025 20:56Неважно -- вопрос всегда заканчивается тогда, когда заканчивается время. Обычно 45 минут на кодинговую часть.
mmMike
19.09.2025 20:56Я развлекаюсь интервьюингом больше десяти лет, лет пять вёл SRE Interview Club
как просуммировать листья произвольного дерева за O(1) по памяти;
как найти узел в дереве по произвольному условию;
как сделать LRU-кеш;Почему то, мне кажется, что автор чешет свое ЧСВ на таких вопросах...
Например, аббревиатура LRU. С ходу я не помню что это. Но вот глянул как расшифровывает и что подразумевается, и понял, что я уже делал реализацию (кэш активных переводов с вытеснением по времени последнего входящего/исходящего сообщения в "холодный кэш" для СБП переводов).
И, скорее всего, с точки зрения автора, ели "не знаешь что я спросил (LRU) без подскзаки Интрернета" - фуу лох.
Может и ошибаюсь, но вот фраза "или блокнотики, начинаем кодить на доске!" как бы намекает исходный "промпт" вида "ты в постапокалиптическом мире и у тебя нет возможность найти информацию. только помнить".
datacompboy Автор
19.09.2025 20:56Например, аббревиатура LRU. С ходу я не помню что это
Посмотрите еще раз как задаётся задача. Суть вопроса -- "напишите проход по матрице".
Так же и дальше, не надо спрашивать "напишите обход дерева" или "Напишите LRU кеш". Это тупо. Есть псевдо-задача, решение для неё органично через обход дерева или через кеш. Я ожидаю что сеньёр знает кеши и деревья и может продемонстрировать это знание, ничего больше."начинаем кодить на доске"
отсылка на интервью когда почему-то нельзя пользоваться ничем кроме доски. я как не понимал этого подхода -- так и не понимаю... :D хорошо, что сейчас чаще это удалённо, поэтому как минимум редактор с подсветкой...
mmMike
19.09.2025 20:56Я ожидаю что сеньёр знает кеши
Кстати, а что Вы подразумеваете по "кэш"? Кэш конвеера выполнения команд в CPU, прикладной кэш name+value параметров в памяти программы, кэш HTTP сервера (GET запросов), кэш планов запросов DB.. и и.д. и т.п.
Слово одно, важные нюансы - разные.Когда мне задают "простой вопрос" я долго и нужно выясняю, а что спрашиваешь то. Потому что, как показывает опыт, в половине случаев "то что подразумевал вопрошающий != как понял вопрос отвечающий".
На встрече с заказчиками (особнено общение с менаджерами, желанющими кнопку "счастье"), приходится долго выяснять "а что он имел в виду". А для него "это же очевидно что я имел в виду". Да нихрена не очевидно в 90%.
Кстати, достают задачи в школных учебниках (иногда приходится помогать) в которых решение состоит из "угадай что имел в виду под вопросом автор учебника".
На собеседовании (да и просто в работе), если человек не задают уточняющие вопросы по поставленной задаче - это "что то не так". Либо вообще не понял что, либо понял не правильно "это очевидно" и будет делать не то.
Суть вопроса -- "напишите проход по матрице"
Я общие принципы матричного исчисления помню (проходил когда то). Но в практической работе мне приходилось это применять очень давно (программа раскладки выкроек и управление раскроечным столом). Если меня вот прям сейчас спросят "быстро быстро на время напиши функцию умножения матриц" - пошлю нахер. Общие принципы и зачем это надо я помню. Вывезти из этого формулу - на это нужно время.
Как то сдавал дип курс (PADI). На 40м нужно было решить какую ни будь задачку (тест на склонность к азотному наркозу). И тут мне инструктор подосовывает деление в столбик двух числе (5 и 3 разряда где то). Я его послал (жестом). Да я блин минут 5 на боте/на поверхности вспоминал (а точнее выводил из общего принципа) метод деления.
Так что, задавать какую то узкую задачу (которую знаешь сам, повторил недавно) и считать, что если человек с ней быстро быстро не справился, то все - это чесать свое ЧСВ.
datacompboy Автор
19.09.2025 20:56Именно к этому я и веду: нет, нельзя спрашивать "напишите обход дерева". Какого? Зачем? Чтобы что?
Нельзя спрашивать "напишите A* поиск пути".
Есть практическая ситуация: вот робот, надо почистить весь дом.
Уже пришёл к тому, что надо строить карту, на карте надо помечать где был, через какую сторону не смог пройти между ячейками.
Пришёл к тому, что надо найти ближайшую не чищенную клетку, и надо найти дорогу туда.
Все, пишем заголовок функции, и продолжаем основную логику.
Есть время? Можно подумать и написать её тоже.
Не хватило? Неважно, взяли библиотечную.Если человек умеет думать и декомпозировать задачу - задача решается на ту степень детальности, на которую было времени.
Вопросы уровня "назовите пять протоколов когерентности кеша процессора" оставьте шарашкам, именующим себя универами... Это там проверяют кеш студента, "что из лекций он ещё не забыл со вчера"
mmMike
19.09.2025 20:56Есть практическая ситуация: вот робот, надо почистить весь дом.
Уже пришёл к тому, что надо строить карту, на карте надо помечать где был, через какую сторону не смог пройти между ячейками.В принципе, ответов должно хватить для того, чтобы стало понятно, что же надо.
Если говорить про эту контретную задачу, что при входных условия "нужно накатить на прод хотя бы как то работающее решенеи и быстро".
Все эти рассуждения про карту, "где был" и пр. это разновидность мозгового _а_нанизма.Потому что, как минимум, не прозвучало ни одного вопроса "а какими датчиками" оснащен робот (какие входные данные доступны для алгоритма)
А без ЭТИХ входных условий дальнейшие рассуждения просто вредны. Потому что решается на фактическая задача, а что то придуманное при иллюзии что решаем проблему.Да и решение для прода в условиях "нужно решение и быстро и тестировать некогда" должно быть минимально сложным и желательно шаблонным, что бы уменьшить риски на период до "сделали нормальное тщательно оттестированное решение".
Например, шаблонная классика из самых первых вариантов работы роботов уборщиков: смена направления движения на произвольный угол после срабатывания датчика удара/касания (как минимум такой датчик должен быть в любой подобной платформе робота). И пофиг что он промусолит несколько раз по одному и тому же месту и не будет "карты комнат". Потому задача не составить карту комнат, а "что бы работал и езди и убирал". Пусть и кажется (умозрительно, а не фактически ибо это тоже нужно оценивать на тестах) что это не оптимально.Как показывает лично мой опыт, попытка по быстрому сделать что то сложное, что бы закрыть проблему быстро быстро и выкатить на прод кончается еще большими проблемами.
(кстати, минусы не я ставлю)
datacompboy Автор
19.09.2025 20:56Потому что, как минимум, не прозвучало ни одного вопроса "а какими датчиками" оснащен робот (какие входные данные доступны для алгоритма)
Строго говоря, в задаче четко описано: либо шагает либо не шагает вперёд на 1 шаг, и гарантированно поворачивается влево либо вправо на месте на строго 90 градусов.
Это -- входные данные для алгоритма.смена направления движения на произвольный угол после срабатывания датчика удара/касания
https://habr.com/ru/articles/948784/comments/#comment_28863822
1nd1go
19.09.2025 20:56Хз чего тут заминусовали. норм задача, на чуть-чуть подумать, причем не сильный-то и алгоритмодроч.
rpc1
19.09.2025 20:56Я бы сказал, что задача тривиальная, если был бы задан размер комнаты или хотя бы форма. А мы имеем что форма комнаты это произвольный многоугольник, в котором еще могут быть и барьеры, может иметь формы форму лабиринта. Т.е. довольно много неизвестных. Сходу такое не просто решить.
viordash
19.09.2025 20:56Вопросы, которые так или иначе влияют на решение,
я бы задал и такой вопрос, а что такое чисто в комнатах? Если пока нет конкретного сигнала\понятия, то и алгоритм с random поворот\движения когда-нибудь позволит пройтись по всей площади.
NeriaLab
Изюююмительный текст - 87% сгенерирован. И без анализа видно "кучу маркеров", что сгенерирован
datacompboy Автор
Прикольно, но нет -- текст мой :)
А вот картинку заставить сгенерить правильную было сложно.
Он вместо того, что я хотел, сгенерил мне вот такое:
пришлось давать референс...