Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Методика голосования Apache

Занятный документ, который описывает способ голосования в Apache Software Foundation. Выглядит местами переусложненно, но, возможно это следствие процесса его эволюции (похожим образом эволюционируют описания командных процессов в документации, если они есть конечно). Да и вроде как красивые модели человеческих процессов редко применимы в реальности.

Есть интересные идеи: для разных вопросов стоит использовать разные системы голосования, разные веса у голосов со спектром от -1 до +1, возможность сказать “жестко не согласен, но не буду препятствовать” и т.п. Ну и разумеется, никуда без “проще заслужить прощение, чем разрешение” и “критикуешь - предлагай”. И минусы надо обосновывать.

Разумеется, процесс в целом имеет смысл только в “плоских” организациях — если вы не можете договориться, но есть лицо, принимающее решение — пусть у него и болит голова. С другой стороны, если культура такая, что не все готовы настроиться на прагматичный лад и принять консенсусное решение вместо миллиона раундов обсуждений, то вряд ли использование какой-то схемы голосования сильно поможет.

СсылкаКомментировать

Кастомные комбинации для Compose Key

Понадобилось мне упростить себе ввод, да еще и экзотическим способом — хочу быть владычицей морскою, которая может на русской раскладке писать ј, ћ, џ, ђ и прочие славянизмы.

Разумеется, вспомнил про Compose Key — не зря блог веду, получается! :). Сначала попробовал найти готовые комбинации — все стандартные комбинации можно посмотреть в файлах локали(ей):

cat /usr/share/X11/locale/en_US.UTF-8/Compose | grep č

И если для латиницы все в порядке, то ћ не будет ни в латинской раскладке, ни в русской. Придется делать самостоятельно.

Для этого нужно создать ~/.XCompose и добавить туда сначала include "%L", чтобы работало все стандартное, а потом свои правила, например

<Multi_key> <Cyrillic_CHE> : "Ћ" U040B

Тут <Multi_key> — это собственно Compose Key, <Cyrillic_CHE> — заглавная Ч, а U040B — код для Ћ. Чтобы узнать обозначение для Ч, можно использовать xev | grep keycode. А чтобы узнать код для символа (и заодно найти его) — есть встроенное приложение Character Map, можно воспользоваться онлайн-таблицами вроде этой или чуть более прикольными штуками, которые распознают рукописные символы.

Увы, чтобы протестировать все это безобразие, придется разлогиниться и залогиниться снова, какого-то более простого способа я не нашел.

Но в итоге все получилось, я доволен.

СсылкаКомментировать

Функциональный DDD

В продолжение доклада про чистое ядро и грязную оболочку — годный доклад про то, как собственно это чистое ядро моделировать и полноценно использовать систему типов для хорошего DDD. Тоже в целом базовая база. Даже если реализовать все рассказанное на уровне псевдонимов типов — уже будет неплохо. Еще у автора есть хорошая серия постов про то, как использовать типы для бизнес-логики.

СсылкаКомментировать

Жизненный цикл аннотаций Java

Как известно, в Java есть три варианта аннотаций по RetentionPolicy:

  • Compile — видны только компилятору, полезны для преобразования кода.
  • Runtime — видны и при исполнении, можно сделать что-то рефлексией, например, создать класс из JSON.
  • Class — записаны в байткоде, но не видны через рефлексию, могут из пользоваться инструментацией или в плагинах (чтобы делать что-то при их загрузке).

А теперь минутка извращений! Несмотря на то, что через рефлексию не видны аннотации с уровнем Class, если очень-очень надо, то можно… перепрочитать класс через что-то вроде

clazz.getResourceAsStream(clazz.simpleName + ".class")

и запихать это в ClassReader из ASM с переопределенным visitAnnotation. К счастью, в прод этот код не попал, но пики надо держать острыми.

СсылкаКомментировать

Коаны Elixir

Поразвлекался с еще одним языком — Elixir. Ничего серьезного не написал, прочел LearnXinYMinutes и cheatsheet и сразу начал делать коаны.

Коаны прикольные — запустил в терминале скрипт, который отслеживает прогресс, а в соседнем окне редактируешь готовый файл-тест. Первый скрипт отслеживает изменения файла и автоматом все обновляет — удобно.

Сам язык мне показался странным: вроде типизация есть, но она не очень строгая. Список — контейнер для каши из объектов разных типов, 1 > "33" — это норма, миллион привкусов структуры/ассоциативного массива и т.п. Есть интересные идеи, но в целом впечатления смешанные.

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

Подробнее описывать пока не буду — планирую еще что-нибудь написать на Elixir попозже, чтобы полноценно акторную систему использовать.

СсылкаКомментировать

Степень влияния задержки на пользовательский опыт

Неплохая статья (хоть и с всратыми картинками) про то, как воспринимается время с точки зрения типичных пользователей:

