Комментарии 212
маскируют вредоносный код, прячут проблемы на code review, создают абстракции с экспоненциальным ростом сложности.
Искренне извиняюсь за иронию, но звучит забавно, что Вы начали замечать это, работая в Яндексе, который шлифует пятьюдесятью этапами собеседований на профпригодность.)
О, я даже помню, что написал своему бывшему коллеге из LinguaLeo, который тоже работал в Яндексе в прошлом. Диалог был на фейсбуке, который я удалил, скопировать не могу — расскажу, как помню.
— А у тебя на проекте в Я тоже был говнокод?
— Да, тот еще.
— А где-либо ты работал, чтобы код был нормальный?
— Нет.
Я уже 10 лет в разработке. Такого, чтобы прийти на проект, которые поддерживается 3+ лет и чтобы кодовая база была хорошей, — не было ни разу. Смотрел на код друзей, работающих в других компаниях. Самое-самое ужасное, что видел — размер функции main в java-приложении на несколько десятков тысяч строк кода.
Я думаю что никакого заговора нет, а это следствие естественной деградации кодовой базы в условиях недостатка времени (кто видел бизнес которому фича нужна не вчера, поднимите руки) и недостаточной аналитики (не обязательно прям работы СА, а просто малого времени на анализ в целом)
Да, просто стилистику написния статьи такую выбрал. Иногда дело в том, что требования меняются, а времени мало. Иногда в том, что текучка, и новые разработчики пишут код, не зная деталей. Но бывает и так, что плохой код пишется сразу — на это повлиять уже можно.
Совершенно верно. Или выгорание текущих сотрудников и нежелание бизнеса это решать. Это уже в сторону "нам нужен человек, способный управлять звездолетом в одиночку" Но это анриал, и лично я считаю, что как раз-таки повальная экономия на специалистах (давайте возьмем одного фуллстека вместо бэк + фронт) вкупе с нехваткой времени и выгоранием текущих и приводит к проблеме.
Насчет же сознательного саботажа - мне кажется, это уже крайняя форма, когда тебя настолько все заколебало, что ты злостно потирая ручки хочешь что-то испортить. Но мне такое не встречалось
(Вспоминается бородатый анекдот про то, что лучшие хакеры - злые админы)
Зато встречалось нежелание заботиться о сотрудниках. Про wellbeing у нас обычно говорят только топы рынка, хотя эмоционально-психическое состояние сотрудников безумно важно и грамотная помощь и поддержка может сильно сократить проблемы
Насчет же сознательного саботажа - мне кажется, это уже крайняя форма
Если что, я не имел в виду, когда писал статью, что на самом деле есть люди, специально разрушающие код. Это скорее троллинг над теми, кто пишет плохой код по тем или иным причинам сразу, например, от того, что опыта нет.
А под фразой “Но бывает и так, что плохой код пишется сразу — на это повлиять уже можно.“ имел в виду, что плохой код (написанный плохо по неопытности или в торопях) можно на codereview найти и устранить. Можно увеличить количество ревьюеров. Можно вести доклады команды, чтобы все росли, проговаривая ошибки, или процесс, подразумевающий защиту архитектуры до написания кода. Или pair programming.
Можно вести доклады команды, чтобы все росли, проговаривая ошибки, или процесс, подразумевающий защиту архитектуры до написания кода. Или pair programming.
pair programming - это очень крутая штука, которая хорошо работает. На текущей моей работе мы пользуемся этим и это позволяет хорошо повысить качество кода
Есть даже развитие этой техники - mob programming, это то же парное, но в роли Навигаторов может быть несколько человек, которые обсуждают проблемы до написания кода.
Про доклады - интересная идея, которая мне очень близка, это внутренние митапы. Но это довольно тяжело обосновать бывает
Однако, есть еще и другая точка зрения Сводится все к тому, что т.к. теперь двое работают над одной задачей - то это, якобы, неэффективно расходует ресурсы (ведь можно им выделить каждому по задаче)
Контраргумент: продуманная архитектура сейчас сэкономит время в будущем, за счет того, что новые фичи на нее будут ложиться легче и интуитивнее
Если что, я не имел в виду, когда писал статью, что на самом деле есть люди, специально разрушающие код. Это скорее троллинг над теми, кто пишет плохой код по тем или иным причинам сразу, например, от того, что опыта нет.
У моих комментариев также не было цели заставить оправдываться. Скорее углубить и развить тему. Это дискуссия а не спор.
P.S. Я вообще не разделяю никаких теорий заговора
Это важная ремарка.
Как-то раз попадал в ситуацию, когда человек тоже начал рассказывать о всемогущей организации, которая устраивает саботаж. Я сначала решил подыграть ему, но через несколько минут стал подозревать, что он не прикалывается, а абсолютно серьёзно в это верит. К счастью, когда я уже совершенно точно понял, что это была не шутка, то мы уже почти приехали, поэтому неловкое молчание продлилось недолго, и больше мне этот таксист никогда не попадался.
Иронично, что эти самые разрушители корпораций из статьи - это обычно эффективный менеджмент самих корпораций, который не даёт разработчикам времени на рефакторинг и анализ.
У меня были примеры в статье, которые не получится оправдать спешкой. Например, логика через try-catch (не связанная с ошибками), или неидиоматичное использование ключевых слов языка (apply), или !isDisabled()==false
, или добавление мета-программирования, которое ничего не дает, кроме магии.
Но что-то действительно может возникнуть из-за спешки.
Я уже 10 лет в разработке.
(Презрительно сплёвывая:) Черпак!
Linux, U-Boot, скучаю по ним, кодовая база очень качественная. Сейчас ковыряю FreeBSD, даже близко не дотягивает. Например, не ожидаешь увидеть в ядре достаточно известной ОС куски кода в функциях, в которые по условиям в принципе попасть невозможно.
Да вот да, где применяются все эти "лучшие практики"? Я не видел нигде кода, который работает в продакшене, чтобы он был без говна. Всё равно, какой бы язык ни был, реальность и физика всегда просачиваются и выглядят не всегда лицеприятно. Можно только максимально собрать все такие куски где-нибудь в одном месте и оградить забором из комментариев с восклицательными знаками.
на фейсбуке, который я удалил
А... Так вот кто удалил Фейсбук.... А я-то думаю, чего он больше не открывается....
А зачем искать злой умысел там, где все легко объяснить глупостью?
Бизнес давит на разработчиков "давай, давай, быстрее" и вот разработчик находит самый примитивный путь решения и фигачит.
Спросите, а где же тут глупость? Так там, откуда подгоняют.
Да, согласен.
Другая причина — когда приходишь на новый проект, а тебе дают большую фичу, ты не знаешь деталей проекта, можешь случайно продублировать функциональность или наступаить на какие-то грабли, расставленные предыдущим разработчиком.
Но бывает, когда пишешь плохо сразу — от недостатка опыта, например. В статье есть пример, как implies
вместе с отрицанием отрицания дает большую сложность. И так пишут — сразу, без всякой спешки, на текущем проекте видел.
Иногда переходишь на другой стек и пишешь неидиоматично, просто потому что привык к другому.
То что заговора нет — это понятно, мне интересно было в таком стиле написать статью.
Приходит пм: "хотим такую фичу." Лид: "спроектировать, написать, протестировать - за месяц справимся". Пм: "мы её уже продали, давайте без тестов, за баги как-нибудь отбрехаемся."
Есть вариант еще хуже, когда пм начинает мнить себя программистом, при этом в деталях проекта, который сам же и ведет, не понимает от слова совсем... И метод "х...к, х...к и в продакшен" начинает применяться не потому что дедлайн был вчера, но об этом никому не сказали, а потому что пм художник, он так видит и уже всем наобещал, что будет сделано именно так, и это было одобрено самым высоким руководством, с которым уже не поспоришь...
после чего ПМ слышит какой же он дебил,
причем сначала от команды разработки, а через месяц от заказчика (можно, хоть тут не буду писать его с большой буквы?)
хотя, встречал много случаев когда функционал на самом деле не нужен и только для галочки - тогда ПМ просто гений
Так все тесты при собеседованиях - это удаление гланд через жопу. Каких набирают и обучают, такие и есть.
Т.е. вначале от людей требуют пользоваться совершенно адскими регэкспами какими-нибудь (там где можно и без них) через раз фигачат рекурсию (которая чревата), а потом удивляются чего это в продакшене разобраться что делается можно только прорвавшись через все эти "прикольные штуки".
И уверяю вас, если вы будете писать понятно, интервьюеры вас не поймут. Будут ругать за то, что вы проитерировали через аж десять значений вместо построения индексов и деревьев. А вдруг значений станет миллион (а в задаче значения - это ребенок-подросток-взрослый).
Никто и нигде(почти) не оптимизирует код по метрике "насколько это просто поддерживать", так чего удивляться, что модификации приводят к бедам?
Далеко ходить не надо. Как-то проходил собеседование в яндекс, и попалась задачка простая на аккумулятор с хитрой логикой. Интервьюер мягко намекнул на использование reduce. Против reduce ничего не имею, но за 10 лет встречал примерно 1.5 человека, которые без бутылки за 2 секунды могли разобраться в том, что оно делает, особенно, когда нет нормального названия у переменной, куда кладётся результат.
Я решил не идти на поводу у собеседующего и написать обычный код. Коллега начал жестко противиться, мол, зачем, если можно написать вот тут однострочник, которые делает 678 операций с массивом. Я ответил, что предпочитаю в продуктовой разработке простой и понятный код, нежели усложнять жизнь коллегам ради красоты и лаконичности. Он смирился, и мы пошли решать дальше.
На следующий день пришел отказ с поинтом, мол "кандидат не идёт на контакт", а дальше ссылка на reduce в MDN. Ну, может оно и к лучшему)
Надо сказать честно, большинство разработчиков не умеет писать нормальный код, даже если им давали время. А те кто умеют временщики - среднее время на одном месте по статистике 1,5 года, поэтому не очень напрягаются и пишут, как прийдется
большинство разработчиков не умеет писать нормальный код, даже если им давали время.
По отдельности в тепличных условиях много кто умеет. Вместе - нет.
Давайте 100 таких умеющих посадим на один проект (неважно, монолит или микросервисы). И будем менять 30 из них каждый год. А требования станем менять еще чаще. Что будет? К гадалке не ходи, через 3 года код станет непроходимым легаси. А через 10 лет им можно будет пугать джунов, да так, что они разочаруются в профессии и побегут в курьеры.
К сожалению, чистая правда. Из своего опыта:
На проекте было 3 разработчика, мы согласовывали решения между собой, поэтому не было никаких проблем. Потом решили взять ещё двоих. До того, как я пришел на проект, был ещё один разработчик, гадящий в гит, собственно которого я и заменил, и мне было сказано - перед тем, как что-то делать, надо посмотреть, как уже было сделано, и если что, посоветоваться. Затем мне показали тот говнокод, который уже существует (примерно в 3-х местах), и не делать также. Инструкцию я понял, и мы спокойно работали дальше.
Так вот, взяли мы двух новых разработчиков и сказали им ту же идею. Что происходит в течении следующего месяца:
Первый разработчик, не спрашивая никого, берёт и изобретает собственный велосипед, причём который ещё и не работает. На вопрос, почему он не следовал инструкции, которая была дана, он ответил, что "решил проявить себя".
Второй разработчик поступил более элегантно. Однажды меня позвал тимлид посмотреть его код. После просмотра я понял, что этот код уже где-то видел, и меня осенило - он подозрительно похож на те места, которые мне строго запрещалось использовать. Тимлид зовёт его на звонок и спрашивает, "Ваня, почему ты взял здесь это решеие?". А далее, как в анекдоте (ответ убил):
- "Я сделал поиск по проекту и нашел 85 мест, где делается вот так, и 3 места, где делается иначе. Я решил, что эти 3 места - то, что мне нужно. "
Тимлид: "Слушай, а почему ты не догадался спросить у меня или у других ребят, какое конкретно место тебе выбрать..."
- "Я так чувствую".
Выводы, как всегда, делаем сами, но вот такая репрезентация доказывает проблему. Боюсь представить, что происходит, когда на проекте 20 разработчиков.
Приготовлюсь собирать летящие тапки, но.
Вот сколько не говорят про говнокод - всегда вспоминают про бизнес, которому "надо быстрее". А бизнесу не то чтобы надо прям дохрена быстрее. Он часто может подождать и час, и 2, и даже пару дней. Да каких там дней, проекты стандартно срываются на месяца - и все живы.
Но не так часто вспоминают что в нашем джентельменском клубе, в общем-то, хватает во-первых - не очень квалифицированных специалистов, назовем это так. У которых если программа скомпилировалась - то она работает. А если она интерпретируемая, то это вообще подарок - "хрен его знает когда там баги всплывут, может я уже уволюсь".
А во-вторых - все люди сильно разные, и бекграунды сильно разные. И код, как ни крути, получается разношертсный. Ну, люди существа такие. Разнородные. Для одного понятнее по-одному, для другого по-другому. Кто-то (я) вообще плюсовик, мне красивый код писать религия не позволяет.
И пример с AspectJ в статье он как-раз об этом - у человека был план, и он его придерживался. Если бы он вел проект от начала и до конца, и этот паттерн был бы в проекте стандартным - вопросов бы ни у кого не было. Проблемы от того, что человек что-то сделал на его взгляд красивое, и ушел.
В общем не стоит все скидывать на бизнес, мы (разработчики) тоже так себе людишки.
Плюс опять-же, человеку даже если пальцем тыкать в говнокод, который по его понятиям норм, он не измениться никогда.
Я (фронтендер) в начале пути просто обожал миксины (подвид множественного наследования). Пихал их по поводу и без везде, было удобно и понятно, код переиспользуется, все классно. А потом попал на проект другого такого дурачка и проклял эту фичу
А зачем искать злой умысел там, где все легко объяснить глупостью?
Про глупость и злой умысел верно, но есть чуть более емкая формулировка: как известно, миром правит не тайная ложа, а явная лажа (с) ;-)
Но тем не менее, рискну предположить, что догма про плохой код все-таки не универсальна. Несмотря на мое безусловное доверие к опыту @arturdumchev:
Я уже 10 лет в разработке. Такого, чтобы прийти на проект, которые поддерживается 3+ лет и чтобы кодовая база была хорошей, — не было ни разу. Смотрел на код друзей, работающих в других компаниях. Самое-самое ужасное, что видел — размер функции main в java-приложении на несколько десятков тысяч строк кода.
я бы все-таки задал ему такой вопрос: а что в таком случае будет с проектом, который поддерживается 30+ лет? Код будет хуже в 10-й степени?
Что-то у меня есть сомнения, что это всегда так. Несмотря на мои крайне слабые знания разных ОС, а тем более кода их ядер, из чтения Хабра складывается впечатление, что с тем же Линуксом, несмотря на многодесятилетнюю преемственность ядра, не все так ужасно (злые языки говорят, что бывают и кодовые базы похуже ;-). Ну или рискну привести
пример из личного опыта (крайне скромного, но все-таки).
Проекту 35 лет. Функция main (cpp, это главный корень проекта) около 200 строк. Фортрановским MAIN (с жуткими процедурными циклами обработки сообщений) 400-600 строк. Причем половина из этих строк - это комментарии, которые визуально фрагментируют "простынку" на логически монолитные секции с более-менее понятной структурой (обычно не больше экрана кода, иногда много меньше). Библиотеки оформлены в этом же стиле. Другие функции, кроме MAIN, обычно
еще кратно короче
Исключение - это функции с сугубо линейной логикой, где блоки кода выполняются строго последовательно. А единственное исключение - это "аварийный" выход из какого-то блока. Там тоже бывают сотни строк кода, но в этом случае он обязательно разбит на короткие разделы - примерно как текст в большой книге с пронумерованными заголовками 1-2 уровней.
Из супермонстров в проекте присутствует только пара заголовочных файлов: один на две тысячи строк, другой на три. Но, там есть один трюк, который позволяет ориентироваться в этом море текста, даже не зная имен тех объектов, которые надо найти. Если вдруг у кого-то есть аналогичная проблема с монструозными файлами - советую заглянуть под второй спойлер ниже ;-). Да, решение не универсальное, но вдруг кому-то и пригодится? ;-)
Типичный фрагмент главного цикла обработки сообщений
Кусочек главного цикла обработки сообщений в одной из фортран-программ с наиболее заумной внутренней логикой
(...)
c
c===========================================================================c
c Выполнить выбранную операцию:
c===========================================================================c
c
80 SELECT CASE(ABD_Mode)
c
c.........101. Создать новый ряд или поправить паспорт имеющегося:
CASE(-$F2)
call abdnew('new'); call restore_help()
if (dbh.nf >= 1) cycle
ABD_Mode=-$Esc; goto 80
c
CASE(-$CtrlF2, -$R); call abdnew('old')
if (ied == 1) then; ABD_Mode=-$F4; goto 80; end if
c
c.........102. Разные виды импорта данных:
CASE(-$F3); call abdimport()
c
CASE(-$AltF3); call edit_ximport_table()
c
CASE(-$CtrlF3); call abdximport()
c
c Если установлен автоматический режим, то сразу после XIMPORT - выход:
if (Plot_Mode == $PM_ABDxImport) exit
c
CASE(-$CtrlF4)
dos_line=' Объединить две базы данных'
ansyer_default='y'; call abdmain_ask('AbdDBUnion')
if (ansyer == 'Y') call abdDBUnion()
goto 60
c
c.........103. Редактирование данных:
CASE(-$F4)
CB_Mode=$CB_Mode_Edit; ied=abdedit(); CB_Mode=$CB_Mode_String
if (ied == 1) then; ABD_Mode=-$CtrlF2; goto 80; end if
ied=0
c
c.........104. Составить отчет о наличии данных:
CASE(-$F5,-$AltF5)
i=1; if(ABD_Mode == -$F5) i=0 ! Краткий/Подробный
call set_help_topic('PrintReport'); call abdreport(i)
А вот фрагменты одного из двух имеющихся в проекте монструозных заголовочных файлов
Тех самых, где 2 и 3 тысячи строк одной простыней. А трюк в том, что каждая строка в шапке-оглавлении монстра точно соответствует аналогичной строке в теле файла. Если забыл название структуры, то ищешь нужную строку в "оглавлении", затем Ctrl+C, Ctrl+F - и ты в соответствующем подразделе (фрагменте) монстра, где на 1-2 экранах собраны все объявления по выбранной теме.
Это фрагмент из начала файла:
c WinABD_inc.for, версия 4.10.42
c План файла:
c
c 0. Глобальные параметры процесса c
c
c=* 1. БАЗОВЫЕ ТИПЫ И ГЛОБАЛЬНЫЕ ДАННЫЕ *=c
c 1.1. Строки для разных нужд (имена файлов, топики справки и др.) c
c 1.2. Мнемонические константы и базовые структуры для разных нужд c
c 1.3. Текстовый слой. Структуры для описания цвета, окна, символа и др.
c 1.4. Текстовый экран: взаимодействие с сервером c
c
c
c=* 2. ТИПЫ И ГЛОБАЛЬНЫЕ ДАННЫЕ ДЛЯ ГРАФИКИ *=c
c
c 2.0. МАКСИМАЛЬНЫЙ РАЗЕР ПАКЕТА РЯДОВ $MAXSERIES c
c 2.1. Графика: работа с таблицей команд c
c 2.1.3. Константы для настройки шрифта:
c 2.1.4. Константы для настройки типа и толщины линии:
c 2.2. Предопределенные номера сегментов и текстов для разных нужд c
c 2.3. Глобальные параметры виртуального графического экрана c
c 2.5. Работа с RBOX c
c
c=* 3. РАБОТА С ЦВЕТОМ *=c
c 3.1. Стандартные цвета = именованные константы c
c 3.1.1. Стандартные цвета графиков (частично повторяют текст-цвета)c
c 3.1.2. Стандартные цвета = именованные константы c
c 3.2. Работа с прозрачностью в разных функциях (замена XOR-моды) c
c 3.3. Составной цвет для текстового экрана: символ+фон c
c 3.4. Стандартные цвета для графики c
c 3.5. Градиентные цвета для графики c
c 3.6. Управление печатью и экспортом рисунков, таблица подмены цветов
c
c
c=* 4. БАЗОВЫЕ ТИПЫ И МНЕМОКОНСТАНТЫ ДЛЯ РАБОТЫ С КЛАВИАТУРОЙ. *=c
c 4.1. SCAN-коды клавиатуры: c
c 4.2. Специальные "внутренние" скан-коды ABD для обработки особых случаев
c 4.3. Псевдо SCAN-коды для обработки событий мыши
c 4.4. Флаги вставка/замена, наличия символа, макроса. Последний SCAN-код.
c
c
c=* 5. МЫШКА, ВСПЛЫВАЮЩИЕ ПОДСКАЗКИ, КОНТЕКСТНОЕ МЕНЮ, "МЕНЮ F1-F12"
c 5.1. Константы и типы для обработки событий мышки: c
c Регионы графического/текстового экрана, режим адресации
c 5.2. Работа с всплывающими подсказками: c
c
c
c=* 6. РАБОТА С КОНТЕКСТНЫМ МЕНЮ *=c
c 6.1. Максимальная длина элемента КМ, размера КМ и число регионов КМ
c 6.2. Идентификаторы КМ для использования в различных режимах.
c 6.3. Элемент контекстного меню:
c 6.4. Контекстное меню: массив элементов
c 6.5. Активная таблица КМ = PPMENU_Regions($PPMENU_MAX_REGIONS)
c 6.6. Альтернативы для создания КМ:
c
c
c=* 7. ТЕКСТОВЫЕ ЭКРАНЫ, РАЗНОЕ *=c
c 7.1. Стек текстовых окон, УТМ, ВП и КМ (текстовый режим): c
c 7.2. Структуры данных для "меню функциональных клавиш" c
c 7.3. Структуры данных для "меню кнопок": c
c
А это - фрагмент из "где-то далеко, очень далеко..." в его середине:
c
c
c=======================================================================c
c=*********************************************************************=c
c=*===================================================================*=c
c=* *=c
c=* 5. МЫШКА, ВСПЛЫВАЮЩИЕ ПОДСКАЗКИ, КОНТЕКСТНОЕ МЕНЮ, "МЕНЮ F1-F12"
c=* *=c
c=*===================================================================*=c
c=*********************************************************************=c
c=======================================================================c
c c
c
c=======================================================================c
c 5.1. Константы и типы для обработки событий мышки: c
c=======================================================================c
c
c.....5.1.1. События мыши:
integer*4, parameter ::
+ $MOUSE_LCLICK=1, $MOUSE_RCLICK=2, $MOUSE_CLICK=3,
+ $MOUSE_LDBLCLICK=4,$MOUSE_RDBLCLICK=8, $MOUSE_DBLCLICK=12,
+ $MOUSE_HOVER=1024, $MOUSE_MOVE=2048, $MOUSE_ALL=2048*2-1
logical*4 :: Mouse_Jump=$True ! Разрешается ли "перескок" мышки в окно ввода
c
c.....5.1.2. Вид курсора мыши (устанавливается SetMouseCursor).
Я конечно не знаю: может по современным меркам это как раз и есть то самое
жуткое легаси
формально оно так и есть: написано лет 30 назад, серьезно рефакторилось последний раз лет 20 назад
в котором я могу разобраться лишь по причине участия в этом проекте с его начала. Поэтому было бы интересно узнать впечатления при "взгляде со стороны". Но отдельные случаи "вхождения в проект" с нуля (причем оба разработчика фортран раньше не знали, а писали на С++) у нас были, и проблем вроде бы не возникло
А вопрос к автору статьи в том, что к его тезису про неизбежность деградации кодовой базы любого развивающегося проекта наверно нужны какие-то уточнения? Например, кроме требования о возрасте (3+ лет), надо добавить требование о текучести кадров? Или еще что-нибудь?
Или все-таки в общем случае достаточно одного условия 3+ лет? А те аномальные ситуации, когда более древняя кодовая база не деградирует с приемлемой скоростью, - это досадные исключения, лишь подтверждающие общее правило?
;-)
У меня проект который я начинал с нуля и командую им уже 20 лет. Люди приходили-уходили, а я был на нем всегда и в силу этого там очень много именно моего кода. И этот код - полная деградация. Хотя сам по себе я в каждый момент времени - молодец. Вот только навыки, опыт и знания растут, подходы меняются, а уже написанный код остается. И начинает выглядеть как коричневая субстанция. И я более чем уверен что мой сегодняшний код через 5 лет будет выглядеть так же. И никак не спихнешь это на нерадивых джунов или торопящихся менеджеров, хотя и такого много.
Просто есть одно правило - если вам нравится код, написанный вами несколько лет назад - значит все эти годы вы не развивались.
Просто есть одно правило - если вам нравится код, написанный вами несколько лет назад - значит все эти годы вы не развивались.
Круто Вы сейчас меня опустили! ;-) Люто плюсую за такой троллинг ;-)) А если чуть-чуть серьезнее, то собственный старый код наверно не нравится никому. Тем более, что развивается не только сам автор кода, но и инструменты еще иногда. Да даже внешний контекст - он меняется, и требует адекватного отражения в коде.
Вопрос скорее не в том, нравится тебе старый код или нет, а в том, насколько он понятен спустя много лет, и
насколько легко поддается рефакторингу
Меня самого на начальном этапе учил мой старший коллега, инициатор проекта и основной мой соавтор на первом этапе его развития. Я был зелен и относился с известным пиететом и к нему самому, и к его коду, естественно. Так как до этого меня в ВУЗе учили кодить на перфокартах, экономя не только операторы, но и символы ;-)
А спустя много лет я вдруг обнаружил, что рефакторить мой код, даже совершенно забытые фрагменты, мне гораздо легче, чем моему соавтору сделать то же самое со своим кодом аналогичного возраста. Так и хочется вспомнить известную фразу
"Делай, как я говорю! Не делай так, как я делаю"
которая у меня почему-то ассоциируется с Козьмой Прутковым, но прямого подтверждения его авторства я сейчас не нашел
Можно ли сказать, что разработчик перестал расти, если у него нет проблем с чтением и с поддержкой собственного кода 15-летней давности?
Тем более, что развивается не только сам автор кода, но и инструменты еще иногда.
Ну так вот так может можно начать считать себя хорошим программистом, когда уже начинаешь писать инструменты для своей работы? И вроде как и без них можно сделать, но ведь с ними быстрее
Скрытый текст
(я как-то написал перевод несложных экселевских таблиц в C#, потом было прикольно, когда сам заказчик писал формулы, добивался, чтобы они корректно работали, а я уже почти без дополнительных усилий вставлял его код в свой проект. Ещё и ЗП за это получал).
Ну так вот так может можно начать считать себя хорошим программистом, когда уже начинаешь писать инструменты для своей работы?
Я дико извиняюсь
(я сам в очень специфической сфере работаю)
программирование научных задач
Только не смейтесь, пожалуйста, если глупость спрошу. Но разве в природе есть программисты с многолетним опытом, которые
НЕ пишут собственных инструментов?
Особые случаи "летунов" не в счет (хотя и у них наверняка есть собственные pet-проекты). Но если ты много лет кодишь какие-то похожие вещи, то ведь это наверно превратится в нудятину? Которую неизбежно захочется автоматизировать каким-либо способом, чтобы в освободившееся время заняться чем-то более творческим?
Или я слишком отстал от жизни в своем диком заповеднике, и работа в больших командах с жесткими правилами далеко не всегда оставляет такую возможность?
Если под инструментами иметь в виду что-то серьезное, вроде среды разработки, то такое в крупных компаниях если и пишут, то отдельной командой.
Если какие-то скрипты в помощь, то наверное все пишут.
Если брать какой-то популярный стек, вроде андроид-разработки, то абсолютно все используют андроид-студию, потому что это лучший инструмент для задачи.
На Clojure часто пишут макросами “умные“ решения, я пал жертвой таковых, когда приходил работать в австралийский стартап на годик. Лучше бы ничего не писали и использовали готовые отлаженные библиотеки, потому что все, кроме автора этих решений, страдали и плакали.
Но разве в природе есть программисты с многолетним опытом, которые НЕ пишут собственных инструментов?
К сожалению, да. Мне кажется, тут иногда срабатывает триггер, что инструменты пишутся крупными компаниями, а "я ведь один, не компания, куда мне?" Я размышлял над этим противоречием и родилась такая мысль - крупные компании пишут то, что можно продать, поэтому всякие мелочи проходят мимо. Хотя мне кажется, что поскольку я не могу дать определения "инструмент", а границы между макросами, утилитами, плагинами очень условны, то холиварить можно очень долго. )
программирование научных задач
Извините за любопытство, а можете пару слов сказать о задачах? А то вокруг в основном только web-интерфейсы да базы данных (совсем не наука, скукота, хотя и тут ещё можно сделать открытия, даже для себя).
Извините за любопытство, а можете пару слов сказать о задачах? А то вокруг в основном только web-интерфейсы да базы данных (совсем не наука, скукота, хотя и тут ещё можно сделать открытия, даже для себя).
Спасибо за интерес! Научная задача - это анализ временных рядов геофизического и геодинамического мониторинга. Где-то черт знает где (например, в подземной лаборатории на глубине в километр) стоят всякие датчики. От них сигнал идет на регистратор, который пишет полученное в файлы каких-то форматов. Моя непосредственная задача - загрузить эти данные в СУБД, провести
их первичную обработку (чистка брака и пр),
Чуть подробнее только что в комментах к соседней статье написал.
затем изучить поведение измеряемой переменной, и найти там всякие "эмпирические закономерности". Потом на основе таких "эмпирических обобщений" можно строить или проверять разные физические гипотезы о процессах в земной коре. Начиная от сугубо фундаментальных задач, и кончая всякой прозой типа поиска предвестников землетрясений. В общем, задачи очень разнообразные.
Ну а программистская составляющая сводится к тому, чтобы обеспечить инструменты для всей этой работы. Результатом чего стала сверхузконишевая программа для анализа геофизических временных рядов. Этот проект развивается уже почти 40 лет. Сейчас наш пакет позиционируется, как "три в одном": СУБД временных рядов, интерактивная среда визуализации ВР и комплекс алгоритмов их обработки, оптимизированный
для работы в режиме конвейера
Как ни смешно, несмотря на сугубо виндовую среду разработки и использования пакета, в основе его архитектуры лежит именно эта идея ;-) Ну и еще идея обработки скользящем окне, которая на заре нашего становления была еще непривычна, а сейчас стала практически общим местом. Возможно, мозги разработчиков перекосило на самом первом этапе развития проги, когда мы жили под RSX11M ;-))
Причем во всех трех компонентах есть куча уникальных фич, которые для нас являются "предметом первой необходимости", но которые отсутствуют в более универсальных пакетах. Например, своя собственная
идеология работы с пропусками данных,
набор трюков при визуализации рядов данных большого размера
как пример приведу динамическое переключение двух методов
Почему-то авторы большинства программ считают, что 640Кб одного способа генерализации хватит всем - а я при первичной обработке данных команду переключения режима генерализации использую чаще, чем любую другую.
своя собственная система синхронизации временных рядов с разными шкалами времени,
Она работает вообще без timestamp-векторов, что в нашем случае дает двойную-тройную экономию памяти и аналогичный выигрыш в скорости. Правда, наш метод не годится для рядов с нерегулярными наблюдениями, и
теряет эффективность при очень большом количестве пропусков /перерывов в сигнале.
Например, если измерения выполняются в случайные дни раз в две-три недели, то мы приписываем такому сигналу периодичность наблюдений "раз в сутки", а все значения между датами измерений объявляются пропусками. А еще он непригоден (будет смещение времени), если шаг между измерениями (=частота дискретизации) не может быть выражен целым количеством микросекунд. Но для подавляющего большинства сигналов все Ок
нестандартный подход к организации рабочего пространства,
оно хранится не в памяти, а на диске, что дает кучу плюсов, но и такую же кучу ограничений
ну и всякие прочие изыски в виде немеряного количества нестандартных алгоритмов обработки сигналов, которые зачастую не только пишутся, но и придумываются самостоятельно.
В общем, как Вы уже видимо поняли, на недостаток творческого элемента в работе лично мне жаловаться не приходится ;-).
что с тем же Линуксом, несмотря на многодесятилетнюю преемственность ядра, не все так ужасно
В популярном open source всё в среднем получше. Не берусь перечислить все причины, это объясняющие, но вот несколько:
Когда у тебя open source проект, ты можешь просить сделать так, как видишь ты. У автора ПР не будет выбора. Менеджеры, сроки, завязки на маркетинг тут не помогут влить ПР.
Когда профессионалу не платят (при благотворительности), он выкладывается на максимум. Это моя вольная интерпретация идеи из книги Дэна Ариели “Предсказуемая иррациональность“.
Когда что-то пишешь в open source, не мешают мысли вроде: “надо быстрее“ или “мне все равно тут работать еще полгода — и уволюсь“.
Пишешь в open source — увидит весь мир.
Средний уровень разработчика, поддеривающего популярный open source проект, выше среднего уровня разработчика средней компании. Не могу доказать, но ощущается так.
А вопрос к автору статьи в том, что к его тезису про неизбежность деградации кодовой базы любого развивающегося проекта наверно нужны какие-то уточнения?
Попробую и тут перечислить часть причин, почему кодовая база протухает за несколько лет:
Текучка. Новые разработчики и проект не знают, и хотят затащить новые библиотеки и подходы (иногда), создавая зоопарк.
Постоянно горящие сроки — ошибки проектирования в спешке.
Меняющиеся требования, когда фича уже реализована и требует рефакторинга на скорую руку.
Количество джунов на проекте.
Отсутствие процесса: нет code review на начальном этапе (или вообще всегда), нет встреч по защите архитектуры и т.п.
Даже язык программирования, на котором пишут, влияет. Пример — Android-приложение на Scala. Бизнесу надо будет нанимать новых сотрудников, у которых опыта под андроид на нестандартном языке гарантированно не будет. Кроме того, всплывет море проблем связанных с нестандартным ЯП. В случае со Scala время комплияцие значительно возрастет.
Или все-таки в общем случае достаточно одного условия 3+ лет?
Взял цифру 3, пробежавшись мысленно по собственному опыту, вспоминая плохие кодовые базы. Во всех возраст был 3+ лет. Уверен, можно написать отвратительно за первый месяц — сам так делал на первой работе :)
Взял цифру 3, пробежавшись мысленно по собственному опыту, вспоминая плохие кодовые базы. Во всех возраст был 3+ лет.
Мне кстати вспомнился один проект, который на момент моего прихода был прямо свежий, написан 3-4 месяца назад и буквально передо мной зарелизен. Тем не менее, код выглядел как дичайшее легаси из 2000-х :)
Это был сарказм. Мне понра :)
А горькая правда в том, что это лишь на 20% объясняется глупостью и спешкой. Многое из здесь приведённого - это тот способ кодирования, который делается степенно, со вкусом и уважением к собственной квадратноголовости. И вот есть большая когорта людей, которые оттачивают этот навык.
Поддерживаю. Более того, если мозги и опыт на месте, то волей неволей начинаешь писать адекватный поддерживаемый код даже в условиях дикой спешки, просто потому что привык делать нормально. И за этим следует еще одно открытие: даже в дикой спешке можно писать чистый код и при этом успевать по срокам
Статей про то, как можно оптимизировать Postgres, очень много, например, вышедшая в совсем недавно «Оптимизация SQL запросов».
Не хочу пересказывать примеры из подобных статей, все что нужно для саботажа — делать обратное рекомендуемому в статьях.
Только не в этом случае. Я бы сказал что автор как раз из "этих", из вашей тайной организации.
Про школы только если на проекте нет ни мидла, ни сеньора чтобы в мердж реквесте замечание сделать. Имхо , как уже говорили выше, часто говнокод это трейдофф со временем. Или поленился кто-то, например , тесты написать на свою поделку.
Про Яндекс вообще странно такое слышать.
Еще бывает так, что джун/милд уже написал огромную фичу, там много косяков, страшная архитектура, но фича нужна позарез, потому что уже и маркетинг завязан, и менеджеры требуют скорее — и приходится это вливать.
А почему про Яндекс странно? Везде накапливается легаси, везде бывают сложные времена с текучкой, когда приходит новая команда и пишет фичи, не понимая старый код, затаскивая кучу новых технологий, дублируя функциональность и т.п.
С другой стороны кто дал джуну в одиночку писать огромную фичу?
Я с таким и в Яндексе, и в Сбердевайсах, и в Австралийском стартапе AudienceRepublic сталкивался. И сам, будучи джуном, писал приложения с нуля в аутсорс компании. Не завидую тем, кто был вынужден это поддеривать.
Фича нужна, рук не хватает, фича при этом не очень важная и срочная, вот и можно ее спихнуть на джуна, авось справится.
Плюс джуны тоже разные бывают, иногда можно банально ошибиться в оценке человека: думаешь он уже к такому готов, а он оказывается не готов и городит лютый говнокод. А время уже потрачено, фича уже сделана, переделывать с нуля конечно же никто уже не будет. Ловушка невозвратных затрат.
Я думаю, тут очень много причин, почему код усложняют или пишут “грязно“.
Иногда это следствите того, что люди не своим делом занимаются — т.е. не любят программировать и не хотят разбираться, как писать простой код. Кто-то может усложнять из-за комплексов, желания исползовать ”умные” решения.
Иногда просто надо очень быстро написать, а потом меняются требования, и надо быстро зарефакторить — и вылезают косяки.
Члены этой тайного сообщества уже проникли в ркн или еще нет?
Судя по всему, да, недавно попадались новости, что они случайно Youtube разблокировали


