Хабрахабр

Школа разработки интерфейсов: разбор заданий для Минска и новый набор в Москве

Сегодня открылся новый набор в Школу разработки интерфейсов Яндекса в Москве. С 7 сентября по 25 октября пройдёт первый этап обучения. Студенты из других городов смогут в нём поучаствовать дистанционно или очно — компания оплатит дорогу и проживание в хостеле. Второй, он же финальный этап продлится до 3 декабря, его можно пройти только очно.

Мы оба разработчики интерфейсов в минском офисе Яндекса и выпускники ШРИ прошлых лет. Меня зовут Юлия Середич, этот пост мы написали вместе с Сергеем Казаковым.

По случаю открытия регистрации в Москве мы публикуем разбор вступительных заданий в предыдущую Школу — здесь, в Минске.
Если проследить историю заданий ШРИ, то мы из года в года проверяли три важных для программиста навыка:

  • Вёрстка. Каждый разработчик должен уметь верстать. Не бывает такого, что у вас есть дядя Сережа, который верстает для всей команды, а вы только пишете скрипты. Поэтому и каждый студент должен показать, как он умеет верстать.
  • JavaScript. Если бы дело ограничивалось вёрсткой, то у нас была бы не Школа разработки интерфейсов, а Школа верстальщиков. Красивый свёрстанный интерфейс нужно оживить. Поэтому всегда есть задание на JS, но иногда оно же является и заданием на алгоритмы — настолько сильно мы их любим.
  • Решение проблем — пожалуй, главный навык разработчика. В создании интерфейсов всё очень быстро меняется. Это как у Льюиса Кэролла: «Приходится бежать со всех ног, чтобы только остаться на том же месте, а чтобы попасть в другое место, нужно бежать вдвое быстрее». Каждый день мы сталкиваемся с новыми технологиями — необходимо с ними считаться и уметь в них разобраться. Поэтому в третьем задании мы предложили разобраться в технологиях, с которыми начинающий разработчик обычно не знаком.

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

Задание 1: Портфолио

Над первым заданием работал дизайнер Яндекс.Коллекций Алексей Черенкевич, который умеет верстать, и его коллега по сервису — разработчик интерфейсов Сергей Самсонов.

Условие

Создайте сайт-портфолио: расскажите о себе, своих работах и ожиданиях от Школы. Сайт должен максимально соответствовать предложенному макету (ссылки на макеты: 1000px, 600px, 320px, спецификация). Нас интересует только вёрстка, поэтому JavaScript просьба не использовать.

При проверке мы будем учитывать:

  • размеры отступов, правильность цвета, начертание шрифтов, размер кегля;
  • семантическую вёрстку;
  • наличие различных состояний элементов: отображение кнопок и ссылок при наведении курсора, выделение активных полей ввода и т.д.;
  • кроссбраузерность (проверяется в последних версиях популярных браузеров).

Плюсом будет:

  • использование современных CSS-решений: flexbox, grid и др.;
  • адаптивная вёрстка;
  • использование пре- и (или) постпроцессоров, сборка, минификация, оптимизация выходного кода;
  • HTML-валидация формы, стилизованная кнопка загрузки файлов.

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

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

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

Критерии

Свёрстанный сайт. Это кажется очевидным, но некоторые ребята пропускали некоторые блоки целиком — то ли хотели сэкономить время, то ли не смогли сделать их. Макет условно можно поделить на четыре основных экрана: главный экран с аватаркой, блок со списком ожиданий от ШРИ, блок с портфолио и блок с контактной информацией. Их можно было делать секциями или просто с помощью div, главное — чтобы в наличии были все четыре блока.

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

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

«Уж сколько раз твердили миру», что ссылка должна быть оформлена как <a>, кнопка — как <button>. Семантическая вёрстка. Не все распознали притаившийся список в ожиданиях от ШРИ, сделав его при помощи тегов div, но это не так страшно. К счастью, большинство кандидатов выполнили и это требование. Например, вместо списка — <section> и <article>. Был кандидат, который вставил все семантические теги, которые только знал — где надо и где не надо. Всё-таки семантика — она про понимание состава своей странички и назначение каждого блока (здесь большинство справилось), а также про использование пре- и/или постпроцессоров (здесь справились немногие, хотя это тоже было в пунктах — чаще всего подключали less и scss).

