Опыт разработки тестового задания на React для Aviasales

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, я хотел поделиться опытом разработки тестового задания для Aviasales.


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


Само тестовое задание вы можете найти тут по ссылке.


Вот тут ссылка на мой репозиторий выполненного задания.


Я ограничил себя в выполнении задания в один день (правда небольшие доработки я все же делал уже после публикации: дорисовывал эскиз, так сказать).


Что я выбрал для разработки:


  1. Я выбрал NextJS как основу, так как возиться с настройкой среды под Webpack мне не хотелось, да и задеплоить сам проект можно парой кликов.
  2. Я хотел писать быстро и выбрал пакет React-IOC в связке с MobX, вместо Redux. Это пакет, позволяющий писать приложения через сервисы, напоминающие сервисы angular.
  3. Я использовал Web Worker, чтобы не было лагов в интерфейсе во время сортировки большого объема данных.
  4. Я не использовал Typescript с целью не писать дополнительный код с бесполезной растратой времени на тестовое задание.
  5. Исходя из пункта 4 я так же не писал тестов.
  6. Я добавил в проект два дополнительных пакета: debounce, RxJS. Первый нужен для создания простых callback, например смену состояния загрузки, чтобы не показывать спиннер загрузки, если загрузка занимает крайне мало времени. Второй пакет я всегда использую для создания сценария действий, например, для обработки состояний в случае ошибки при отправке запроса на сервер.

Порядок действий первой стадии разработки:


  1. Проинициализировал репозиторий.
  2. Проинициализировал проект NextJS.
  3. Добавил базовую страницу index с сообщением Hello World.
  4. Создал сервис ticket.provider, который взаимодействует с api сервером.
  5. Создал сервис ticket.service, который инжектирует ticket.provider и заполняет обсервер с массивом отображаемых билетов
  6. Создал ticket.filter.service, который хранит в себе отфильтрованные данные, инжектирующиеся из ticket.service через @computed

Вторая стадия разработки:


  1. Создал компоненты и расписал стили к ним, используя макет, предоставленный в репозиторий задании.
  2. Сделал спиннер загузки и проставил его значение из сервисов.
  3. Подсоединил всю сервисную логику с компонентами.
  4. Добавил утилиты с форматированием данных, таких как время и деньги.

Тут я решил попробовать интерфейс «на ощупь» и нашёл недоработки при использовании приложения:


  1. Имеются подтормаживания интерфейса при изменении фильтра и сортировке, поэтому я перенёс хранение, получение и фильтрацию данных в Web Worker, после чего лаги полностью пропали.
  2. Спиннер не исправлял прыгающей анимации строк, вследствие чего я заменил его на визуальное отображение строк с анимацией мерцания.
  3. Для простоты рендеринга данных я перенёс вызовы форматирования данных в Web Worker, это уменьшает нагрузку на рендеринг компонента

Тут я закончил свою работу и под конец дня отправил задание на проверку.


Так как я ограничил себя во времени, я не стал оптимизировать приложение далее, а именно я не стал использовать React.memo(...). Я так же не стал заменять window и router на инжекторы в сервисах. За это простите меня, это недоработка.


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


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


Давайте разберём замечания по пунктам:


[1]. «Работа выполнена очень не аккуратно»


Не вижу где, не аргумент, так как нет примеров.


[2]. «Кажется, при разработке преследовались цели «оно же работает», и игнорировалось «как это работает»»


Не аргумент, нет примеров.


[3]. «Если исходить из наших требований к уровням кандидатов, то реализованное задание едва дотягивает до middle.»


Какие требования? Где они?


[4]. «Мы ожидаем, что фильтры не будут захардкожены и будут подстраиваться под данные. Если зайти на aviasales, то можно увидеть, что билеты показываются сразу как появляется первая партия, а не дожидаемся загрузки всех.»


Этого требования не было в приложенном задании. А само приложение Aviasales, на мой взгляд, не обладает эталонным интерфейсом, и прыгающие билеты не являются наилучшим решением.


[5]. «Почему filter.service знает об router и window? Он не должен управлять состоянием приложения и иметь таких зависимостей вообще.»


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


[6]. «Web Workers. В чем профит? В данном кейсе тратится много времени на асинхронные операции, а сэкономленную нагрузку в main thread тратим на serialize/deserialize объектов (и observable объектов). Если речь идет об оптимизациях, то стоило начинать не с выноса в web worker, а исправить проблемы в main thread.»


Как можно сделать обработку больших объемов данных в main thread и при этом без лагов интерфейса? Мне будет крайне интересно узнать, если есть у кого пример, то пишите в комментах. Видимо я что-то упускаю.


[7]. «Нет смысла добавлять в проект rxjs только для реализации retry и последовательной загрузки.»


Почему нет смысла? В RxJS можно импортировать строго необходимые функции, если tree shaking пакетов и функций не работают должным образом, то это не увеличит размер приложения.


[8]. «Проект невозможно масштабировать и поддерживать. Конструкции типа (ticket.segments || [{}, {}]).map((ticket) => ...) сильно громоздские.»


Давайте пройдёмся по критериям масштабирования и поддержки: доступность (сервисы решают эту проблему), риски (ticket.segments || [{}, {}] — как раз пример того как обходиться со случаями, если input не содержит данных. Пример плохой, но подход nullable structure как минимум, но я стараюсь соблюдать), чистый код (ну как минимум я знаю что это :), хотя старался писать все как положено). Вроде все схвачено, опять не аргумент.


[9]. «Сложилось впечатление, что абсолютно нет понимания как работает React под капотом. Много критичных ошибок по работе с props у компонентов, которые очень плохо влияют на производительность. Игнорируются механизмы оптимизаций в React.»


Не могу понять где эти описанные проблемы. Какие именно механизмы?


[10]. «Создание функций-обработчиков внутри render.»


У меня вообще нет никаких функций обработчиков в render, кто нибудь понимает о чем тут написано? Даже обработку форматирования я перенёс в Web Worker


[11]. «Создание нового пустого объекта и передача его в билет.ticket-list-loading.jsx:10 или Ticket.tsx:24


Тут нет ничего критического, кроме моей лени выносить отдельно loading-ticket компонент. Передача пустого объекта не нарушает никакой принцип программирования, кроме того что тут нужно было сделать это через ticket = ticket || {}; но это сугубо из-за того что время на разработку было ограничено одним днём и исправлять все незначительные недочеты потребовало бы больше времени.


[12]. «Индексы массива как ключи на списке билетов»


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


Ну и напоследок вывод: «В общем, в React не получилось (
Итого: для middle уровня мы ожидаем, что с react не будет критичных проблем, но в выполненной работе их довольно много.»


Тут уже без комментариев...


Благодарю, если дочитали до конца. У меня сложилось очень негативное мнение о Aviasales, поэтому выкладываю все тут, чтобы вы могли сами оценить, стоит ли вам связываться с ними или нет.


Полный текст сообщения:


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


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


Далее, более технические моменты.


  1. Почему filter.service знает об router и window? Он не должен управлять состоянием приложения и иметь таких зависимостей вообще.
  2. Web Workers. В чем профит? В данном кейсе тратится много времени на асинхронные операции, а сэкономленную нагрузку в main thread тратим на serialize/deserialize объектов (и observable объектов). Если речь идет об оптимизациях, то стоило начинать не с выноса в web worker, а исправить проблемы в main thread.
  3. Нет смысла добавлять в проект rxjs только для реализации retry и последовательной загрузки.
  4. Проект невозможно масштабировать и поддерживать. Конструкции типа (ticket.segments || [{}, {}]).map((ticket) => ticket) сильно громоздские.
    React:
    Сложилось впечатление, что абсолютно нет понимания как работает React под капотом. Много критичных ошибок по работе с props у компонентов, которые очень плохо влияют на производительность. Игнорируются механизмы оптимизаций в React. Тезисно о проблемах:
  5. Создание функций-обработчиков внутри render.
  6. Создание нового пустого объекта и передача его в билет. ticket-list-loading.jsx:10 или Ticket.tsx:24.
  7. Индексы массива как ключи на списке билетов.
    В общем, в React не получилось :(

Итого: для middle уровня мы ожидаем, что с react не будет критичных проблем, но в выполненной работе их довольно много.

Источник: https://habr.com/ru/post/468715/


Интересные статьи

Интересные статьи

На текущий момент существует множество готовых фреймверков для разработки приложений с использованием микросервисной архитектуры. Как правило, фреймворки разделяются на два типа — под...
Использование контроля версий для разработки в ERP-системе MS Dynamics AX — штука довольно неоднозначная. Кто-то не использует совсем, кто-то использует встроенную систему контроля версий...
Говорить о том, как не надо заказывать сайты, у разработчиков уже язык устал. Но совершенно справедливо подмечено, что свой опыт крайне трудно вложить в чужую голову, а посему неопытные заказчики...
Испугавшись цен на астротрекеры и экваториальные монтировки именитых брендов, нагуглил несколько вариантов. У каждого был свой + и -, но всех их объединяло одно — дверь сарая. Или как говорят анг...
Каждый лишний элемент на сайте — это кнопка «Не купить», каждая непонятность или трудность, с которой сталкивается клиент — это крестик, закрывающий в браузере вкладку с вашим интернет-магазином.