AspectJ, на самом деле, можно сделать более-менее удобоваримым - по крайней мере, в одном из доставшихся мне проектов (в целом весьма, кхм, отсаботированном) это, на мой субъективный взгляд, удалось. Конкретно, там аспекты использовались по принципу "если на методе есть такая аннотация - завернуть его в такую обёртку". В итоге получалось, что, с одной стороны, ничего неявного в этом коде нет (аннотация на виду, что она означает - задокументировано), с другой - какие-то общие вещи, вроде логирования, таким образом делать оказалось вполне удобно. Хотя, конечно, мне потребовалось время, чтобы во всё это въехать, но на общем фоне это смотрелось скорее положительно.
Господа, я как раз из этой организации. Специально не писал ничего на хабре со времен царя гороха, но раз уж нашу сетку вскрыли, решил, наконец, высказаться.
И где-то в другом файле пишется:
val (_, id) = getUser()
Если ваш язык позволяет это скомпилировать, то ищите истинного злодея не в яндексе.
Спойлер
Напоследок, если хотите избавить свою жизнь от взаимодействия с противоречивыми параграфами в программировании, просто используйте языки с жесткой типизацией и, вообще, такие языки, у разработчиков которых было нормально с головой.
Kotlin как раз strongly staticaly typed.
Код превратится в:
val (_, id: Int) = getUser()
Тут проблема в том, что и age, и id — одного типа Int
, а destructuring возвращает компоненты по порядку из указания в декларации data class
.
"val id: Ing" - опечатка
В нормальном языке порядок неважен. Destructuring с объектами работает по именам. Так в JavaScript или Rust. Проблема в языке.
В нормальном языке...
Так в JavaScript...
JavaScript и “нормальный язык“ в одном абзаце звучит подозрительно :)
1 === '1'; // false
1 == '1'; // true
true === 1; // false
true == 1; // true
[0] == 0 // true
{} + [] == 0 // true
Ну пример странный, это особенность языка, которую, наверное, только ленивый не знает :) Так что плохим это его не делает
Ну пример странный
А этот вам больше нравится?
"5" - 3 // 2
"5" + 3 // "53"
это особенность языка
Неявная типизация имеет свои плюсы и минусы, но в случае с JS, который разрабатывался второпях из-за гонки между Netscape и Microsoft, не было времени продумать хоть что-нибудь. Так что особенность тут только в том, что язык сделан очень плохо, и единственное его преимуществе — в том, что он первым занял нишу.
То, что основа гнилая (или точнее, не рассчитана на что-то вне своей изначальной узкой ниши) не значит, что более поздние фичи не могут быть реализованы "нормально".
strongly staticaly typed и тысяча способов обойти это недоразумение, класс! :)
Тяжесть закона компенсируется неведением, что творишь...
да сфига ли он strongly typed, если у него age и id - один тип данных?
age - это duration и измеряется в секундах или любых других единицах времени, а совсем не безразмерный int
народ жалуется, выкручивается как может, но насколько понимаю в язык - и в ту самую идиоматичность - этого так и не завезли. https://discuss.kotlinlang.org/t/units-of-measure/3454
strongly typed — это описание типизации языка; язык не позволяет смешивать разные типы и не выполняет автоматические неявные преобразования. Например, нельзя умножить строку на число. Kotlin — strongly typed. Проблем с destructuring решится, если как раз в id и age разные типы засунуть, как я в статье в качестве решения предлагал.
что и age, и id — одного типа
Int
А все потому, что вместо dateOfBirth: Date
сделали зачем-то age: Int
.
Это просто пример для демонстрации идеи. Могло быть count и id, или countA и countB.
За такое тоже линейкой по рукам бьют. dateOfBirth: LocalDate
ещё может прокатить в зависимости от домена, но иногда лучше String
(или он же обернутый в new type), т.к. даты рождения типа 00.01.1944
вполне существуют.
даты рождения типа
00.01.1944
вполне существуют.
Это типа когда человек родился в день между 31 декабря и 1 января? Или как?
Это когда «знаем, что январь был, но числа не знаем».
Почему нельзя использовать первое число? 00 нарушает RFC/ISO:
date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on ; month/year
А если уж нужно хранить признак отсутствия компонентов даты рождения, то можно просто сделать флажок unknownDateOfBirth: int
, с двоичной маской типа YMD
. Если ЯП поддерживает расширение встроенных типов без костылей - то расширить им Date, если нет - то сделать обертку. В базе можно вынести DateOfBirth
в отдельную сущность.
Потому что первое число — это «точно знаем, что первое».
А вообще нужно было сделать три поля: «день», «месяц» и «год», и каждое из них может быть nullable. Но вполне очевидно, что систему выпустил в прод со стандартным полем для даты — а когда попёрли данные из жизни, схватились за голову.
Ибо жизнь — она такая...
«Всё не так плохо, как вам кажется. Всё гораздо, ГОРАЗДО хуже!»
Вполне очевидно, что систему сделали строго в соотв. с RFC/ISO на формат даты. Если "из реальной жизни" поперли некорректные данные, то надо либо пинать источник данных чтобы поправили, либо (если это невозможно), костылить флажки (а вообще об этом нужно было подумать на этапе составления ТЗ). Нарушать RFC/ISO не нужно. Вы в своем коде подопрете костыликами эти "00", а в какой-то третьесторонней либе в проекте этого не будет, и начнется веселье.
может быть nullable
А я бы вот за это бил линейкой по рукам.
А я бы вот за это бил линейкой по рукам.
Абсолютно стандартная договорённость: все значения, которые неизвестны — NULL. Бить надо, когда их начинают пытаться сравнивать.
Я уже понял, что линейку Вам давать нельзя. А то Вы её всё норовите не по назначению использовать.
В данном конкретном случае значение известно, просто оно не валидно и потому не может быть принято в информационную систему.
Ошибку лучше фильтровать на входе в ИС, чем обкладывать всю ИС насквозь костылями на случай, если она вдруг такая вся из себя с подворотами и обмороженными лодыжками.
В данном конкретном случае значение известно
В том, о котором говорю я — неизвестно («Великий русский писатель Васечкин родился в январе 1825 года, хз какого числа»)
просто оно не валидно и потому не может быть принято в информационную систему.
А жизнь — она такая: то, что дата рождения точно не известна, ещё не повод не вводить человека в систему. (А вот за ввод в систему выдуманных данных «от балды» точно надо линейкой.)
то, что дата рождения точно не известна
ещё не повод вбивать в систему вообще хрен пойми что. Нулевой день в месяце не определен, в любом. Изначально проблема не техническая, а административная, вот и пусть решается административным порядком. Например, «принять, что при отсутствии сведений о точной дате рождения, неизвестный день месяца принимается за первое число, а неизвестный месяц — за январь; провести проверку данных ЗАГС, МИД и иных органов на предмет таковой неполноты, при необходимости известить граждан, заменить соответствующие документы без взыскания пошлины, принести извинения; впредь поставить такие данные на контроль и продолжать исправление недостатков данных при выявлении новых случаев указанным выше порядком».
Если у вас в процессах бардак, то автоматизация вам даст лишь автоматизированный бардак
Собственно, насмотрелся такого при миграции ПК ИС «Юстиция» и ЕГРП в нынешний ЕГРН. При проектировании ЕГРН просто уперлись рогом: данные либо валидные, либо никакие. Ну и вот, либо кадастровые координаты соответствуют границам участка, либо дом, расположенный на участке поперек, вылезая за его границы, в базу не поедет. Всё, баста, карапузики, кончилися костыли. Так вот и победили срань в государственном реестре недвижимости, прав на нее и ограничений, установленных по закону.
все значения, которые неизвестны — NULL
Maybe/Optional для кого умные люди придумали, а?
Система, которая такое позволяет (программная, конечно же), обычно является vendor-lock.
С какой стати это vendor lock-in? Это просто часть требований к системе в относительно узком домене, где нужна реальная дата из бумажного документа. И если в ДУЛ написано 29.02.1961, то хрен что с этим сделаешь без довольно непростого административного решения (уровня государства), т.к. эта дата могла протечь в тонну других документов.
Большинство же систем будет использовать вполне простой и человеческий LocalDate
Destructuring это всё же одна из слабых сторон Kotlin. Плюс то, что он не поддерживает destructuring на классах, полученных из Java (у Jetbrains было это под экспериментальной фичей, не знаю доделали ли уже).
Она полезна только для `value class` разве что.
Так это одна из фишек языка, только нужно с умом использовать ) Автор даже подсказал, что value class спасёт
По-моему, в F# была подходящая фича которую не спешат тянуть в другие языки - единицы измерения в качестве модификатора типа. Фича позволяла без особой когнитивной нагрузки избежать ошибок когда метры могут быть в качестве килограммов использованы.
Работал в Сбедивайсах с беглыми из Яндекса. Подтверждаю
Шизофреническое усложнение архитектуры проекта, в ревью - постоянные переименования переменных. Документации не было вообще никакой. При этом у всех этих светил была полная профнепригодность в практических задачах. Когда задавал вопросы - звонили и закатывали истерики. Страдали от них в основном исполнители, которых набирали со стороны и которые не задерживались. Было это до февраля 2022 года.
Здравствуйте, коллега, на каком проекте вы были? Я с 2020 под 2022 написал и поддерживал медиа-приложения на девайсах.
А кто-нибудь может рассказать, как разрабатывают транспортную карту "СберТройка"?
(И тестируют ли её не только на живых людях?)
В Новосибирске (не знаю, как в других городах, где её тоже вводят) её (и терминалы/валидаторы, через которые можно платить банковскими картами) ввели вместо уже работавшей и отлаженной ЦФТшной ЕТК --- и сразу несколько критичных (но только для пользователей) багов:
после прикладывания к валидатору может пройти до 10 секунд (а к "коробочке у кондуктора" -- до целой остановки), а у 5% социальных карт не работает совсем (но это не проблема: пассажиры оплатили банковскими картами или наличными, некоторые могут получит возврат денег, надо всего лишь отправить имейл и "В письме нужно указать номер социальной карты и прикрепить платежный документ об оплате проезда банковской картой из приложения или личного кабинета пассажира.");
когда был сбой у систем связи, за многие поездки оплату взяли несколько раз (у кого-то --- семь раз за две поездки) (но это тоже не проблема) -- то есть всё работает только в идеальных условиях.
Как обычно, предлагается потерпеть: «Сейчас период сложный, и со временем всё стабилизируется».
И "Сейчас ведется работа над тем, чтобы добиться скорости срабатывания до пяти секунд (пока она больше)."
Настоящие программисты не будут писать функцию для импликации, они используют операцию <=
Но вообще-то в конкретном примере из статьи проще написать shouldGoHome = (isRaining && !carryingUmbrella)
, а в общем претензия к импликации неясна.
В целом, вкорячивать условные операторы для вычисления булевского значения по таблице истинности – неважная практика. Так как во-первых, можно забыть одну из веток условий, а во-вторых, это может неэффективно транслироваться.
А макросы – это вообще святое.
shouldGoHome = (isRaining && !carryingUmbrella)
В черновой версии статьи был такой код. Я решил изменить на if
, чтобы читалось чуть проще, но это субъективно, конечно. Были бы примеры кода на Lua
, так бы и оставил.
Тут тезис про то, что если то же самое через импликацию писать, читается сложнее. А претензии к импликации те же, что и у разработчиков большинства языков, которые не включают implies
как ключевое слово в кор языка. Тема обсуждалась много раз.
А макросы – это вообще святое.
Ловите, один уже скомпрометировал себя!
Так именно потому, что импликация – это просто-напросто <= на булевском типе, для неё и нет отдельного ключевого слова.
Но мы же не можем в java, kotlin, go, clojure, dart, lua, fennel применить `<= ` к двум boolean. И ключевого слова implies
нет.
Ну clojure вообще не из этой оперы, там есть условная функция, которая позволяет всё это сформулировать изящнее, да и можно написать макрос. Хотя в racket есть implies из коробки.
А вот почему в языках типа java нельзя применять операции сравнения к примитивному булевскому типу, хотя это самое натуральное перечисление из двух значений, лично мне непонятно, и это выходит за рамки вопроса об импликации. А как вы в java будете сортировать по булевскому полю? Инкапсулируя его в объект и применяя compareTo? Хорошо ли это?
А как вы в java будете сортировать по булевскому полю? Инкапсулируя его в объект и применяя compareTo? Хорошо ли это?
В зависимости от задачи.
Можно boolean-ы представить единицами и нулями и использовать Arrays.sort. Можно самоу алгоритм реализовать за один проход, если создаем другой массив.
Если надо сортировать объекты по boolean полю, можно объектам реализовать Comparable или передать реализацию Comparator<Obj> в место, где это требутся (например, TreeMap).
сортировать по булевскому полю
а зачем? селективность такого индекса будет очень плохой, лучше его избегать и изменить подход к задаче. Ну а если не получается - на некоторых, редких задачах - то придется страдать. То, что язык отталкивает от самой идеи "сортировать по boolean" - это как раз хорошо.
Промышленные языки вообще ценны не свободами (раздвигать горизонты - это удел академических языков), а оковами (заставить всю команду писать одинаково, в том числе дебила Васю и студента Петю). То, что Java мягко отталкивает Петю от плохой идеи, вынуждая его морщиться и лапками писать нудные вещи - это как раз хорошо. Может быть он вспомнит о благодетельности лени и начнет меньше писать, но больше думать.
Точно. Потом будет Вова править программу и радоваться узнаванию: "а, разобрался, этот код нужен, потому что это оковы, на которых Петя укрощал свою гордыню!"
применить `<= ` к двум boolean
А что больше, true или false?
А как определён булевский тип?
Но вообще-то всегда true > false.
А как определён булевский тип?
Если #define false 0 то так и есть. А так, если определена операция > , то должна быть определена и операция -. Чему равно false - true?
Если #define false 0 то так и есть.
Это не определение типа.
А так, если определена операция > , то должна быть определена и операция -
С чего вдруг?
Операция >= (и это, кстати, не совсем то же самое, что >) говорит только о том, что множество значений упорядочено.
Если Вася умнее Пети, то чему равна разность их умов?
Если Вася умнее Пети, то чему равна разность их умов?
У одного из них больше баллов IQ, да?
множество значений упорядочено.
Множество {true, false} действительно упорядоченно? Или просто так получилось, что раз один элемент кодируется как 0, а другой как 1, то один автоматически больше другого?
У одного из них больше баллов IQ, да?
Нет, конечно. Я утверждаю, что Дональд Кнут умнее Аллы Пугачёвой, хотя их баллов IQ не знаю, и подозреваю, что они могли и не измерять их.
И в общем я не думаю, что баллы IQ являются мерой ума, хотя некоторая корреляция есть.
Множество {true, false} действительно упорядоченно?
Множество значений [false, true] очевидным образом упорядочено. Просто потому, что оно так записано.
Или просто так получилось, что раз один элемент кодируется как 0, а другой как 1, то один автоматически больше другого?
Нет, конечно. Истину можно и минус единицей закодировать в какой-то интерпретации. Но это не меняет семантики типа.
Множество значений [false, true] очевидным образом упорядочено. Просто потому, что оно так записано.
Тогда неупорядоченных множеств не существует, правильно?
утверждаю, что Дональд Кнут умнее Аллы Пугачёвой
Какие ваши доказательства? ;) В математике без этого никак.
Истину можно и минус единицей закодировать
А можно и листочком. А ложь - камешком. Что больше - листочек или камешек?
Тогда неупорядоченных множеств не существует, правильно?
Существуют, но не в компьютере.
Хотя в языках программирования из соображений эффективности обычно не реализовывают отношения упорядоченности на агрегатных типах данных (кроме строк символов, если их считать таковыми). Не потому что в принципе нельзя, а потому что сложно в переносимой реализации и крайне мало востребовано (кроме строк символов).
Кстати, вот, на строках определено сравнение, но не определено вычитание. И вы задолбаетесь придумывать вычитание строк в UTF-8. Хотя Кнут может и смог бы :)
утверждаю, что Дональд Кнут умнее Аллы Пугачёвой
Какие ваши доказательства? ;) В математике без этого никак.
Это просто моё утверждение, которое может быть истинным или ложным.
Но для его доказательства не обязательно численно измерять их ум.

