Комментарии 67
Не смотрел код, но могу сказать только одно — рассматривайте по жизни такой кейс ещё и с обратной стороны. Если Вы не прошли собеседование — возможно это проблема компании. Устраиваясь на работу — вы заключаете двухсторонний договор. И как правило, все проблемы всплывают только, когда вы соблюдаете этот договор долгое время.
Кто его знает, чем они руководствовались. Может теперь сядут и будут внимательно изучать ваш код и перенимать ваш опыт. Может рынок исследуют. Может у фронтэндера жена в это время рожает… а ему там ваш код дали;)
Вообще, мой совет — не заморачиваться на этот счёт. Выше нос!
гораздо сложнее, чем могло бы быть
В такой ситуации совершенно нормально попросить пояснить – что именно могло бы быть проще, и как ту же задачу (в общих, конечно, чертах) решил бы рецензент. Может, вы и правда перемудрили, кто знает.
нам важно, чтобы простые вещи кандидат делал просто.
Если об этом речи не шло, то можно (я бы так и сделал, по крайней мере) ответить что-то вроде «Делал как прототип большого проекта с возможностью развития, что привело к усложнению; готов переделать, имея в виду, что это маленькая часть большого проекта»
Буквально за неделю до этого, я делал техническое задание для другой компании, где так же фигурировала игра, но с другой логикой. Игра называлась Калах.
Может имело смысл рассмотреть уже существующие на рынке решения, подобные этому варианту Game: Axiom Development Kit (были и статьи по этому решению на Хабр) и уже дальше изобретать вариант своего «велосипеда»?
Заодно можно узнать особенности чужой платформы. Неужели Spring/Boot не допускает другой структуры проекта?
Я не знал, что мы тут советы по развитию ЯПов собираем.
Да, не нужно менять кодстайл каждый вторник, но что-то приятное или полезное со временем утянуть может быть стоило бы?
Почти все конторы, которые я поведал, используют общепринятый стандарт. И только одна контора, где что-то своё… ну так там и лид разработчик, с которым работать не захочешь…
И не понимаю фразы «искать нужный класс». Навигация IDE мне помогает в 100% случаях поиска, если знать что искать.
И колаборация с чем/кем нам дала лямбды? Это не колаборация дала, а просто на волне хайпа разработчики языков добавляют то, что пошло в других языках, та же самая функциональщина например. Но симбиоз негибкого и устоявшегося ООП языка, на подобие джава, с функциональным стилем лично у меня оставляет некоторый осадок после использования. Я сам любитель функционального программирования, но в джава реализация мне не понравилась. То ли дело котлин/скала. Поэтому не нужна колаборация в том, что уже испытано и привычно, я считаю
А я наоборот, пришел из явы и называл (и до сих пор называю) в C++ переменные через camelCase (или как он там называется). Вот и пример удачной коллаборации.
> Это не колаборация дала, а просто на волне хайпа разработчики языков добавляют то, что пошло в других языках
Окей, почему бы не поднять хайп на более человечское именование сущностей?
я думаю, что если вы за стандарты и ясность, то в первую очередь Вам не стоит использовать размытую, эмоционально заряженную лексику. Вы просто нахваливаете некий свой подход, который почерпнут неизвестно откуда, его суть неясна, но вы пытаетесь продать собеседнику, что он лучше. Это несерьезная и довольно-таки бессмысленная реплика.
Насколько я помню структуру зачастую задает бут-стартер, и если она работает у людей, то трогать в ней ничего не надо. Если что то не устраивает в бут-стартере, пишите авторам проекта issue и они посмотрят.
Ну вот например Роберт Мартин в книге «чистая архитектура» говорит
> А что кричит архитектура вашего приложения? Увидев высокоуровневую структуру каталогов и пакетов с исходным кодом, услышите ли вы, как она кричит: «Это медицинская система», или «Это система учета», или «Это система управления складским хозяйством»? Или вы услышите: «Rails», или «Spring/Hibernate», или «ASP»?
Плюс неоднократно слышал похожие мысли по разным углам.
Мне показалось, что здесь — именно проблема некачественной обратной связи, в первую очередь. Было странно получать такой фидбек от подобной компании. Хотя, kinall, ваш комментарий заставил задуматься — А что если это часть поведенческого интервью? Но какой-то жёсткий стиль интервью получается :) Всё-таки черным по белому написали, что решили не продолжать, и я принял это за вердикт.
В любом случае, хотелось поделиться подходом. Скажите, велосипед — ну ок. Этот велосипедный цикл в нашем ремесле бесконечен :) Может быть, кому-то окажется полезным.
что если это часть поведенческого интервью?
Вряд ли. Слишком круто) Вопрос про сложность я бы задал исключительно из любопытства, а переписать задание предложил бы только в том случае, если вакансия/компания очень уж «вкусная». Потому что, как тут уже не раз сказали, в целом ответ интервьюера не очень корректный.
Лично я призываю либо делать платное тестовое, либо получить фитбек в ходе очного собеседования: Холивар. Тестовые в разработке: нужны ли, какие и когда?. Кстати там дальше по ходу видео участники упоминают, что в некоторых HR методичках написано всеми силами не давать фитбек.
Видимо задание сделано на тех технологиях, которые знал кандидат, хотя мы никакого redux и redux-saga
А ожидалось что видимо напишет на чём то неизвестном ему что ли? Такое чувство что человек не осилил код, с учетом потраченного времени кандидата это просто обычное хамство.
А есть архитектура на UML, а то для не Java программистов действительно трудно что то понять. Я бы взглянул, как вы смоделировали игру и тогда было бы ясно, просто это или сложно.
Посмотрел бегло ваш код, обе части: фронт и бэк, не нашёл никаких явных проблем. Вполне стандартный для индустрии набор архитектурных решений. Да, есть моменты из разряда «я бы тут сделал по другому», но ни одного «за такое по рукам нужно бить». В разных местах видно умение работать с различными техниками, типа streams в джаве, генераторы в js, понимание CI. Видно стремление делать правильно. Уж точно никакого оверинжиниринга.
Если ревьюверам было сложно понимать этот код, это скорее характеризует их уровень. Если у них действительно сложный продукт, то чего имеется ввиду под «делать просто», писать говнокод забив на декомпозицию?
Возникает впечатление (имхо), что у них там некое легаси написанное говнокодом побыстрому и они избегают опытных разработчиков, которые привыкли делать всё правильно и будут тянуть одеяло в сторону рефакторинга, вменяемой архитектуры, хороших практик.
Ну явно лапша, дальше лезть не захотелось.
Ну вы хотя бы логику объясните вот этих кусков связанного кода:
```
this.threshold = Math.min(threshold, BOARD_DIMENSION);
…
this.threshold = threshold;
…
public void setThreshold(final int threshold) {
this.threshold = threshold;
}
…
public boolean isInRange(int x, int y) {
return x >= 0 && x < BOARD_DIMENSION && y >= 0 && y < BOARD_DIMENSION;
}
```
Вот как минимум за это это я бы Вас развернул.
github.com/itallix/noughts-crosses/tree/master/server/src/main/java/io/karniushin/tictactoe/core/service/handler/rule/condition
6 файлов, с иерархией классов и вложенными классами чтобы проверять условие Х в ряд, по моему это оверкилл, особенно что тестов я на это не нашёл (может искал не там, я не джавист).
Ещё например мне кажется что проверяется только ownerId, а значит можно ходить за других игроков.
Проблема которую я вижу здесь — несовершенство коммуникации.
Люди дававшие вам задание не обозначили явно ограничения и пожелания к ожидаемому решению. И видимо не договорились даже между собой.
Вы порезвились на славу, выдав нечто за границами ожидаемого. Интервьюеры увидели подход явно отличающийся от стандартного, и решили не разбираться. Предполагаю что выбор у них есть.
Фидбек они конечно тоже завалили
Еще не дойдя до финала статьи поймал себя на мысли:
«О господи, зачем так сложно».
Редакс с сагами для крестиков-ноликов это что-то похожее на шутки про java enterprise hello world" — несоответствие масштаба задач и масштаба применяемых инструментов.
Сами инструменты при этом нормальные.
И я могу понять интервьюера. Умение пройти по тонкой тропинке между слишком простой и переусложненной архитектурой — это в общем одна из основных задач которую приходится решать разработчику высокого уровня. И одна из распространенных задач от бизнеса не «сделать архитектуру на века», а «mvp asap». Зачастую приходится угадывать и вытягивать из бизнеса, что это именно это за задача — попробовать и выкинуть, или долгосрочная ставка.
Вот интервьюер решил что Вы с этим не справились.
Лично я в вашем коде не вижу ничего страшного.
Эти решения были бы прекрасным поводом поговорить. Но они действительно слегка странные. И вдруг у них действительно большой выбор. Или они ищут «того самого единственного». Тогда не рассматривать дальше странное решение — может быть разумно, в целях оптимизации времени собеседующих.
Насколько сложно код читать и менять — зависит разумеется в том числе и от того насколько люди читающие знакомы с теми или иными библиотечными паттернами и подходами. Я глянул одним глазком, по мне Ваш код вполне нормального качества, особенно для тестового задания. Но людям не использующим редакс вообще это может так не казаться.
И пара слов про сами технологии:
— редакс и саги — это в общем проверенный временем подход, использовать его в реальном приложении ок, особенно если надо быстро стартовать. Но в целом в окружающем меня мире фронтенда редакс в основном не любят. Я сам его оч не люблю.
— «там просто заоверрайжены стили из библиотеки это плохой способ писать CSS». Эммм, крайне странный фидбек если честно. У вас там antd затянут и оверрайдить в нем стили — обычный подход. Я понимаю что в больших проектах так не делают, но там и юи кит кастомный, а не антд. Есть ощущение что это один из поводов отказа, а не причина.
Умение пройти по тонкой тропинке между слишком простой и переусложненной архитектурой — это в общем одна из основных задач которую приходится решать разработчику высокого уровня.
А я бы сказал по другому — простота реализации с одной стороны, и недостаточная гибкость и расширяемость решения — это два разных конца одной палки, и какой бы классный программист не был, какой бы хороший код он не написал — его всегда можно упрекнуть в том, что код или переусложнен (а значит можно было сделать быстрее и проще), или его код не расширяем (сделано слишком просто).
Кто-то скажет, что нужно соблюдать золотую середину — вот только практика показывает, что золотая середина у каждого своя.
Если кто-то вас в подобном начинает упрекать — говорите ему, пусть в ТЗ пишет нужную простоту, или нужную расширяемость в дальнейшем. Иначе можно быть всегда виноватым.
public GameSession newGame(String username, String gameName, Integer threshold, boolean isX) {
//тут гонка, два потока могут добавить игру с одинаковым именем
validateGameName(gameName)
//тут аналогичная проблема
Player initiator = playerRegistry.registerPlayer(username);
...
return session;
}
@Override
public GameSession connect(UUID gameId, String username) {
//тут еще одна гонка, игру могли удалить уже после checkGameExists
//в текущем варианте вроде такого быть не может, но как только появятся
//таймауты, то будет
checkGameExists(gameId);
ReentrantLock lock = locks.get(gameId);
...
}
TurnHandler + GameRule это попытка выдать желаемое за действительное. Судя по коду, есть идея, что «широкий класс игр можно моделировать в виде chain or responsibility». Эта неверное предположение ведет к слишком сложному коду, который не оправдан для такой простой игры как крестики-нолики. Становиться тяжело судить о том, верно ли вообще закодированы правила игры. Перепутай местами два Rule и все посыпется. Печаль в том, что более сложные игры в chain of responsibility не впишутся, в итоге вся эта сложность не нужна.
Я бывал в вашей ситуации, когда делал тестовое задание для Revolut. Мне тоже отказали, и даже фидбека не дали, но знания и навыки, которые я получил, пригодились на собеседовании в Яндекс.Маркет.
P.S. Я в тестовых заданиях обычно использую незнакомые мне технологии, чтобы поковырять что-то новое. + оставляю много комментариев для потенциальных ревьюверов.
Для себя вынес, что даже когда все кажется понятным, имеет смысл заранее договориться об использовании тех или иных вещей. Задать лишнюю пару-тройку вопросов (даже ради вопросов). Коммуникация, как всегда, решает :)
Всем спасибо за конструктивные фидбеки! Я не получил их в процессе интервью с этими ребятами, но, я получил их с Вами! А именно этого мне и хотелось после проделанной работы :)
Да еще и набажили в единственном интересном месте бека. Стейт и мультипоточность. Точнее даже не набажили, а просто забыли про это почти везде. В проде будет взрываться красиво и внезапно.
Кусочек с локами выглядит как проба интересных для вас вещей, а не что-то нужное в этом месте. Там обычных syncronized(gameId) блоков с головой хватает.
Пиши только тот код который нужен сейчас (с) Гугл
Касательно конкретно фронта — сложно всерьез воспринимать человека, который говорит, что redux+saga — это взросло, энтерпрайзненько, и поддерживаемо. Вообще, если человек применял редакс на больших проектах и при этом не плюется на редакс — что-то с ним не так. Самое безобидное — не применял в жизни ничего кроме редакса.
Ну а сага под соусом удобства и «теперь тебе просто не надо думать про асинхронность» превращает глобальный редакс в глобальную же лапшу вида «всё может дёргать всё». Неудивительно, что к этому сразу же надо писать тесты (а у вас для саг тесты не написаны, кстати).
PS: А еще попробуйте погонять сагу на ИЕ11. Узнаете много чудного.
PPS: А еще я бы очень сильно не доверял человеку, который на тестовое задание пишет монструозный enterprise-grade код, но который при этом не взял на фронт typescript. Это то ли мазохизм, то ли просто ненависть к себе. Даже не смотря на то, что из-за саги там ни о каких серьезных статических гарантиях типов в бизнес-логике говорить будет нельзя (зато в остальных местах всё-таки что-то будет).
заоверрайжены
Любопытства ради — из какого языка это слово?
Я написал некоторый набор кастомных итераторов, каждый из которых занят идентификацией выигрыша — по горизонтали, по вертикали, основная и побочная диагонали.
Ынтырпрайзненько...
Как мне кажется читать такой алгоритм проще и в любой момент можно включить/выключить конкретный итератор, что делает архитектуру достаточно гибкой.
Один метод с пачкой циклов читать было бы проще.
На техническом собесе было два тимлида разных проектов, они спросили о моем опыте, я рассказал. Других вопросов от них не последовало, только сразу высказали пожелание сделать тестовое. Я отказался со словами, что итак можно посмотреть мой гитхаб, я специально его обновил, сделал несколько маленьких проектов, где можно посмотреть мой стиль и сделать какие-то выводы, ведь и тестовое будет примерно в похожем духе. Они обещали посмотреть, а я обещал все же посмотреть их тестовое, которое они мне пришлют. На том и разошлись, все интарвью заняло минут 10, где 3/4 времени я рассказывал о себе, блестящая скорость оценки кандидатов у них.
После собеседования посмотрел на тестовое задание. Оно мне пришлось по душе, короткое и интересное, не должно занять более 4 часов. Надо было сделать простой клон Redis c get/set данных через очередь. Нужно реализовать «Storage и Log persistence». Я это понял как сохранение в storage файл каждые n секунд и запись какого-то лога запросов, который при падении процесса может потом применится к этому storage файлу при повторном запуске приложения, чтобы не потерялись никакие данные. По моему разумению примерно так работает Redis. Реализовал это дело на Typescript, на Node.js-фреймворке NestJS c RabbitMQ в качестве очереди, упаковав в Docker-контейнер. Короче, все стандартно. Потратил в районе 6 часов, как раз чуть больше запланированного. Можно потратить еще два раза по столько же, сделав полноценную документацию и тесты, но я остановился на минимально-достаточном варианте.
Отправил HR-ше, и где-то через неделю получил ответ, что со мной дальше не готовы продолжать общение. На мой вопрос о каком-либо фидбеке по тестовому получил молчание. Ну и ладно, судя по собесу не особо надеялся на фидбек.
Тут можно посмотреть код, если кому интересно Даже если это недостойное внимания убожество и отвергли в ноунейм конторе, я все равно его не стыжусь =)
С одной стороны все это неприятно:
— они не ценят мое время, раз не общались со мной на интервью и так продинамили с отзывом по тестовому; причем, как показывает практика, чем компания ноунеймовее, тем хуже подобные процессы
— какой раз обещаю себе соглашаться только на оплаченное тестовое, но из-за интересности самой задачи все же делаю его; даже зная, что возможно не получу фидбека и тем самым поощряя такой подход использования времени кандидатов на фильтрацию
С другой стороны очень даже неплохо:
— задача довольно интересная и в меру быстрая, сильно я не пострадал по времени
— кода не стыжусь, убожество и другой мой код потом смотрели в других компаниях и работу я нашел успешно, выбирая из нескольких офферов
— любой опыт полезен, опять же =)
какой раз обещаю себе соглашаться только на оплаченное тестовое
Что машет сделать интересное тестовое в стол или гитхаб? Агитирую только за платные тестовые: https://youtu.be/sptmCVTCyzU. Отпишите и вы комментарием свою историю.
Если вы претендуете на опытных экспертов, постарайтесь обойтись без тестового (особенно при найме опытных кандидатов senior уровня). Мотивация делать тестовое есть только у junior или тех, кто мечтает о работе в вашей компании.
По этому, если вы не ждут (а по вашему github вы не джун) — лесом эти тестовые! Уважайте себя и свое время, и будут уважать вас.
В этом плане выгоднее смотрятся задачи с явным акцентом на конкретный навык/навыки — реализовать алгоритм, написать сервис (без UI), — либо когда интервьювер явно указывает на второстепенность каких то аспектов.
Или я столкнулся с технической незрелостью интервьюеров?
Вы столкнулись с предвзятостью, которая заключается в двух моментах.
Во первых, вот что говорят сами HR о тестовых:
Если вы претендуете на опытных экспертов, постарайтесь обойтись без тестового (особенно при найме опытных кандидатов senior уровня). Мотивация делать тестовое есть только у junior или тех, кто мечтает о работе в вашей компании.
Получается, если хороший программист согласился на выполнение тестового (у вас история на github идет с 2012 года — джуниором никак не назвать) — то он уже принизил себя.
Второй момент, о котором они прямо написали:
сложнее, чем могло бы быть
Т.е. они ожидали простое тяп-ляп решение на пару файлов. Ваш случай не уникальный — когда программист делает тестовое задание не тяп-ляп за часик, а качественно (еще и с тестами) — то это воспринимается хуже. Потому что «чет сложно, много кода, и вникать лень».
Как там говорят:
Не бросайте жемчуга перед свиньями
В текущем случае можно сказать: «Не бросайте жемчуга перед теми, кто его не ждет»
На самом деле интервью вещь двусторонняя, у команды есть свои критерии и если вы по ним не подходите, то и команда вам не подходит. Хотя конечно желательно предупреждать о желаемом стиле.
С одной стороны тестовое задание это маленький пример большого проекта. С другой стороны как по мне сага тут уже перебор, да и редакс для таких мелких проектов не рекомендуют. Кроме того домен на стороне сервера в данный момент не домен вообще, а только набор датаклассов, которые без правил бесполезны, часть реального домена находится в сервисах (но остальная часть сервисов это не домен).
Кстати зачем возня с реентрант локами, когда каждая сессия это один и тот же инстанс, на котором можно синхронизироваться?
Оверинжиниринг это усложнение кода без явной необходимости, дело не только в читабельности. Этот код необходимо будет поддерживать, изменять, исправлять, и вы будете тратить на это больше усилий, чем на более простой код, допуская больше ошибок.
На сколько понял — в тестовом у автора ничего такого сказано не было, соответственно некорректно обвинять его в том, что он выбрал «энтерпрайз» подход, а ожидался иной.
players.values().stream()
.filter(p -> p.getUsername().equalsIgnoreCase(name))
.findAny()
.ifPresent(p -> { throw new IllegalArgumentException(String.format(PLAYER_EXISTS, p.getUsername())); });
Это ужас. Ну и как правильно уже написали выше, использование класса ConcurrentHashMap не делает ваш код автоматически безопасным, есть проблемы с многозадачностью.
Билдеры смешанные с конструкторами, статические методы просто вызывающие конструктор, геттеры которые не делают ничего кроме разрушения инкапсуляции. Ну и конечно типичны случай константы которые меняются чуть чаще чем никогда
public static final String PLAYER_UNKNOWN = "UNKNOWN";
Мне кажется, научитесь сначала программировать на Java понятно и без ошибок, не лезьте в spring. Написать программу на spring сложнее чем на Java, потому что надо хорошо знать как работает spring в дополнение к тому как работает Java. Учите Java, не spring
import {gameConnect, gameStatus} from "../ducks/actions";, и подобное
2. Css действительно хромает, формально он работает, но внося изменения легко что-то сломать,
position: absolute;и
right: ...не есть хорошо, выдаёт что опыта со стилями/вёрсткой нет.
3. Местами выглядит сложно, есть впечатление что код надёрган откуда-то, но и лапшой вроде не назвать.
Если UI работает шустро и хорошо (лень запускать), то можно простить некоторую переусложнённость из-за вебсокетов и асинхроньшины.
UI before API
Думаю что если человек подходит под все хотелки то может оказаться что расти на этом месте будет уже некуда, это тоже не есть хорошо.
Вероятно требования/пожелания к соискателю невнятно сформулированы или они просто придираются.
Процесс обучения в нашем ремесле — процесс постоянный. А будучи фулстеком, надо успевать и тут и там — и это уже двойная работа. Но, работая в команде, я всегда могу расчитывать на поддержку коллег-профессионалов. Жонглировать 5 яблоками явно сложнее чем 3. Возможно, сделав ставку на 3 яблока, к тому же огненных — ваше шоу будет более эффектным и вы выиграете больше :). Очередной повод задуматься, может быть лучше сфокусироваться на чём-то одном? Всегда должно быть место для сомнений и переосмыслений.
Статья родилась именно из фрустрации. И я рад, что она оказалась многим интересной, и многие эту фрустрацию разделяют. В конечном итоге, здесь есть работа над ошибками для обеих сторон.
В одном из комментариев (Undiabler) подметили, что в условиях недостаточного времени получается вот так брезгливо отвечать кандидатам и нет времени ничего объяснять. Мне кажется, что это очень плохо и не даёт возможности кандидатам делать правильные выводы, оставляя неприятное впечатление о компании. Мне кажется, что компаниям с достаточным бюджетом, к тому же претендующим на звание Developer Friendly, просто необходимо должным образом выстраивать процесс собеседования. Даже когда её имя уже работает на неё. Этот культурный аспект очень важен и не принесёт большого финансового ущерба самой компании, но при этом сделает жизнь соискателя проще и увеличит степень уважения к имени компании.
Во-первых, как уже писали, явно проблема с коммуникациями.
То есть прежде чем тратить n часов на решение, было бы логично узнать, что им надо. Если они сами не знают/не хотят говорить -можно описать свою архитектуру, озвучить, что будете делать, и спросить — норм? или слишком сложно? слишком просто? слишком энтерпрайзно? слишком тяп-ляп-и-в-прод? Если не удастся выяснить эти вопросы — может, ну ее, эту вакансию?
Во-вторых, именно поэтому я не люблю тестовые задания. И не выполняю их (исключение — если задание мне понравилось и я его делаю "для себя", не ставя трудоустройство как основную цель)
Выдача объемных заданий на дом — это неуважение к времени кандидата. И, если компания — не условный "Гугл", может не стоит тратить на нее столько усилий? За это время вы успеете очно пообщаться с 2-3 другими компаниями.
Вот насчёт "что им надо" мне понравился ответ одной из компаний (недословно, но близко) "Пишите как хотите. Мы хотим проверить не только как вы знаете язык, но и как любите его использовать. Мы не хотим, чтобы наш будущий коллега страдал из-за того, что мы заставляем его писать так как мы считаем нужным."
P.S. Речь шла о PHP, который позволяет как выстраивать "ентерпрайзных монстров", мало отличающихся от джавных, так и писать километровые скрипты в "баш-стиле" с рельсоподобной магией посередине.
Работодатели с подобным подходом будут только отфильтровывать стоящих кандидатов, если это не совсем начальный уровень.
Cложный код или история одного интервью