11512594484_0e24c9b0b0_k (1)

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

Больше кода, не значит лучше

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

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

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

Не нужно забывать о том, что код тестового задания будет читать человек, у которого, возможно, не так много времени на его доскональное изучение. Краткость — сестра таланта!

sheldon

Нет единого стиля

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

psy-gangnam-style-3

Создание «велосипедов»

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

nByxIuS

Зачастую такое желание связано с незнанием (или плохим знанием) стандартной библиотеки использующегося языка или тех библиотек и фреймворков, которые применяются в решении. Наиболее популярные велосипеды — isdigit, to_string, directory_iterator и т.д. Не нужно забывать, что многие компании были бы не против использования сторонних библиотек и решений в проекте: чтобы не проделывать работу, которую уже давно сделали другие разработчики, стоит не стесняться уточнить этот момент как можно раньше.

«Плохие» наименования сущностей и некорректная декомпозиция классов и функций

Пояснять здесь что-то излишне — печально, когда путь до директории называется s, а число, прочитанное из файла, хранится в переменной под названием mynumber.

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

tumblr_o0z5u9AgJE1ukes1io1_500

Неверная организация логирования

Частично этот пункт связан с предыдущим, то есть с неспособностью продумать грамотную декомпозицию функций. Печально видеть, когда get_file_content(const std::string& file_name) самостоятельно пишет в лог в случае ошибок при открытии файла или считывании из него данных. Гораздо лучше пробрасывать информацию о произошедшей ошибке уровнем выше, где она и будет обрабатываться должным образом.

Многим кандидатам будет полезно узнать, что функция std::ifstream::read сама пишет в лог.

635772007494184422-98950927_drumming

Применение слишком низкоуровневых инструментов

Как это ни странно, но многие разработчики так и норовят использовать в своих решениях такие low-level API, как WinAPI или POSIX API, даже тогда, когда аналогичные средства доступны и в стандартной библиотеке соответствующего языка. Некоторые делают это ради того, чтобы показать, что работа с низкоуровневыми интерфейсами не является для них существенной проблемой, кто-то стремится к «увеличению производительности», и лишь немногие делают это из-за того, что они по-настоящему хорошо знакомы с данным API.

006e74b9b74d9f315b331fcf41e95e65

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

Преждевременная оптимизация

Частично этот пункт уже был затронут выше, но здесь речь идёт не столько об использовании WinAPI / POSIX, сколько об усложнении общей архитектуры приложения ради получения решения, которое работает лишь немного быстрее.

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

bX7Eo

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

Создание неуниверсальных решений

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

Что самое интересное, зачастую на написание универсального решения будет затрачено немногим больше времени, чем на написание частной версии. Так зачем мучать себя и других?

1460095348910072

Использование магических чисел

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

Иногда это 256 для буфера, содержащего путь до файла, 8 в качестве размера thread pool и 512 в качестве размера для содержимого файла.

Magic-GIF-Image-19

Следует помнить, что в большинстве API есть соответствующие инструменты для подобных сущностей — в WinAPI есть MAX_PATH, в стандартной библиотеке C++ и boost есть hardware_concurrency, а размер буфера, хранящего содержимое файла, можно и не задавать вовсе — вместо него достаточно просто использовать std::string.

Отсутствие RAII

Если речь идёт о языках, по своей природе располагающих к автоматическому освобождению ресурсов, этим надо пользоваться в 99% случаев. Вместо new и delete — умные указатели, вместо fopen и fclose — std::fstream… В общем, идея понятна.

tumblr_nw79bfqxP51uh3d32o1_500

Автор: Никита Трофимов, разработчик ПО

Другие интересные статьи в блоге GMS: