...

пятница, 26 июля 2013 г.

Что можно узнать о кандидате по тестовому заданию

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

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


Предлагаю вашему вниманию тестовое задание, которое я уже довольно давно даю кандидатам в компании, где я работаю:



На экране есть сетка M на N из цветных квадратиков. Нужно реализовать на этой сетке следующий эффект — по клику слева на право со скоростью V пробегает волна, меняя цвет квадратиков на другой (единый для всей волны). Эффект должен работать при любых значениях M, N, V. Волна начинается всегда у левой стенки. Одновременно может идти несколько волн разного цвета.

Анимационный пример: http://dl.dropbox.com/u/3601116/wave.swf (покликать по флэшке).






Я не сомневаюсь, что это задание с легкостью сделают все программисты посетители Хабра.

А у меня получилась следующая статистика:



  1. В итоге, задание взяли чуть больше 20 человек.

  2. Пара человек ничего не сделали.

  3. Половина из оставшихся (по моим критериям) с ним не справились.

  4. Кандидаты четко разделились на весьма интересные группы.




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



Disclaimer




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

Задание




Пара слов о задании. Мне было все равно на чём человек его реализует, но сразу же обговаривались два условия:

  • я смогу его запустить,

  • я смогу понять код, если меня разбудят в 7 часов утра.




Группы




Как я уже сказал, кандидаты разделились на отчетливые (пересекающиеся) группы, о которых пойдет речь ниже. Попадание человека в определенную группу само собой уже может многое о нем рассказать, как с хорошей, так и с плохой стороны.
Щаз все будет! Завтра сделаю!



Конечно же, несколько человек рьяно ринулись в бой… но, так ничего и не сделали. На тех, которые пропали сразу, я сразу и забил, но пара человек упорно кормили завтраками, и потом в итоге все равно куда-то слились.

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


Всё просто, надо сделать вот так и вот так



К своему удивлению, я наткнулся на людей, которые мне долго (и объёмно) объясняли, как нужно делать это задание, как построить архитектуру, как оптимизировать отрисовку. Сыпались технические термины и на показ выставлялось знание win32 api. В конце концов, я не выдерживал и говорил прямо, мол, отлично, теперь идите и сделайте это задание.

И знаете что?.. У всех у них возникли с ним сложности.


Вывод: на практике, теория с практикой сильно расходятся. Часто люди создают вокруг себя тонкую оболочку профессионализма, за которой скрывается откровенное дилетантство. Хорошо это вовремя понять и правильно использовать.


Нам в универе преподавали С на втором курсе



На двери этой группы огромными желтыми буквами было написано IMPERATIVE. Как и полагается для решения задачи со второго курса университета, в коде фигурировала матрица, какое-то количество переменных с началами и концами волн, а также переменные i, j, k, m, n и другие в огромных количествах.

Код невозможно было понять, а в большинстве случаев он работал с косяками и/или медленно.



Так, i от нуля до точки распространения волны, j и k симметрично в обе стороны… а это граничное условие… так, а это что за m пошло теперь обратно до границы текущего цвета… так, а это вообще зачем и каким образом работает??! argh! fuck it!





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

Вывод: К сожалению, на перегретом рынке IT бОльшая часть кандидатов обладают весьма посредственными знаниями и хотят сразу кучу денег и BMW. Тестовое задание (даже по факту выполненное) может многое сказать о том, как человек привык писать код и его подход к решению алгоритмических задач.


Add(new Wave());



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

При этом, тоже умудрялись проявлять чудеса ООП Головного Мозга. Но, все же, большинство с заданием справились.


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


Вывод: хороший структурированный по канонам ООП код — признак опытного разработчика, который может грамотно разбить задачу по модулям и собрать из них приложение. Стоит внимательно относиться к признакам overengineering и нарушению абстракций.


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


Я все пишу с нуля



В задании специально не ставились рамки на использование языков, фреймворков и библиотек. Но люди из этой группы упорно выбирали непростой путь решения и сами писали для себя инфраструктуру (например, вывод на экран меша в 3d пространстве). В итоге с заданием справились все.

Вывод: На первый взгляд, кандидаты обладали достаточными знаниями, чтобы с нуля на любимом инструменте реализовать решение задачи и не запутаться в алгоритме. Но сразу же появилось подозрение в «Not Invented Here Mentality» и сомнения в том, что человек сможет эффективно использовать уже существующие наработки и сторонние библиотеки.


Я не ищу простых путей!



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

Человек пытался показать, что может писать многопоточные высоконагруженные системы? На таком тестовом задании?..


— Почему ты сделал это вот так? Ведь, можно было сделать гораздо проще.

— Зачем? Я же могу так!


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


Я все перерисовываю / я не все перерисовываю



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

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


Вывод: С одной стороны, мне было совершенно плевать на лишнюю перерисовку, если приложение работало правильно и быстро. С другой стороны, человек понимал, что можно все сделать оптимальнее. Но, сделал ли он сначала рабочий неоптимальный вариант, а только потом стал его оптимизировать? Зачастую, нет. А это заставляет задуматься.


Мне пофиг, как оно выглядит, если оно компилируется



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

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


Вывод: Очень важно, чтобы человек закрывал таск лишь в тот момент, когда уверен, что работа сделана. Передавая на проверку приложение с очевидным косяком (в надежде, что его не заметят?) он тратит не только своё время, но и время проверяющего. А товарищ, который знает как решить проблему, но не будет ее решать, потому что… — будет вести себя так же и на работе.


Мне пофиг как они будут это запускать



Несколько раз у меня возникли трудности с запуском приложения:

  • Использовались какие-то библиотеки, которые не понятно где быстро взять.

  • Нужно было ставить IDE, SDK и всё на свете, чтобы лишь скомпилировать апп.

  • У меня были явно не все файлы проекта, а с незнакомым языком возиться его настраивать было не много желания.




Понятен был код, но не было возможности увидеть результат. Один из кандидатов магическим образом отвалился на этом этапе…

Вывод: Тоже какое-то неприятное пофигистическое отношение к проверяющему. В итоге на дополнительную переписку была потрачена еще куча времени.


Резюме




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

  1. Какой у него опыт и в каких проектах.

  2. Насколько он абстрактно мыслит.

  3. Способен ли он реализовать простой алгоритм.

  4. Может ли он сам четко закрывать поставленные задачи.




Нахождение в той или иной группе само по себе не означает, что человек плохой или хороший. У меня одни потребности, у вас другие. Я ищу одни качества и настораживаюсь при виде других, у вас же, наверняка, какие-то свои критерии отбора.

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


Конечно, самым популярным был ООП подход с созданием экземпляров класса Wave, апдейтом его по таймеру и очисткой при достижении некоторого критерия. Но, при этом, люди разным образом подходили к распространению волны и перерисовке сетки.


Я рад, что некоторые исходники позволили мне самому чему-то научиться!


Решение




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

На самом деле, изначально его не было. Лишь к концу второго десятка «проверенных работ» я стал задумываться, а как бы я решил эту задачу… Интересной была одна особенность всех решений — все… абсолютно все смотрели на задачу, следуя буква к букве её описанию в начале поста: сетка, по ней пробегает волна…


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


Действительно, пусть клетка принимает целое положительное значение от 0 до бесконечности. Каждый тик клетка принимает максимальное значение своих четырех соседей. Не трудно увидеть, что устанавливая значение одной из левых граничных клеток в N+1 (где N — максимальное значение клеток на сетке), получится ровно нужный нам эффект распространения волны.



for (var i:uint = 1; i < Width-1; i++) {
for (var j:uint = 1; j < Height-1; j++) {
buffer.setPixel(i, j, Math.max(bmp.getPixel(i, j), bmp.getPixel(i-1, j), bmp.getPixel(i, j-1), bmp.getPixel(i, j+1)));
}
}


Можно было бы так всё и оставить, но проще завести еще один массив с рандомными цветами. В итоге, весь код на флэше выглядит так (https://gist.github.com/valyard/6084814):


Посмотреть код


var Size:uint = 10;
var Width:uint = int(stage.stageWidth/Size);
var Height:uint = int(stage.stageHeight/Size);

var bmp:BitmapData = new BitmapData(Width, Height, false, 0x000000);
var buffer:BitmapData = new BitmapData(Width, Height, false, 0x000000);
var display:BitmapData = new BitmapData(Width*Size, Height*Size, false);
var zeroPoint:Point = new Point(0, 0);
var colors:Array = [0xFFFFFF];

addChild(new Bitmap(display));

stage.addEventListener(MouseEvent.CLICK, clickHandler);
stage.addEventListener(Event.ENTER_FRAME, frameHandler);

function clickHandler(e:MouseEvent):void {
addWave((e.localY % display.height)/Size);
}

function addWave(position:uint):void {
if (position == 0) position = 1
else if (position >= Height-1) position = Height-2;

bmp.setPixel(1, position, colors.length);
colors.push(0xFF000000 + int(Math.random()*0xFFFFFF));
}

function frameHandler(e:Event):void {
for (var i:uint = 1; i < Width-1; i++) {
for (var j:uint = 1; j < Height-1; j++) {
buffer.setPixel(i, j, Math.max(bmp.getPixel(i, j), bmp.getPixel(i-1, j), bmp.getPixel(i, j-1), bmp.getPixel(i, j+1)));
}
}
bmp.copyPixels(buffer, buffer.rect, zeroPoint);

var rect:Rectangle = new Rectangle(0, 0, Size, Size);
for (i = 0; i < Width; i++) {
for (j = 0; j < Height; j++) {
rect.x = i*Size;
rect.y = j*Size;
display.fillRect(rect, colors[bmp.getPixel(i,j)]);
}
}
}







Как видите, код минимальный и совсем простой. Но, ничего подобного никто мне так и не показал.

Эпилог




Все имена выдуманы, все совпадения случайны. Если вы когда-либо выполняли такое же тестовое задание, я об этом ничего не знаю, придумал его не я, и вообще, ничего не происходит… нечего тут смотреть… расходимся, господа!

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


Комментариев нет:

Отправить комментарий