Ранее в нашем блоге мы рассказывали о квизе для C++ разработчиков. С момента запуска мы тщательно собирали обратную связь. Часть из неё касалась ошибок в работе квиза, которые мы естественно решили исправить.
Для тех, кто пока не знает о каком квизе идёт речь, расскажем кратко: мы отобрали несколько ошибок, найденных нашим анализатором в Open Source проектах. Участнику предоставляются 10 случайных фрагментов кода, в которых нужно найти и отметить ошибку. На каждый фрагмент выделено 60 секунд.
Вы можете почитать о квизе подробнее в заметке или пройти его.
Вернёмся к теме. Мы получили много комментариев от тех, кто прошёл наш квиз. И часть из них была о проблемах при прохождении. Являясь апологетами качества кода, мы стремимся к тому, чтобы улучшать и наши собственные проекты.
Мы обработали комментарии с нескольких площадок, где разместили информацию о квизе, включая Habr, соцсети, тематические форумы и сайты. По итогу выделили 2 момента, требующие исправления. С первым столкнулись многие, прошедшие квиз – мы собрали немало комментариев по поводу способа выделения и подсчёта ошибок. Вторая же ошибка не была так распространена, о ней был лишь один комментарий. Ирония в том, что мы сознательно её допустили. Но обо всё по порядку.
Ошибка 1. А как попасть в правильный ответ?
Изначально механика квиза предоставляла возможность множественного выбора ответа (т. е. нескольких элементов). В примере ниже ошибкой является clip->GetSequence.
Ранее вы могли выбрать как правильный ответ, так и дополнительно (специально или случайно) отметить знаки = или (), и даже всю строку. По сути, вы нашли ошибку, но ответ засчитывался как неверный из-за лишних символов.
Теперь можно выделить не более одного элемента. Но это не значит, что выбранный элемент может быть единственным правильным. В качестве верного ответа часто нами размечено несколько мест в коде, поэтому выбор любого из них будет корректным.
Наглядно увидеть, как и что изменилось вы можете на видео:
Было
Стало
Недавно мы подводили итоги двухмесячной работы квиза и рассказали о том, как почти 2 000 разработчиков в сумме заработали 3,5 балла из 10. Не исключаем, что способ подсчёта отчасти мог повлиять на итоговый результат.
Ошибка 2. Наш мини-фейл
Писать о своих багах всегда неловко, но делать это нужно. Вернёмся немного назад, в период разработки квиза. Изначально при прохождении был баг: как только задание появлялось на экране, ему ставился статус "время закончилось". Почему это происходило:
- при загрузке задания таймер обнулялся и запускался заново;
- далее таймер запускался от 60 до 0 и каждый тик проходил проверку: если таймер === 0, то ставим флаг "время закончилось", но т. к. в начале таймер обнулялся, то проверка на ноль проходила сразу.
Если объяснить просто, то:
открывается задание -> таймер обнуляется -> сразу проходит проверка на === 0 и выдаётся "время истекло".
А должно было работать так:
открывается задание -> таймер обнуляется -> таймер запускает отсчёт -> через 60 секунд проходит проверка на === 0 и выдаётся "время истекло".
Чтобы не тратить время мы решили поставить таймер от 60 до 1. После этого таймер спокойно обнулялся, а флаг "время закончилось" ставился при проверке "если таймер === 1".
Решение не изящное, но быстрое. Мы были уверены, что никто не станет косплеить героев боевиков 90-х и нажимать "Отвечаю" на последней секунде. Но, если разработчику кажется, что всё будет работать именно так, как он задумал, – найдётся тот, кто "споткнётся о костыль")
2 месяца мы со спокойной душой не думали об этой ошибке. Пока в одном из комментариев нам не прилетел скриншот:
Получается, что, пообещав 60 секунд на ответ, мы на самом деле давали участникам 59 секунд. Что ж, ошибку признали, поправили и теперь каждый участник может со спокойной душой нажимать кнопку ответа на последней секунде. Но лучше, конечно, не затягивать, если ответ вы нашли быстрее.
Этот опыт в очередной раз доказал, как важна обратная связь и работа с ней.
Кстати, все эти моменты мы учли при разработке квиза по C#, который уже доступен к прохождению. Проходите его сами, скидывайте друзьям, которые пишут на C#, и, конечно же, делитесь своими результатами. А если вдруг попадётся баг, то обязательно пишите нам – будем чинить.
И не забывайте, что каждому прошедшему квиз доступен промокод на 30-дневную лицензию PVS-Studio.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Alexey Sarkisov. The feedback for our C++ quiz and why it matters.
Комментарии (8)
datacompboy
31.08.2022 17:32в тесте про LANG_USER_DEFAULT наверное надо принимать оба упоминания за ошибку? :)
al_sarkisov Автор
01.09.2022 14:01Мы рассматривали данный вариант, но всё же пришли к выводу, что ошибка состоит именно в использовании поля. Его объявление, очевидно, ошибкой не является.
datacompboy
01.09.2022 15:06+2Я подумал что ошибка -- определение её ниже использования. Ну ничего, я сам уверен что я прав =))
a-tk
01.09.2022 10:27Ещё один спойлер из теста по шарпам с вопросом о разметке ответов.
al_sarkisov Автор
01.09.2022 11:44Действительно, исключение будет выброшено именно при итерировании. Однако мы исходили из того, что в коде ошибка состоит не в том, что разработчик использовал foreach. Ошибка в том, что выражение может быть равно null, поэтому правильным ответом считается выбор именно этого выражения.
В то же время понятна ваша позиция :). Увы, тяжеловато сделать квиз так, чтобы понимание "ошибок" совпало вообще у всех людей. К примеру, кто-то вообще может посчитать, что беда именно в объявлении namedLifecycleParticipantCollection. В любом случае, главное, что вы сами знаете, что ответили правильно)a-tk
01.09.2022 12:35По-хорошему ревью не должно пройти использование оператора костыля (?.) с потенциально нулевой коллекцией. А коллекция на null должна быть проверена задолго до начала итерирования.
xi-tauw
Спойлер из теста по шарпам
pageNumber > 0 всегда истинно, соответственно, я хотел указать на dead code.
al_sarkisov Автор
Действительно, в некотором роде это логично). Но мы исходили из того, что нужно указывать именно на допущенную ошибку. Мёртвый код в данном случае является скорее следствием ошибки, а ошибка предполагается в условии.