TLDR:

  • 0.1 с — “мгновенный ответ”, “это сделал я” (печатаю символ)
  • 1 с — максимальное время без потери фокуса (предпочтительное время загрузки веб-страницы), “это сделал комп”
  • 10 с — нет сил ждать, время смотреть тик-токи и подумать о чем-нибудь другом
  • 1 м — если неинтересно, то пока
  • 1 д — оптимальное время ответа от техподдержки, ежедневная рутина
  • 1 неделя — если пользователь посещает сайт раз в неделю, то это можно назвать привычкой
  • 1 месяц — время решения в B2B процессах
  • 1 год — время, за которое можно стать “продвинутым пользователем”
  • 10 лет — время изменения орг структуры/процессов большой компании
  • 100 лет — время для перемен в обществе (эээ, сомнительно, но ладно)
СсылкаКомментировать

Впечатления от Cursor

На прошлой неделе потыкали с коллегами палкой в Cursor, который AI агент и кодит за вас. Мы попытались пофиксить относительно мелкий баг (отсутствие валидации пользовательского ввода у одного из эндпоинтов) в реальном приложении, которое уже несколько лет в проде. Написано оно на Scala + Play (что может дать представление как и о его почтенном возрасте, так и о степени легаси).

Мы открыли проект, дали агенту лог с ошибкой от невалидного ввода и попросили Cursor sanitize input. Он изменил глобальный обработчик ошибок контроллеров, по сути добавив try-catch. Попросили исправить ошибку собственно в контроллере, но не сказали, в каком. ИИ нашел похожую ошибку в другом эндпоинте, но мы решили в рамках эксперимента закрыть на это глаза и продолжить.

Что хуже, обработка была заточена под конкретный пример неправильного ввода. Промт важен: мы уточнили, что надо именно validate. ИИ использовал херовую регулярку (даже если проигнорировать контекст/инварианты). После пары итераций с доведением ее до уровня “и так сойдет”, попросили добавить валидацию для всех эндпоинтов с таким же параметром. Cursor сделал отдельный статический класс валидатора и добавил его вызов в паре контроллеров (но точно не везде, где было нужно). Решение в итоге получилось хреновым, код тоже был не супер, но по виду это должно было худо-бедно работать.

Далее мы попросили ИИ добавить тесты. Он что-то нагенерировал, но проект в итоге даже не скомпилировался из-за отсутствующих импортов. Понадобилось 3 промпта, чтобы исправить ошибки компиляции.

Наконец, запустили тесты и получили ошибки. И после этого агент резко сдулся. В тестах были проблемами с ассертами и моками, и Cursor не смог их сам исправить даже после 30 итераций “запусти тесты и поправь ошибки”. Мы даже пробили максимальный лимит на количество запущенных команд от одного промпта (25). Мы давали небольшие подсказки, предлагали решать проблемы по порядку и т.п., но ИИ не смог побороть тесты. В то время как продакшн-код был написан за полчаса, с тестами провозились больше часа и в итоге бросили.

Посмотрев на изменения, обнаружили, что ИИ в процессе добавил новый эндпоинт, хотя его никто не просил. Попросили по приколу Copilot отревьюить PR — но он не смог, потому что не знает скалу:) Мержить PR не стали — проще с нуля сделать, чем править.

В итоге впечатления похожи на те, что я писал ранее (1, 2, 3): можно использовать как помощь для рутины и грязной работы, но если ИИ застрял, то проще сделать самому. Одно из полезных открытий — если давать ИИ наводки типа “залогируй параметры, чтобы лучше понять ошибку”, то он чуть лучше работает. Не исключено, что ИИ испытывал трудности из-за стека технологий, но сомнительно, что для какого-нибудь JS были бы кардинально другие впечатления.

С точки зрения самого Cursor: есть прикольная фича, что можно заставить ИИ править код, пока не пройдут тесты, но разумеется, это не поможет, если ИИ тупит или ходит по кругу. Был еще один мелкий баг: если использовать режим “ask”, итеративно дорабатывать решение и затем применить его, код может оказаться в неконсистентном состоянии – например, в какой-то момент ИИ добавил приватный метод, который нигде не вызывался, хотя в его первом ответе вызов был.

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

P.S. На следующий день на HN в топе попался сборник проблем кодинга с AI и коллеги вбросили эту статью — там все в целом по делу.

СсылкаКомментировать

Как починить билд Gradle

У Gradle куча разных кэшей и, увы, изредка может получится так, что в них будет записано некорректное состояние. Если есть подозрения, что это действительно так, можно попробовать перезапустить билд…

  1. с флагом --rerun-tasks
  2. с --no-configuration-cache
  3. с --no-build-cache
  4. после clean
  5. с чистой домашней папкой Gradle: --gradle-user-home ./gradle_home
  6. после убийства всех демонов Gradle (по идее то же самое что и выше, но стоит попробовать)
  7. после рестарта IDE, если вызываете билд из нее.

Если вышеперечисленное не помогло, то скорее всего проблема где-то в билд-логике и/или окружении.

СсылкаКомментировать