В задании мы написали, что JS использовать нельзя. Работающий слайдер. Вся магия происходит на уровне селектора #button-N:checked ~ .slider-inner .slider-slides. Тут проверялась способность решать проблемы — слайдер можно было сделать с помощью связки <input> и <label for=”#id”>. Мы можем этим воспользоваться и назначить нужный нам translate на контейнер со слайдами: transform: translate(-33%). Когда мы кликаем по одному из инпутов-чекбоксов, он переходит в состояние checked. Реализацию слайдера можно посмотреть здесь.

Тут всё тоже сводилось к <input name=«accordion» type=«radio»> и похожему селектору: .accordion-item input:checked ~ .accordion-item__content. Раскрывающиеся списки. Реализацию можно посмотреть здесь.

Очень важный пункт. Наличие состояний :hover, :active и :focu*. Пользователь всегда должен получать обратную связь о своих действиях. От него зависел комфорт во время взаимодействия с интерфейсом. Если я нажал кнопку «Позвоните мне» и визуально ничего не случилось (хоть запрос и отправился) — это плохо, потому что затем я нажму её вновь и вновь. Этот пункт проверялся на протяжении всего взаимодействия с анкетой. Не нужно забывать и о том, что на мобильных устройствах нет мыши — а значит, не должно быть hover. В итоге отправится десять запросов и мне перезвонят десять раз. Если ваш контрол не является интерактивным элементом, то при наведении на него курсор останется стандартным. И ещё один момент, который не коснулся тех, кто выполнил пункт про семантику. Не стоит недооценивать cursor: pointer. Это выглядит очень неопрятно, даже если вы прописали реакцию на hover.

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

Многие использовали flex, при этом никто не выполнил задание с помощью grid. Использование новейших технологий. Если где-то вёрстка из-за этих самых flex разъезжалась — увы, дополнительных баллов вы не получали. Пункт засчитывался, если flex использовался грамотно.

Требовалось всего лишь дописать в каждый input формы атрибут required. Валидация формы. Мы прибавляли балл тем, кто валидировал поле электронной почты как email.

Мы ожидали увидеть связку вида: <input type=”file” id=”file” name=”file” class=”inputfile” /> и <label for=”file”>Выберите файл</label>. Cтилизация кнопки загрузки файлов. Есть ещё один распространённый способ — сделать прозрачный input и положить его поверх кнопки. Дальше требовалось скрыть input и стилизовать label. Да и семантически правильнее сделать label. Но не все браузеры разрешают стилизовать <input type=”file”>, и такое решение нельзя назвать в полной мере кроссбраузерным.

Мы проверяли, что всё хорошо, в двух последних версиях современных браузеров (без IE — участникам повезло), а также в Safari на айфонах и в Chrome на андроидах. Кроссбраузерность.

Причём участники с Bootstrap не только получали минус, но и недополучали много баллов за семантику и реализованные элементы. Мы, наоборот, снимали баллы, если кто-нибудь использовал JS или Bootstrap: и то и другое лишало смысла всё задание.

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

У тех, кого мы не приняли, теперь есть свёрстанное резюме — можно гордо прикреплять его ко всем откликам или выложить на свой gh-pages. Первое задание было очень полезным в первую очередь для студента.

Задание 2: Транспортный путь

Автор задания — руководитель группы поисковых интерфейсов Денис Балыко.

Условие

У вас есть карта звёздного неба. На ней указано название каждой звезды, а также расстояние от неё до других звёзд в световых секундах. Реализуйте функцию solution, которая должна принимать три аргумента: объект, в котором ключами являются названия звёзд, а значениями — расстояния до звёзд (в космосе одностороннее движение), а также названия начальной и конечной точки пути — start и finish соответственно. Функция должна возвращать кратчайшее расстояние от звезды start до звезды finish и путь, по которому нужно пройти.

Сигнатура функции:

const solution = function(graph, start, finish) { // Ваше решение
}

Пример входных данных:

const graph = , A: { C: 40, D: 20 }, B: { A: 90, D: 90 }, C: { D: 160, finish: 50 }, D: { finish: 20 }, finish: {}
};
const start = 'start';
const finish = 'finish';

Пример выходных данных:

{ distance: 90, path: ['start', 'A', 'D', 'finish']
}

Примечание: каркас решения находится в папке src/, поместите свое решение в solution.js.

Проверка второго задания была самой автоматизированной и объективной. Большинство ребят догадались, что необходимо реализовать алгоритм Дейкстры. Те, кто нашёл его описание и реализовал алгоритм на JS, — молодцы. Однако при проверке задания мы встретили много работ с одинаковыми ошибками. Мы поискали в интернете по фрагментам кода и нашли статью, откуда участники списали алгоритм. Забавно, что многие копировали код из статьи вместе с комментариями автора. Такие работы получили низкий балл. Мы не запрещаем пользоваться любыми источниками, но хотим, чтобы человек вникал в то, что он пишет.

Критерии

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

Например — наличие единого кодстайла. Были и «человеческие», ручные критерии. Другое дело, если вы чередуете одиночные кавычки с двойными по одному вам известному правилу, а точки с запятыми ставите вразнобой. Никто не снижал баллы за то, что вы используете табы вместо пробелов или наоборот.

На всех конференциях мира рассказывают, что работа программиста на 80% состоит из чтения чужого кода. Отдельно учитывалась понятность и читаемость решения. Так что этот критерий имел значительный вес. Даже школьники проходят кодревью — у своих кураторов и друг друга. Очень радовали комментарии участников — за исключением тех, которые были идентичны комментариям Стеллы Чанг. Встречались работы, в которых не было переменных длиннее одного символа — пожалуйста, не делайте так.

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

Правильное решение:

const solution = function(graph, START, FINISH) { // Всё не бесплатно в этом мире const costs = Object.assign({[FINISH]: Infinity}, graph[START]); // Первая волна родительских нод const parents = { [FINISH]: null }; Object.keys(graph[START]).reduce((acc, child) => (acc[child] = START) && acc, parents) const visited = []; let node; // Ищем «дешёвого» родителя, отмечаем пройденные do { node = lowestCostNode(costs, visited); let children = graph[node]; for (let n in children) { let newCost = costs[node] + children[n]; // Ещё не оценена или нашёлся более дешёвый переход if (!costs[n] || costs[n] > newCost) { costs[n] = newCost; parents[n] = node; } } visited.push(node); } while (node) return { distance: costs[FINISH], path: optimalPath(parents) }; // Возврат назад по самым «дешёвым» родителям function optimalPath(parents) { let optimalPath = [FINISH]; let parent = parents[FINISH]; while (parent && parent !== START) { optimalPath.push(parent); parent = parents[parent]; } optimalPath.push(START); return optimalPath.reverse(); } // Минимальная стоимость из текущей ноды среди непросмотренных function lowestCostNode(costs, visited) { return Object.keys(costs).reduce((lowest, node) => { if (lowest === null || costs[node] < costs[lowest]) { if (!visited.includes(node)) { lowest = node; } } return lowest; }, null); };
};

Задание 3: Календарь событий

Его подготовили разработчики интерфейсов Сергей Казаков и Александр Подскребкин.

Условие

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

Других требований к дизайну нет. Календарь должен выглядеть, как список. После первой загрузки с интернетом календарь должен открываться и функционировать в офлайне. Сделайте возможность ставить напоминания о событии за 3, 7 и 14 дней.

Полезные ресурсы

Расписание фронтенд-конференций:
confs.tech/javascript?topics=javascript%2Bcss%2Bux