А можно и листочком. А ложь - камешком. Что больше - листочек или камешек?
Смотря как определён тип. [листочек, камешек] или [камешек, листочек].
По общепринятой конвенции true > false точно в том же смысле, как 'R' > 'Q'. В отношении листочков и камешков применимая конвенция мне неизвестна, поэтому тут есть свобода реализации. В конечном итоге вам всё равно придётся как-то упорядочивать листочки и камешки, если займётесь сортировкой предметов.
Кстати, вот, на строках определено сравнение
Очень плохо определено. Unicode передает привет, в зависимости от локали символы сравниваются немного по разному.
По общепринятой конвенции true > false
Потому что #define false 0, так повелось, что false кодируют логическим нулем.. #define false -1 - и всё сломается. Во всех таблицах истинности закодировано 0/1 а не true/false
вам всё равно придётся как-то упорядочивать листочки и камешки, если займётесь сортировкой предметов.
Ну похоже создатели языка, в котором нельзя просто сортировать по Boolean, считают что это множество неупорядоченное. Непривычно, да. Но выглядит логично.
Очень плохо определено. Unicode передает привет, в зависимости от локали символы сравниваются немного по разному.
Конечно, они сравниваются по-разному в разных локалях. Никто ничего иного и не обещал. Так устроена жизнь, а не язык программирования.
#define false -1 - и всё сломается
Смотря знаковым или беззнаковым сравнением пользоваться.
Но вообще-то это не имеет отношения к обсуждаемому вопросу, потому что само по себе определение констант true и false (как в старом Си) не означает наличия булевского типа. И вы тут подменяете понятия, сравнивая целые числа по имени true и false, а не логические значения.
В языке Форт, кстати, ложь и кодируется минус единицей (точнее, целым числом, состоящим из единичных битов – это равно -1 в случае использования дополнительного кода). Но это никак не противоречит моим словам, потому что в Форте есть только один тип – целые числа, и булевских значений просто не существует, а оператор IF по сути является арифметическим, а не логическим (как, кстати, и в Си).
Bash передаёт привет.
На самом деле, по поводу макросов — все зависит от области применения.
Мне как разработчику, который приходит на новый проект, не хочется разбираться, что понапридумывали другие и какие велосипеды изобрели. Практичеаски все задачи можно решить самым базовым синтаксисом, а оттого, что кто-то на макросах напишет свой ЯП внутри другого ЯП (пример — redplanetlabs так сделали), никому пользы не будет. Но понимаю, что писать такое — большое удовольствие.
Если речь о написании фреймворка, макросы имеют право на жизнь и бывают очень красивы. Пример — nest
в ClojureDart, писал об этом в статье Зачем Clojure Flutter.
Еще в Common Lisp'овых гайдах пытались предупреждать о том, что макросами не стоит злоупотреблять - там где можно функцией обойтись НУЖНО ею обходиться. И вообще, - макрос только если всем участникам очевидно что получилось удачно.
При этом, к сожалению, какого-то более простого критерия уместности макроса никто так и не придумал :(
Ну надо сказать, что в старом лиспе просто система макросов была довольно запутана. Современные гигиеничные макросы (такие как define-syntax в scheme) по своему определению и применению мало отличаются от функций, и подобное требование может диктоваться только эффективностью и удобством применения (например, макрос нельзя передать в функцию высшего порядка). Любой вменяемый человек и так при прочих равных условиях напишет функцию вместо макроса, так как функция универсальнее по своему применению. Но по сути дела, гигиеничный макрос в лиспе - это просто альтернативный способ передачи параметров, то есть реализация специальной формы. Я что-то не видел, например, критиков передачи параметров по ссылке в C++.
Кроме того, надо отметить, что в последние десятилетия все расширения Лиспа реализуются через макросы, что гораздо веселее, чем переписывать транслятор.
А что касается
разбираться, что понапридумывали другие и какие велосипеды изобрели.
- то это относится к любому коду, а не только к макросам. В иерархии классов разве не надо разбираться? В чём отличие?
Я таки подозреваю, что ограничения которые предлагались были связаны именно со сложностью восприятия их кода чем с локальной проблемой типа гигиены. Мало того что там код создает другой код, дык еще и все эти квотинги\анквотинги жизнь проще не делают. Как мне кажется, именно поэтому в scheme попытались как-то исправить эту ситуацию - define-syntax легче воспринимать.
А что касается
“разбираться, что понапридумывали другие и какие велосипеды изобрели.“
- то это относится к любому коду, а не только к макросам. В иерархии классов разве не надо разбираться? В чём отличие?
Это не сопоставимые вещи. Иерархия классов написана на языке, синтаксис которого я знаю. Я могу сразу начать разбираться с бизнес-логикой приложения.
С макросами два шага — сперва нужно разобраться, какой синтаксис придумал себе предыдущий разработчик. Затем уже разбираться с бизнес-логикой.
Тут еще, наверное, можно предположить, что в отличие от синтаксиса мейнстримового языка, который вылизывается командой из десятков или сотен человек — макросы на проекте писал один разработчик, у которого не было ни времени, ни ресурсов сделать это хорошо. И какой-либо ценности в том, чтобы разбираться в его поделках, нет.
Предыдущий абзац — мой опыт работы в Clojure-стартапе, где макросами описывалась схема базы данных и REST api одновременно. Походы в базу тоже были через макросы. Я точно уверен, что если бы у тех разработчиков забрали возможность писать макросы — всем было бы лучше, включая их самих.
По моему опыту, вопросы синтаксиса играют пренебрежимо малую роль в понимании сложной программы.
Разве? Вы же не будете писать сразу на байткоде или на ассемблере?
Вопрос тут не в синтаксисе, а в семантике языка.
Вы же не будете писать сразу на байткоде или на ассемблере?
(Характерным жестом поправляя очки на носу:) Не, нуачо, могу и тряхнуть стариной!
То есть, если дождь не идет, или зонт при себе, никто никуда не идёт. Ждем, когда протухнут зонты и пойдет дождь. Весело там у вас.
Ячан опять протекает в наши интернеты.
Я не шарю в Котлине, но вот это что-то странное:
data class User(
val age: Int,
val id: Int,
val name: String,
val surname: String,
)
val (_, id) = getUser()
Серьезно?! Язык неявно преобразует структуру вида ключ-значение в упорядоченный кортеж? Но это же дичь, фундаментальный смысл ключей в том, чтобы не зависеть от их порядка.
Или что такое функция getUser
, может там внутри какое-то преобразование, которое не такое уж неявное?
Не, в getUser
возвращение класса User. Дока.
А во что распакуются name и surname?
Если написать аналогичный код в Python с NamedTuple, то будет просто too many values to unpack
и надо так, чтобы получить id через распаковку:
class User(NamedTuple):
age: int
id: int
name: str
surname: str
user = User(30, 1, 'John', 'Doe')
_, id, *_ = user
В доке по ссылке нормальный пример кстати, там нет "лишних" полей, а у вас есть.
Ошибка в том, что age и id разные типа данных, age измеряется в годах, а id безразмерный.
Но Java и Kotlin не умеют выражать физические типы. Если бы умели, то сразу бы словили ошибку типов, что duration нельзя неявно конвертировать в int и наоборот.
Правда, реальные типы так просто не помогут, если поменять местами name и surname... Тут уже только под-классы str создавать, но можно ли запретить Котлину неявную конвертацию между двумя под-классами str?..
Я понимаю в чём ошибка с точки зрения логики. age и id оба целые числа и если поменять местами поля в структуре, то получишь age вместо id. Я не понимаю как работает распаковка кортежа в этом случае. Строковые поля, которые есть в структуре, просто игнорируются или что? Тип переменной же выводится автоматически, там var (_, id)
в левой части. Распаковываются только инты, а не вся структура? Тогда это ещё более всрато и не удовлетворяет правилу наименьшего удивления.
ну идея-то языка понятна...
Котлин вероятно ожидает, что все следующие разработчики будут только добавлять поля и только в конец DTO, и не будет удалять/менять поля - и тогда пожалуйста, вот ваше наименьшее удивление, ты добавил поле - а старый код работает без изменений и исправлений
правда эта фишка провоцирует использовать псевдо-кортежи для связывания модулей, а это уже.... Язык неявно преобразовывает DTO в кортежи, а это и впрямь отличная грабля. Все эти микро-фишки функциональные - кортежи и лямбды - отлично работают, пока умещаются на одном экране, но не в коем мере не для связки разных модулей между собой. Лично я бы вообще запрещал кортежам(tuples) быть видимыми вне своего модуля (файла исходников).
ну даже азбучное some-collection.filter(predicate-function) - отлично и наглядно, когда у тебя структура определена сразу над функцией предикатом, тут даже именованная функция не нужна, лямбда даже яснее будет. Но если определение в одном модуле, а именованный предикат в другом... то начинаются мучительные забеги по всем исходникам, а если ещё и совпадения имён в разных модулях и начинает "играть" порядок импортов, ой...
В принципе, это не первая идея, которая экономит время вот-прям-щас, но ценой большой жопы через пару лет. Я вот, так уж получилось, на последних работах разгребаю легаси. Одно зародилось в середине 90-х годов, другое - в середине 2000-х. Но обе построены вокруг паттерна супер-объект. Последнее к тому же в половине кода отказывается от типизации и все переменные делает типа OLE Variant. Так же проще, правда? Ну... писать простые куски конечно проще, но вот потом ловить и давить баги становится ой как хреново. Хотя в моменте - да, съэкономили и усилия, и память.
Но в промышленных ООП-языках раньше такие фичи давили и оптимизировали языки под нудно-надёжное программирование толпой винтиков на протяжение многих лет. А функциональщина, похоже, наоборот, оптимизируется под творца-гения, который как в Perl - write once read never, написал программу на лекции (очень быстро, не отнять), показал студентам и - забыл её сразу и навсегда.
Значит заговорщики пробрались даже в штаб котлина.
Ловушка через «destructuring»
Это частный случай проблемы кортежей и любых неименованных последовательностей типа списка аргументов, передаваемых в функцию.
спасибо за статью, очень понравилось что есть примеры и описание, интересно было бы посмотреть вообще как правильно или как надо, я тоже заметил что в языках много тонкостей, например в си++ в return (vec3)4*sin(10)+cos(1); можно возвращать, по началу было непривычно, потом подумал вроде логично если оператор с регионом памяти может работать(а может всё проще может это число пойдёт во все компоненты )) - подводных камней предостаточно в кодинге почти везде
Лиспоподобные языки тоже нравятся - хороший детокс от С/С++ и разгрузочная остановка )
Отстаньте вы уже от goto. Это как кому чего в руки попадет- дай дураку шар хрустальный, так он и лоб себе (и другим) расшибет.
Хочется, чтобы язык на работе защищал от коллег. Вот как с Golang и его философией, что каждая задача решается одним способом. Не нужно спорить, какой форматтер выбирать — есть только один. Не нужно просить убрать неиспользуемые vars — программа не скомпилируется. Не нужно разбираться с десятками подходов к concurrency — есть горутины. Не нужно думать, тут использовать for/forEach/while/times/repeat/sequence/range — есть только for.
Любую кодовую базу с Go открываешь в интернете — везде всё одинаково, не тратится время на разбор, что за магия происходит.
Правда, structured concurrency в Go нет, и на горутинах можно наворатить говнокода тоже. Но радует, что пространства для маневра (т.е. для говнокода) меньше.
На работе хочется договариваться о жестких рамках, а дома на пет-проектах можно делать что хочешь. Мне свои проекты нравится на Clojure писать, а на работе хотел бы писать на Go.
Вот также и с goto
— не хочется поддерживать проекты, где у людей была возможность это использовать, но сам вне работы на каком-нибудь scheme
c удовольствием потрогаю continuation
.
Ну всякие навороты могут быть полезны, например, задачах, где очень критична производительность вычислений. Например, использовать ли vector, map, ordered_map, queue, stack или еще чего? Или же например делать ли вот так или лучше так, - где меньше происходит дорогостоящих аллокаций памяти, из-за которых можно нарваться на жесткие тормоза?
Но конкретно в Go таких задач очень мало, тк этож бекенд, в котором все равно 80% времени тратится на ожидание ответа от внешней системы...
"чтобы язык защищал от коллег" - это гениальная формулировка.
Не нужно разбираться с десятками подходов к concurrency — есть горутины
А поверх них кто-то будет данные будет передавать через chan
, кто-то через shared memory (и будем надеяться что под корректным mutex'ом), кто-то побежит в нативный shm. И понеслась..
работали в Яндексе, а основу проблем не описали 😬
В крупных компаниях Яндекс, (в Сбере, Вк и тп), есть ревью (аттестация) каждые пол года или год. Там же планирование на следующее.
Если программисту ставят задачу - вот это и это на хорошую оценку, а если ещё и вот это, то отличная оценка на следующем ревью. У менеджеров также и у руководителей: показатель эффективности - сколько заключишь контрактов.
Погоня за оценкой приводит к горизонту планирования в эти самые ревью, а что там дальше будет - плевать (обычно часть команды ротируется). Плюс нынче продукт не считается жизнеспособным, если не прогнозируется его использование тысячами клиентов по всему миру.
И вот сидишь, программируешь, думаешь как и что лучше сделать, оставляешь времянки-костыли-на-потом-переделать, а руководитель в пятницу обрадует: "Всех поздравляю, мы смогли заключить контракт с ещё тремя компаниями в России, также обсуждали с УУУ из Казахстана и начали общаться с ХХХ из Бразилии. Через два месяца же сможем запустить?".
На робкие попытки пояснить за сроки начинается виляние:
У нас дух стартапа, поэтому это нормально перерабывать и запускать сырой продукт, как это было у компании ААА,БББ
Другие займут нишу, и не успеем или что если не покажем результаты сейчас, то дальше денег не дадут и всех разгонят, как было у команд ССС,ДДД
Если сделаем, то дадут бюджет нанять ещё пару человек, чтобы тебя разгрузить (и приходится брать не самых хороших, так как времени нет ждать лучших)
"А мы понимаем, какие углы можно срезать, чтобы успеть?"
"Давайте эту задачу выставим на хакатон?"
"А можно же вот эти манипуляции на ассесоров или Толоку перенести?"
В итоге это приводит к стрессу, куче ошибок, выгоранию и суициду, переходу кадров между компаниями. ☹️
Согласен со всем, что вы сказали, кроме
работали в Яндексе, а основу проблем не описали 😬
Некоторые примеры, что я привел, не связаны со сроками. Реализация логики через try-catch, неидиоматичный код, написание !isNotDisabled()
— больше skill issue.
Допускаю, что когда поменялись требования и нужен быстрый рефакторингчто, в спешке можно нагородить циклов, написать нечисные функции, разместить ловушки с destructuring.
О, определённо согласен с тем, что подобный глобальный заговор существует. Причём более заметен он на высшем уровне - на уровне где разрабатывают принципы разработки типо SOLID, DRY, clean arcitcture. Мало того, что они описаны так точно, что с момента их создания и по текущий день каждые несколько месяцев выходят статьи вида "вы неправильно понимаете %s" и разные трактовки и "углубление" понимания приводят к неоднородности кодовой базы. Так ещё и перед каждым не стоит 10 больших красных предупреждений, что это расплывчатые рекомендации, а не первостепенные правила. Как итог - экран выполняющий ровно одно действие, который можно было бы выкинуть и переписать при любом изменении - декомпозирован на 16 классов, 4 слоя, 3 модуля, имеет 2 внешние зависимости. А далее, когда появляется похожий экран, то чтобы не городить ещё раз этот огород их логику совмещают. В итоге вместо 4х однострочных экранов с понятной последовательной логикой - имеет 1 с кучей переключений, неявных связей, непонятно откуда и как подтягивающихся зависимостей, где сам черт сломит ногу, но ни один принцип не нарушен)
Насчет походов в базу в цикле - я тут у себя недавно натолкнулся - была пакетная обработка (кешим данные из бд, запускаем цикл по массиву и выполняем какую либо работу для каждого документа). То есть экономия на запросах доп данных из базы. Нагрузочные тесты что то там показали.
Потом сменил пакетную обработку на обработку по одному документу (для каждого доп данные грузились соотвественно каждый раз) - я ожидал что будет хуже, а оказалось лучше. Тот же объем документов мало того, что обработался быстрее, так и система еще стала более отзывчива. 🤔
Там конечно очень много всего наверчено - и орм и асинк/авайты. Без пол-литра не разберешься, что именно так влияет. Но по факту - вот как есть.
Да, это про то, что обязательно надо замерять. А еще хорошо бы разобраться, где был затык. Может, вся транзакция с пакетной обработкой висела, потому что параллельно другая пакетная обработка поменяла пересекающиеся данные, и первая транзакция ждет вторую на локе.
Тот пример из статьи, где 4 вложенных цикла, удалось решить запросом в базу до трех циклов и переводом данных в хешмапу. И далее запросы шли в хешмапу, а не в базу, и это на нагрузочном тестировании показало себя очень хорошо. И прод падать перестал.
1С 👹
Автор выбрал отличный способ (юмор) для донесения полезной инфо до говнокодеров 👏
Правда, судя по комментариям, тонкий юмор автора тянут не все. Мало кто может 😜
Вспомнилось детство, когда простые ";" поставленные после while(...) в IDE отошедшего покурить коллеги, могли обеспечить ему несколько часов увлекательного путешествия... 🤪
Не, самое лучшее путешествие - это через #define true false :)
Помниться что Eclipse специально переформатировал такое в что-то вида:
while (someCheck())
;
Я видел у нас хорошо сделанный проект -- и документация отличная и код и качество работы сервиса без нареканий. Но, похоже, разработчиков уже разогнали.
job security никто не отменял
зачем они нужны, если документация есть и написано так, что любой студент что надо добавит без фатальных последствий?
Поэтому некоторые всегда пишут код так, чтобы новый разработчик с легкостью в нем разобрался, минимизируют сложность, пишут комменты и иногда даже ридми для тулов, которыми упрощают разработку. У этого есть два последствия - первое - да, тебя можно выкинуть и заменить в любой момент, второе - ты сам себя этим держишь в тонусе и всегда готов выйти на поиск новой работы, даже если старая тебе нравится. Но как показали события, всякое в жизни бывает.
for ... for ... for ... for ... db.pool.execute...
Какая ерунда... Всего лишь циклы...
А как вам грид, в котором в каждой ячейке кастомная отрисовка. Грид нативный - то есть отрисовываться будет столько раз, сколько Windows отправит WM_PAINT по любой своей причине
И отрисовка каждой ячейки создает новый SQL-запрос (не одну и ту же препарированную query с параметрами, а совсем новый объект query, в котором все параметры сплайснуты в текст запроса) и отправляется на сервер.
как это прошло два ревью - я не знаю, но это несколько лет было в проде
один такой грид я отрефакторил, на один запрос для грида в целом, всех ячеек
а вот вокруг другого уже накое наросло, что переписывать уже нужно весь модуль, пришлось сдаться и пройти мимо
-----
кстати, могу предложить ещё отличную бинарную мину
грузится коллекция, в два шага
сначала примерно 80% из БД, где оно сортируется сразу по ID
потом добавляются виртуальные элементы с ID < 0, оставшиеся 20%
потом запускается QuickSort (который на почти-сортированных коллекциях знаменито плох)
при этом сравнение косвенное - функция сравнения должна выбрать по Foreign ID из другой коллекции, которая несортированная и искать в них можно только перебором
перебор этот делается в функции-геттере глобального синглтона этой коллекции
Потом я, по другим причинам, переписываю эту функцию - из цикла вверх в цикл вниз. Какая хрен разница линейному поиску откуда идти. На тестах все хорошо, уходит в релиз...
Через месяц звонок от обновившегося клиента, у него загрузка модуля стала на два порядка медленнее. Потому что QuickSort на коллекции, специально подготовленной под его худший случай, этот геттер вызывает стопиццот раз. В 15% случаев - чтобы сравнить объект сам с собой!
При этом еще два года назад сделал рефакторинг, который в частности от линейного поиска перешёл к O(1), так все отказываются тестировать, потому что этот модуль печально знаменит непредсказуемыми последствиями любых вмешательств. Мелкие еще можно выпросить, но значимые - все разбегаются.
Я видел как-то чудеса, когда для отрисовки таблички с пятью-шестью строками, выгребалась буквально вся таблица из БД, и все эти данные фильтровались на компе-клиенте. Причем работало все это очень долго, пользователи ругались, плакали, но продолжали жрать кактус, дожидаясь открытия окошка чуть ли не часами. Бага пришла к разработчикам только когда форма вовсе перестала открываться из-за падения по памяти. После переписывания на нормальный SQL запрос она же начала открываться просто моментально.
Бритва Хэнлона гласит: «Никогда не приписывайте злому умыслу то, что можно объяснить тупостью»
За одним исключением, когда это делается для достижения юмористического эффекта.
Только вот умысел тоже не стоит исключать, он хоть и менее вероятен, но все-же возможен.
Только вот умысел тоже не стоит исключать, он хоть и менее вероятен, но все-же возможен.
Если у Вас паранойя, это еще не значит, что за Вами никто не следит (с)
;-)
Мало ссылок на "телеграм-канал".
Зачем там деструкция если можно getUser().id🫣
Просто прекрасно, большое спасибо за статью, прочитал с удовольствием. Примеры будто из моей жизни)
Как-то решал проблему падающего сервера. Организация работы там была очень.. хм.. специфичная, до моего прихода кодревью вообще не использовалось, и нередко бывало что код джунов ехал в прод вообще без какой-либо хотя-бы поверхностной проверки. Команда была внутренняя и занималась допилингом системы под нужды своей фирмы.
В общем у них была боль от того что один из серверов сжирал всю оперативную память и падал по OOM. С проблемой пытались бороться тупо увеличивая память на сервере, и в конце-концов "победили" проблему, накинув ее просто очень много (точно не помню сколько, но по тем временам цифра впечатляла) и сделав принудительный рестарт сервера в период минимальной нагрузки. Сервак память выжирал, но обычно до рестарта ее хватало.
В общем мое первое задание там было - разобраться с причинами и пофиксить. Оказалось что проблема была в коде джуна, который попал в прод. Требовалось отправлять данные из текущей системы в другую, вроде какой-то CRM. Данные отправлялись пакетами через веб API той системы, и в принципе сама отправка была реализована более-менее толково, хотя и тоже с ней были проблемы - иногда она начинала DDOS-ить целевую систему, вплоть до ее падения :))
А сервак падал потому что джун видимо не знал про то что в Спринге есть шедулеры, и изобрел свой велосипед с запуском потока при старте системы, и вся бизнес логика крутилась в бесконечном цикле этого потока. При выгребании данных активно использовался Хибер, и его сущности, загруженные из базы, никогда не освобождались, все ведь в цикле работало. В итоге вся оперативка довольно быстро загаживалась этими удерживаемыми сущностями и кеш хибера распухал до чудовищных размеров, роняя сервак по памяти. :)))
Проблему исправил быстро, переписав все по нормальному, в итоге сервак стал потреблять минимум памяти и падения прекратились, ну и ддосить перестал, тут уже фиксы самой логики были довольно серьезные.
Сталкивался с подобным, но менее страшным. Тесты падали, потому что соединения к базе заканчивались, предыдущая команда дошла уже до того, что ставила 5к коннекшенов :)
Проблема была в том, что коннекшены не освобождали после использования и в каждом новом тесте создавали новые.
Такое часто бывают когда пишут свои велосипеды, по незнанию того что существует нормальные стандартные фреймворки или библиотеки, ну или из желания сделать круче. Часто про всякие мелочи вроде освобождения ресурсов тупо забывают, при тестах на компе разработчика обычно это не вызывает никаких проблем - и БД небольшая и нагрузка никакая.. Но когда это до прода доезжает, оказывается что все колом встает или ресурсы жрет так что никакой самый мощный сервер не тянет.
Стандартные библиотеки иногда тоже творят дичь.
Например, DAO в стандарте де факто для взаимодействия с Apache Cassandra и ScyllaDB (точнее кодогенератор com.datastax.oss:java-driver-mapper-processor
) при маппинге ResultSet
в entity выполняет линейный поиск соответствия имени колонки её индексу на каждое поле для каждой строки. Вместо вычисления этого маппинга ровно один раз на запрос.
То что jackson потребляет тонну времени на форматировани дат (используя java.time.format.DateTimeFormatter
) не очень удивляет, это действительно довольно дорогая операция. Но когда увидел что большую часть времени чтения из базы занимает подозрительный метод DefaultRow#firstIndexOf(String fieldName)
-- очень удивился.
Шутка про алгоритм Шлемиэля, как всегда, актуальна.
Хотя сам такую дичь тоже творил, что уж тут.
сделав функцию
notifyUsers
чистой
лолшто? бггг
напишите на Хаскель и поймете
Скрытый текст
она меняет контекст (создает оповещение) и значит не может быть чистой
она даже не идемпотентна
в лучшем случае вы можете сделать
val users = fetchUsersFromDatabase() // IO (получаем список из БД)
val messages = prepareNotifications(users) // pure (превращаем в список писем)
messages.forEach { mail -> sendMail(mail) } // IO (рассылаем письма)
По поводу заголовка (текст читать уже не хочется)
Работаю с программой, код которой раздут раза в 3 - впечатление, что платили за количество строк кода. ПХП, динамическая типизация, отсутствие контрактов, описаний, размазанная по всей программе логика, меняющиеся раз в пару лет разработчики в лучшем случае.
В итоге, сопровождение раз в 5 сложнее чем соседний проект на C#
Хочется найти того кто писал и менеджеров тоже и прибить.
По поводу “чистоты“ я имел в виду только аспект зависимости на состояние user-ов в базе. Надо было написать не `fun notifyUsers(users)`, а `prepareNotification(users): Notification`. Поправлю попозже, спасибо.
Хочется найти того кто писал и менеджеров тоже и прибить.
В подобного рода проектах, самое обидное, что каждое поколение разработчиков обычно искренне хочет исправить ситуацию, что-то улучшить, "переписать по человечески", но на это обычно не хватает времени, а потом и мотивации (бывает что и квалификации, чего уж..) в итоге переписывание и улучшение на пол пути забрасывается, а чуть позже и разработчики сваливают в закат, оставляя новый нарост дерьма легаси следующим поколениям.
Но вот стоявших у истоков любителей написать простое как можно сложнее, тех да, временами хочется найти и сделать им что-либо нехорошее..
Статья про то, что инструменты разработки не развиваются и морально устарели.
Примитивные code-viewers не умеют динамически переформатировать код в удобный для восприятия вид. Неинтеллектуальные оптимизаторы скорее выбросят что-то необходимое, чем сократят ненужное. Отладчики не умеют эмулировать среду исполнения прямо в среде разработки, чтобы на лету исполнить произвольный кусок кода.
Назрела революционная ситуация - низы не могут писать нормальный код, верхи не хотят вложиться в создание инструментов, и создать среду разработки отвечающую требования современности.
Вся надежда на ИИ. Он проанализирует код и нарисует его алгоритм, разоблачит алиасы, анимирует сложные циклы с триггерами, и заменит макросы на котиков.
Привет всем, кто узнал себя :)
Мой любимый стиль:
if ( !isNotActive ) { // do something... }
1 Кто-нибудь, кто осознал что он не самый лучший кодер: как вы пытаетесь исправить ситуацию и улучшить качество кода?
2 Я задумывался над тем, можно ли использовать фичу Copilot "review" не запуская ее вручную, в автоматическом режиме. Кто-нибудь пытался использовать что-то подобное, до того, как создать PR?
if ( !isNotActive ) { // do something... }
Поддержите мое пиво ;)
if ( isNotActive ) {} else { // do something... }
И читаемость не страдает, и менять ничего не надо ;)
Все равно плохо. Зачем мучать семантику?
if (active) { do something }
Ну так isNotActive используется в 50 местах в 30 файлах. И, возможно, где-то ещё. И не факт что в процессе переписывания в других местах не придется городить зеркальные конструкции.
Возможно, потому что от программистов требуется, чтобы код (особенно такие условия) однозначно соответствовал проекту. В проекту вот такие условия, диктуемые бизнесом. Поэтому и программист реализует их в коде именно таким образом.
И я лично считаю, что это правильно. Потому что потом при дебаге, ревью кода или дополнениях, имея на руках проект модуля и имея соответствующий код, перекомпилировать ваше "оптимизированное" выражение и сравнивать его с выражением, описанным в проекте - занимает массу времени и сил, или как пишет автор, создает когнитивные сложности.
Кстати, почему автор не написал эту ловушку, очень часто присутствующую на практике, я не понимаю. "Реализуй код, максимально не совпадающим с описанием в проекте. Пусть условные выражения делают то, что требуется в проекте, но максимально отличным от проекта. Оптимизируй все, что можно. Пусть последующие разработчики при дебаге, имея проект на руках, сравнивали твой код и условия в проекте максимально долго и мучительно. Такую бомбу ревьюверы никогда не видят и не могут увидеть, потому что никто не сравнивает код с проектом, а даже если бы и увидели, то всегда можно сказать, что ты оптимизировал выражение и упростил код."
А часто ли у нас есть такое "описание в проекте", с которым код может совпадать (иначе говоря, часто ли есть такое ТЗ, которое программист не должен додумывать, а может перевести в код дословно)?
я только сейчас задумался об этом

скорее всего всё плохо, я думал о тоглах а тут видать не то
В итоге всё упирается в качество архитектуры и ценой между временем на реализацию и поддержкой кода.
Но чем больше усилий тратится на "лёгкость" поддержки кода - всевозможные проверки условий, различные фабрики и конструкторы, тем сложнее становится код.
На одной чаше весов мы имеем предотвращение чужих ошибок, а на другой - создание огромного количества абстракций, контрактов и проверок, с которыми весьма тяжело совладать, учитывая количество связей между ними.
В итоге всё упирается в качество архитектуры и ценой между временем на реализацию и поддержкой кода.
Про архитектуру будет отдельная статья, тут скорее микро-паттерны написания кода, которые осложняют поддержку в будущем.
Но чем больше усилий тратится на "лёгкость" поддержки кода - всевозможные проверки условий, различные фабрики и конструкторы, тем сложнее становится код.
Наверное, оно так, но это никак не связано с большинством примеров, которые я приводил. Отказ от destructuring, не исползование широкого скоупа для try-catch, отказ от реализации логики через try-catch (goto), написание чистых функций, отказ от !isNotEnabled
, отказ от функций вроде implies
— всё это никак не усложнит код и не подразумевает добавление новых абстракций.
«Я просто сказал: „Слушайте, я был ведущим дизайнером четыре или пять лет. Я делаю игры уже около десяти лет. Есть ли для меня в WoW какая-нибудь работа?“
чуть позже )
Если бы я немного лучше понимал, на что способен игровой движок, что будет вызывать баги, а что реализовать легко, то это сэкономило бы нам кучу времени», — рассказывает Стрит.
Я не согласен с автором, что программисты создают очень некрасивые и "запутанные" условия в if.
На самом деле, такие условия могут быть описаны в проекте и диктоваться бизнесом. И (я считаю), разработчик должен реализовать такие условия (ОСОБЕННО условия) один-в-один, как проекте.
Потому что, когда появится необходимость продебажить ошибки, последующие разработчики будут, имея на руках проект, мучительно сравнивать условия в проекте и в коде, "декомпилируя" ваши "оптимизированные" условия и сравнивая с условиями в проекте, в попытках выяснить, почему же код аффтара выполняет процессы не тем образом, как прописано в проекте, при правильно настроенных примерах-кейсах.
Имхо: код должен однозначно соответствовать по логике проекту и требованиям бизнеса. Особенно условия.
Второе возражение на isDisabled. isDisableb поле может определяться из поля базы какой-то таблицы. Таблица и поля диктуются логикой бизнеса, то есть, ты не можешь переименовать по своему хотению имена полей. Соответственно, и поле класса ты обязан называть isDisabled. И даже делать костыль-метод isEnabled ты не имеешь права. По причине того, что последующие разрабы при дебаге, при попытке выяснить, где используется поле isDisabled, не смогут его найти и будут долго распутывать ваши "упрощения" и "оптимизации".
ПС. Возможно, это же относится к сохранению в базу объектов в начале и выборке из базы в конце метода. Потому что, согласно требованию бизнеса, применяется уже имеющаяся хранимая процедура или триггер, где-то в середине. А вы такой умный, убрали "ненужное", и все приложение развалилось, причем не у вас на дебаге, и даже не при тестировании, а в продакшене через полгода, потому что эта процедура или триггер срабатывает в одном проценте случаев всех расчетов.
Я хотел донести общую идею и не прописывал все условия, когда написание !isNotDisabled
считается саботажем, а когда нет, как и вы не прописывали в своём комментарии детали. Например, возможность вносить правки в аналитику (ТЗ), по которой делается задача. На текущем проекте у меня есть такая возможность, и я несколько раз этим пользовался.
Если в базе данных, модельках rest api или фича-флаге есть isDisabled, значит, агенты хаоса уже поработали — и теперь придется этим пользоваться. Я же так и писал в статье, что оно хорошо масштабируется. И я не давал советов, как жить, когда isDisabled
уже существует в той или иной форме, ограничившись лишь описанием проблемы.
Иногда это повод мягонько быть аналитиков тяжелыми ботинками чтоб всякая дичь не прорывалась в проектные артефакты.
Но я в целом очень за использования доменной терминологии в бизнес-логике (как минимум на верхнем уровне). Сильно упрощает общение как между разработкой и аналитикой, так и внутри разработки. При наличии разумного общего глоссария/единого языка (ubiquitous language в DDD) поддержка упрощается и некоторые логические ошибки просто не пролезают в код. Правда, это тоже достаточно дорогое удовольствие((
Учитывая картинку из превью статьи, предлагаю обратиться в Инквизицию на предмет заражения скрап-кодом
Вспомнилось... есть (был) такой язык perl - там если писать плейн-код правильно, то очень скучно и лучше вообще какойньть другой язык взять. А если писать так, как он задуман, то весело, но потом ничего не понятно. Было весело, конечно... :)
Статья жизненная, жду продолжения.
Да, топик - супер! Тоже помню команду, которой делать региональный портал на аяксе было "скучно" и они навешали спонсору лапшу про универсализм R-on-Rails. Доки были на английском, а это тоже скучно, проще было заливать таски на upwork. "Жаба" спала не долго и на стадии удушения пациентов задачи раздавались уже на "кабанчики.уа" и прочие weblancer'и. Первые подозрения, о том что на серверах завелась посторонняя "жизнь" озвучил хостер, потом угнали mailserver со всеми "учетками". Предложение о выкупе стоило около штуки баксов, ребята сообразили, что проще скинуться и переключиться на что-нибудь вроде натяжных потолков. Ессно доступов "никому и некогда", а весь деплой всегда делали в три своих умных каски, "мамой клянусь". Думается, что кейсов таких в руссофте не с один десяток наберется, и хорошо, если вскрытие состоялось, а сколько таких спящих с вялотекущим зеркалированием.
Увы, но айтишка давно перестала быть бухтой математики и "ненавредства", и роялей в кустах самых неожиданных намного вперед уже припрятано, только на hackernoon'е 30% аудитории уже female.
У нас как всегда накрыло революционно, бах и: "..в России с использованием IT-технологий совершается почти 40% преступлений. Всего в январе – ноябре 2024 с использованием технологиий совершено 702,9 тыс. преступлении. Почти две трети, или 63,7%, таких преступлении — кража или мошенничество. По состоянию на ноябрь 2024 года в подсистеме «Дистанционное мошенничество» содержится более 2,872 млн преступлении, в рамках расследования которых поставлено на учет более 5 млн телефонов и устройств, около 2 млн банковских реквизитов, 2,8 млн физлиц, более 180 тыс. юрлиц, 420,8 тыс. сайтов и 116,7 тыс. электронных кошельков», — отметили в МВД".
Почему? Да хотя бы потому, что список уязвимостей в самой популярной CMS прирастает троекратно год-к-году, но это последнее, что интересует армию "внедренцев". Про ветки на форумах "эй пограммисты, нашей ГК нужна миграция с ЗИК на ЗУП, возможна удалёнка" - просто ноу комментс. Ждем продолжения, на высоком уровне тоже фактура есть.
Статья жизненная, жду продолжения.
Спасибо, статья в процессе. Если что, можете идей накидать, может, войдет в повествование.
Одна из общих коллекций "жемчугов" - ru_govnokod и свежий пример ))
//Таб пропущенные
УсловиеОтбора = "ABANDON";
ДлинаСтрОтбора = 32;
Для с=1 По ДлинаСтрОтбора - СтрДлина(УсловиеОтбора) Цикл
УсловиеОтбора = УсловиеОтбора + " ";
КонецЦикла;
ТабПропущенные = Таб_queue.Скопировать(Новый Структура("Событие",УсловиеОтбора));
//Таб всё же дозвонились
УсловиеОтбора = "CONNECT";
ДлинаСтрОтбора = 32;
Для с=1 По ДлинаСтрОтбора - СтрДлина(УсловиеОтбора) Цикл
УсловиеОтбора = УсловиеОтбора + " ";
КонецЦикла;
ТабДозвонились = Таб_queue.Скопировать(Новый Структура("Событие",УсловиеОтбора));
В этой секте есть свои акронимические принципы. Например, SOLID
(случайно придумалось, можете забирать, если глянется)
Separated - разделённый
Разделяйте всё: декларацию от имплементации, интерфейс от логики, хранение от вычисления и т.д. Код, написанный таким образом, выглядит более профессионально.
Occult - мистический
После того, как вы всё разделили, необходимо это связать. Передайте небольшие части деклараций в имплементацию, хранилище создайте в интерфейсе и т.д. Все части, разделённые на предыдущем шаге должны хранить частичку друг друга. Это обеспечит самосохраение вашего целостного замысла при попытках рефакторинга всякими неумехами и не даст его сломать.
Теперь вам должен стать понятен истинный смысл первого шага: кто плохо разделяет, тому потом нечего связывать.
Levelled - уровневый
Для любой задачи создавайте уровни абстракции. Задумайтесь, какую общую проблему решает этот цикл, или интерфейс? Например, если вам нужно хранить конфигурацию, то необходимо понимать, что это достаточно общая задача и их удобно держать в человекочитаемом формате. Например, yaml. Создайте вначале абстракцию конфига, затем абстракцию чтерия из него в словарь. Но, поскольку в программе удобнее обращаться к переменным, не лишней будет ещё абстракция перевода значений в имена переменных модуля настроек. Но лучше - в переменные объекта, чтобы можно было иметь несколько независимых инстансов, если понадобится.
Теперь добавьте немножко оккультизма: будет хорошо, если эти имена будут вычисляться, чтобы соответствие значений конфига и переменных модуля нигде в коде не встречалось. Только так можно получить подлинную абстракцию.
Intelligent - интеллектуальный
Используйте самые передовые алгоритмические техники, которыми владеете. Это сокращает линейный код на 10%.
Descriptive - наглядно описательный
Все переменные должны быть названы в терминах ваших абстракций. Тот, кто будет читать ваш код, должен иметь возможность погрузиться в ход ваших рассуждений, чтобы начать в нём ориентироваться. Лучше, конечно, если он начнёт знакомство именно с чтения всех абстракций и правильные имена позволяют это сделать.
Неплохо, с “Occult“ — прям смешно. Кстати, немного с другой стороны я зашел во второй части статьи :)
Заговор разработчиков против корпораций