Comments 35
Это просто саботаж
В ситуациях, когда приходится выжимать максимум производительности, такие "хаки" могут стать единственным выходом, поскольку не всегда компилятор сможет схлопнуть хорошо пачку if.
К примеру, код выше на https://perfbench.com/:
С условными переходами:
ID Calls Min Avg Max StDev
map total 100 0.015 0.018 0.022 0.001
С магией (только я определение массива вынес в `static const`):
ID Calls Min Avg Max StDev
map total 100 0.015 0.017 0.019 0.000
Казалось бы, немного, Но, тем не менее, разница есть.
В ролике на ютубе приводил пример. Сравните
if 215 == n
n = 137;
else
n = 215;
n = (137 + 215) - n; // вылезаем за 1 байт
n ^= 137 ^ 215; // остаемся в рамках одного байта
PS и это не считая лишнего напряжения предиктора переходов по меткам, в панике мечущегося - какой код подгружать
Но ваш код совсем другой. Оригинальный, с if, для любого n заменит значение переменной на 215. Единственное исключение -- если n == 215, тогда результатом будет 137. А "исправленный" код этим свойством не обладает, в чем нетрудно убедиться (мы говорим про байты, поэтому перебрать 256 значений можно даже руками).
И это как раз тот случай, когда тесты помогают -- при рефакторинге кода они сразу скажут, что что-то стало не так.
Хочется вам пожелать побольше отлаживать чужого кода, написанного в таком стиле. А как прикол такое вполне имеет право на существование.
И так не только в программировании.))) IRL многократно наблюдал подобное. И в таких случаях действительно думаешь, что лучше бы поменьше контроля, чем контроль, приводящий к вот такому (а он приводит, практически неизбежно приводит. Не знаю, как в программировании)))
Конечно же тесты нужны, например, они очень помогают при рефакторинге или баг фиксе какой-нибудь дичи, чтобы не наделать больше багов.
Но писать тесты надо на то, что надо, а на то что не надо - писать их не надо)
Короче вместо того, чтобы бороться с бюрократом, который навязал вам каверадж, без которого не проходит CI, вы призываете бороться с самим кавераджем и заодно с вашими коллегами, которые будут читать и поддерживать код.
Хаха, тут надо код переработать под новые требования. У нас сотрудник хорошо писал, полное покрытие тестов, никогда не было нареканий, в общем, разберешься....
Если нормальный техлид увидет это, можно и нарваться за саботаж.
И потом : как все эти выкрутасы в коде объяснить чужим людям при код. ревью ?
Введение любых метрик приводит к работе на выполнение метрик, вместо работы.
Ну .. табличные процессоры всегда были самым шустрым решением, да и чем меньше нагружаешь код ветвлениями, тем проще работать конвееру проца и его кешатору. Имеет место быть, почему нет?
А если зайти с другой стороны, перед запуском теста кавереджа сгенерировать пару терабайт бессмысленных нопов, и подсунуть их как часть кодовой базы. Покрыть их один тестом, вызывающим его, и все, у нас 99% покрытия без того чтобы бороться с ифами.
Как и многие, я заметил ухудшение читаемости кода. Получается, что Вы экономите на написании тестов, но повышаете время на поддержку кода. Это уже звучит пугающе.
С другой стороны, если можно выдать элегантное решение, которое уменьшит количество тестов и не потеряет читаемости, то надо так и сделать. Поэтому можно потратить больше времени на написание кода, сэкономив на написании тестов и не потеряв читаемости.
Как видите, в любом случае мы чем-то жертвуем
Ну это известный способ уменьшить ветвление кода с помощью карты (reduce branching with decision map). Улучшает метрики (complexity/maintenability) и производительность, снижает читаемость. Я так в C# писал, но без загонов со словарём из делегатов. Из дополнительных плюшек - такой код хорошо читают системы аудита кода. Как и SCA так и SAST /DAST, так что прям вот все R#, SonarCube, Chekmarx и т.д.
Веселая компания, саботаж и полное непонимание что такое тесты.
Сталкивался с саботажем, когда "коллеги" обмазывали код пустым try-catch, что починить ошибку. Что-то из этой серии.
Но система кавераджа заставит вас прогнать эту функцию для всех трех веток условий.
То ли лыжи не едут, то ли я не особо умный. Но ведь все эти 3 ситуации в любом случае надо протестировать, чтобы проверить соответствие реализации заданию.
нужна функция, которая при аргументе 1 будет возвращать 10, при аргументе 2 возвращать 20, а при всех остальных аргументах возвращать 30.
Далее,
для юнит-тестов это еще можно тупо накопипастить, создав комбинаторно растущую лапшу кода тестовых кейсов
Модульный тест на то и модульный, чтобы быть максимально простым, а если в нём получается "комбинаторнорастущая лапша", то это просто означает, что пишется такой код, который не только сложно тестировать, но и читать/поддерживать.
для интеграционных тестов нам придется долго и нудно ...
При достаточном количестве качестве модульных тестов, интеграционных может быть совсем чуть-чуть, только для того, чтобы проверить happy-case.
ЗЫ я не питаю иллюзий по поводу Хабра, поэтому жду комментов что я ничего не понимаю, что тесты и каверадж нужны, и т.п. Но и на адекватные комменты тоже хотелось бы надеяться :)
Непонятно, зачем вы под конец статьи так явно выражаете пренебрежение в сторону пользователей ресурса, которые будут читать эту статью.
Вопрос зачем призван выявить целеполагание, а я не настолько манипулятор, чтобы вставлять подобные фразы ради достижения какой-то цели или эффекта. Скорее, тут более уместен вопрос почему, выявляющий причины. Я просто честный и открытый человек, и считаю недостойным скрывать мое отношение к определенной части данного ресурса. Я здесь достаточно давно, чтобы видеть динамику "нетортовости" и иметь по этому поводу определенное мнение.
ЗЫ один из нескольких минусов в карму за эту статью я получил с пометкой "Статья/тема не для Хабра". Так вот когда (если) Хабр снова станет торт, подобные темы снова станут онтопом, ресурс снова станет профессиональным а не школьным, тогда я с радостью поменяю свое мнение и отношение.
ресурс снова станет профессиональным а не школьным
Вы себя тоже тут профессионалом не показываете.
Профессионально - это собрать своих коллег и ЛПРов и предметно доказать им, что ваша точка зрения ("тесты не нужны", "покрытие ну нужно") верна. Далее в вашей организации дружно отменили бы все "лишние" проверки, тесты и код-ревью заодно. Потом вы бы собрали метрики, которые показывают, что без тестов сложное ПО пишется быстрее и содержит меньше ошибок. Потом с этой фактурой вы пришли бы на Хабр и доказали бы уже всем, что ваша точка зрения верна.
А ничего этого не происходит, и вы просто выражаете маргинальную точку зрения без пруфов. А "школьный" при этом хабр, да.
Вы себя тоже тут профессионалом не показываете.
Возможно, да. А возможно, что это вы не видите :) Или вы действительно считаете, что я запушил в мастер код по вышеприведенным принципам?
Если бы я написал статью в стиле, который вы описали выше, вероятно это бы придало больший вес моему виртуальному образу в глазах вас и ваших единомышленников. И да, проблема не техническая а административная, и решать ее надо соответственно... И самое смешное, что я могу делать так, как вы описали. Но это скучно :) Успешно решить задачу обхода кавераджа технически - гораздо интереснее!
В плане же взаимоотношений с социумом, меня больше привлекает реакция типа "о, это тот самый чувак, который нагнул систему кавераджа, предложив рабочий метод ее обхода!" и "господа, давайте не будем усердствовать с лимитом процента покрытия для прохождения деплоя, а то на Хабре есть статья как это дело прохачивать". Я понимаю, что есть большой соблазн навесить на меня ярлык маргинала, и многие ему поддаются :) Но я не готов лишать себя маленьких радостей решения задач и совершения открытий ради поддержания образа "серьезного человека".
И самое смешное, что я могу делать так, как вы описали. Но это скучно :)
Успешно решить задачу обхода кавераджа технически - гораздо интереснее!"господа, давайте не будем усердствовать с лимитом процента покрытия для прохождения деплоя, а то на Хабре есть статья как это дело прохачивать"
Пруфы, Билли, нам нужны пруфы. Без пруфов вы никакой не "тот чувак", а именно что маргинал, который хотел пошатать систему, а пошатал полторы строчки кода и родил из этого статью.
Хорошая статья. Сохраню ссылку и буду использовать для примера как, уж точно, делать не надо.
На таком лаконичном примере псевдокода наглядно демонстрируется усложнение читабельности кода, да еще и в угоду достижения ложной цели.
Именно поэтому разберу подробнее.
Итак, имеем псевдокод А:
if (1 == n) // вроде так советуют писать с ==, чтобы не присвоить ненароком :)
r = 10;
else if (2 == n)
r = 20;
else
r = 30;
и псевдокод B:
bool t = n >= 1 && n <= 2;
int a[] = {30, 10, 20};
int r = a[n * t];
С точки зрения code review псевдокод A выглядит сносно, а вот для псевдокода B потребуется, как минимум, переименование переменной t, чтобы объяснить её дальнейшее участие в алгоритме.
Читабельность кода пострадала. В более сложных случаях, хотя даже и в этом, было бы разумно потребовать написать комментарий, объясняющий неочевидность происходящего при беглом просмотре кода, в отличии от псевдокода A. Более очевидным выбором было бы использование, например, словаря ключ-значение, вместо массива, но здесь не так, и поэтому следует указать, что эта конструкция - альтернатива if/switch.
Использование "хитрого" алгоритма привносит дополнительные трудности:
при возникновении еще одного условия, например, при n == 3 нужно возвращать 25, легко добавить правку в код, чтобы все отлично заработало, но также легко забыть добавить тест для этого значения. При этом инструмент проверки покрытия кода тестами для псевдокода B нам ничем не поможет, в то время как для псевдокода A он обязательно бы отметил этот момент, изменив процент покрытия, поскольку добавленная ветка else if (3 == n) не выполняется.
требования меняются, нужно добавить в существующую программу какой-то специфичный случай, и придется менять этот неочевидный алгоритм. Например, если для текущего псевдокода при n == 0 или n == 8 нужно возвращать какое-либо значение, то алгоритм псевдокода B
a[n * t]
перестанет работать и придется придумывать еще более изощренный и менее читабельный вариант.
Язык программирования не конкретизировался, поэтому я и назвал это пседкокодом, а значит у кого-то может возникнуть идея реализовать этот подход на других языках. Но в других языках придется вносить правки, поскольку компилироваться/работать этот код не будет. Это чревато последствиями, которые в случае псевдокода A просто бы не возникли:
для компиляции объявления массива, например в C#, интуитивно напрашивается ключевое слово new и вуаля - строка скомпилировалось! Только вот теперь объявление приведет к выделению объекта в куче, а это влечет за собой снижение производительности, а также добавление работы сборщику мусора, что дополнительно снизит производительность.
для того, чтобы как в оригинале на C++, код использовал только стек, нужно писать иначе, но не каждый начинающий разработчик догадается/умеет, а ведь именно такие разработчики могут последовать советам из этой статьи. Но! Даже если использовать инициализацию массива на стеке мы потеряем в производительности по сравнению с псевдокодом A.
чтобы производительность все же приросла, массив можно объявить статическим, как это сделал автор одного из комментариев выше, в котором он приводит замеры производительности. Но это значит, что мы выделили объект в куче на все время работы программы - мы повысили требование к памяти (размеру кучи), которое при массовом использовании этого приема может составить существенное значение, а в случае псевдокода A такое явление не возникает.
Так чего же добились, применяя псевдокод B?
Усложнили работу разработчикам, которые будут поддерживать и развивать программу?
Обманули инструмент, контролирующий покрытие кода тестами?
Себя? Тестировщиков? Работодателя? Качество продукта?
Если позволить себе холиварное и провокационное высказывание, то я выскажу тезис о том, что тесты не нужны.
Точнее, от них вреда больше, чем пользы.
Это троллинг, но, как я и указал в начале комментария, статья может принести много пользы, как пример-антипаттерн.
Как-то безапелляционно и бездоказательно :) Люди, к примеру, придумывают всякие Реакты, после чего огромная масса разработчиков вынужденно постигает его модель и паттерны и начинает писать кот в рамках данного фреймворка. Тут не сложнее.
Причем, необязательно применять технологию полностью. Например, заменить чистые свитч-кейсы на хешмапку повлечет исключительно только положительные следствия (со всех сторон, включая и читаемость с расширяемостью и поддержкой), исключая возможно только перформанс (и то не всегда). Но и полное следование подходу также не сильно больно, можете сравнить примеры кода в видеоролике.
Хотя я согласен, что если снять идиотские требования на каверадж, то извращаться таким образом не имеет практического смысла :) Но мы начали с конкрентых условий окружающей среды и выработали эффективный метод выживания в них :)
Как писать ненормальный код и зачем это может быть нужно