Service workers:
developer.mozilla.org/ru/docs/Web/API/Service_Worker_API/Using_Service_Workers
developers.google.com/web/fundamentals/primers/service-workers

Notifications API:
developer.mozilla.org/ru/docs/Web/API/Notifications_API

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

Критерии

Свёрстанный календарь. Да, его всё-таки требовалось сверстать. Были и те, кто понял условие слишком буквально и не вставил ни строчки CSS-кода. Это выглядело не очень лицеприятно, но если всё работало — баллы не снижались.

Это уже задание не на вёрстку, поэтому вшитый в неё список мероприятий не засчитывался. Получение списка событий из источника. Так что требовалось получать данные извне и рендерить вёрстку уже на основе полученного JSON. Всегда можно отменить конференцию, перенести её, добавить новую. Если человек добавлял полифилл для fetch и помечал в readme свой выбор — это засчитывалось как плюс. Важно было каким угодно способом (методом fetch или с помощью XMLHttpRequest) получить данные.

Вот пример service worker с кэшированием расписания на первой загрузке. Регистрация service worker без ошибок и работа в офлайне после первой загрузки. Подробности про service workers, их возможности и способы работы с ними (стратегии работы с кешем, работу в офлайне) можно посмотреть здесь.

Нужно было разобраться в Notifications API, ссылка на который находилась прямо в задании. Возможность установить напоминание, чтобы оно действительно сработало через 3, 7, 14 дней. Принимался любой работающий вариант: хранение в localStorage, IndexDB или периодический опрос сервис-воркером. Мы не ждали какой-то конкретной реализации проверки того, наступило ли время для пуша. Не менее важно было получить пуш после того, как страницу закрыли — и открыли через какое-то время. Можно было даже сделать пуш-сервер (вот пример), но в офлайне он бы не работал. Круто, когда ребята думали о проверяющих и делали возможность получить пуш прямо сейчас — чтобы не ждать 3 дня. Если напоминание «умирало» одновременно с закрытием страницы, решение не засчитывалось.

Мы проверяли наличие файла manifest.json с правильными иконками. Возможность вынести иконку на рабочий стол (PWA). Тогда при попытке установки возникала ошибка вида «нужна другая иконка». Некоторые ребята сделали этот файл (или оставили сгененерированный в CreateReactApp) — но не добавили верные иконки.

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

Если прямо в консоли был индикатор, что что-то не так, а участник не обращал на него внимание, то мы снимали баллы. Ошибки в консоли.

Итоги

Забавное в решениях участников:

  • Одна анкета содержала следующий текст: «Собрать реакт-приложение помог друг программист. Я его закидывал вопросами что как и почему, он рассказывал. Очень понравилось, хочу больше узнать об этом». Мы всем сердцем болели за эту анкету, но к сожалению, друг кандидата не очень помог ему сделать работающее приложение.
  • Один кандидат прислал ссылку на GitHub, где лежал RAR-архив — сложно это как-то прокомментировать. 🙂
  • Ещё один кандидат в комментарии первой строчки файла solution.js честно признался, что скопировал алгоритм.

Мы получили анкеты от 76 кандидатов и отобрали из них 23 человека. Нам присылали анкеты не только из Минска, но и из Москвы, Санкт-Петербурга и даже Татарстана. Некоторые ребята удивили своими нынешними профессиями: один из них судмедэксперт, а другой — студент медицинского вуза.

С первым заданием участники справились в среднем на 60%, со вторым — на 50%, а третье оказалось самым сложным и его выполнили в среднем на 40%. Получилось интересное распределение успешности выполнения заданий.

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

Главное в этот момент — сесть, хорошенько вчитаться в условия и начать делать. Я помню, как увидела своё вступительное задание в ШРИ два года назад и подумала, что мне его никогда не решить. Например, в условие третьего задания (самого сложного) мы добавили ссылки на service workers и Notifications API на MDN. Оказывается, в условиях содержится практически 80% решения. Студенты, которые изучили содержимое ссылок, справились без труда.

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

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»