Читать в телеге. Когда-то там были посты не только от меня.
Фингерпринтинг TLS
Давным-давно, когда писал краулеры, было достаточно менять User-agent и IP-адреса, чтобы сайт принимал запрос за браузерный. Но это уже прошлый век.
Сейчас можно получить “отпечаток” уже при установлении TLS-соединения: какой протокол используется, какие алгоритмы шифрования поддерживаются и т.п. — это все задается TLS-библиотекой и ее настройками. Записывается в виде “хэша” JA4. Посмотреть свой можно тут.
Ну и разумеется, подделать этот хэш не очень сложно: вот например, форк curl, который маскируется под популярные браузеры.
Переключение контекста — дорогая операция
Причем как для процессора, так и для мясного мозга.
Уже неоднократно упоминал это на канале в рамках других обсуждений (например, про количество мониторов) — ну, очевидно же! Но, увы, реальность полна разочарований :(
Исторически я стараюсь на работе делать одну задачу за раз, и фоново получаются всякие чтения чатов и мелочевка. Если цикл обратной связи большой (например, куча тестов вертится на CI) — можно взять параллельно другую задачку, пока первая простаивает. С агентами можно еще какую-то мелочевку взять третьей, но тут мой лимит кончается. Было пару раз, когда я постоянно переключался между 3-4 задачами, и каждой уделял крошечный квант внимания — во-первых, на выходе получалась шляпа, во-вторых, под вечер голова была как вареная. Стараюсь больше так не делать.
Так вот, при этом я слышу как некоторые коллеги хвастаются, насколько они AI-native и рассказывают о 20-30 агентах, работающих одновременно… Выдают ли они результаты хотя бы в 10 раз лучше? Скорее нет. Может, они стали больше делать? Может, но я уже вторую неделю слышу как высокоприоритетная задача все еще в процессе и почти готова (хотя там для MVP дня на два фокусированной работы) — ее делает рой агентов, и задача инженера, как у Микеланджело, выкинуть ненужное (ага, удачи это сделать с тысячами строк баша и сотней коммитов). Про качество даже упоминать не буду.
В общем, агенты — большое искушение делать еще больше, еще продуктивнее, еще быстрее! Но ментальная нагрузка на переключение контекста может съесть всю потенциальную выгоду: ресурсов в итоге будет потрачено больше, а результат не будет гарантированно лучше.
Доверие к коду
Лекция Кена Томпсона из 1984, после которой популяризовался термин “троян” (хотя он использовался и раньше). Интересен пример, на котором Томпсон его разобрал.
Пусть есть какая-то программа с вредоносным кодом. Получили ее исходники — там все чисто. Где искать проблему? В компиляторе/системе сборки. Получаем их исходники — тоже ничего. Где же проблема? В сборке самого компилятора. Но сборку сборки тоже надо собрать… Но неизбежно в конце будет что-то, чему нужно будет довериться.
Но хотя бы начало цепочки можно сделать очень маленьким, которое можно проверить “вручную”, и постепенно наращивать функциональность до нужной. Сам процесс построения такой цепочки называется bootstrapping (раскруткой).
У меня были смутные знания, что чем-то подобным занимается Debian, но нет, у него пока в целях только более слабое свойство — воспроизводимые сборки. А ближе всего именно к реализации изначальной идеи — экзотический менеджер пакетов Guix, у которого начало цепочки — hex0 (357 байт).
Однако, we need to go deeper — а стоит ли доверять операционной системе, где происходит сборка? А железу и его драйверам?…
"Я не знаю язык X"
Два раза за прошедшую неделю услышал подобное заявление в качестве оправдания от разных коллег и что-то пригорело.
И я допускаю, что могут быть обстоятельства, когда подобное высказывание допустимо, но:
- это не было сократовское “я знаю, что ничего не знаю”, а прям искренний комментарий касательно кода.
- это сказали не джуны, а два сеньора-помидора с миллионами лет опыта.
- языки были не какие-нибудь Haskell или Lean, а мейнстримовые Си-подобные.
- один написал код нейронкой, другой ревьюил нейронкой — господи, ну так попросите ее же вам объяснить если чего не поняли, у вас обоих доступ к Fable есть.
- если в коде лапша if-ов вместо паттерн-матчинга или switch, или массив сортируется только ради того, чтобы взять первый элемент как максимальный — это, очевидно, не в языке проблема.
- в конце концов, можно посмотреть на код вокруг и вычленить основные паттерны.
Вообще привязка к языку — это какой-то моветон. Да, когда основная работа была кодерская, это может и было важно. Но сейчас, когда тебя за ручку ведут среды разработки и статические анализаторы, да и код чаще всего пишет нейронка — какая разница, на каком языке писать? На международном рынке в нормальные места уже давно ищут бэкэнд-разработчиков или SWE, а не java-кодеров. Да и на нашем рынке тоже сдвиг идет в ту же сторону.
Я еще в школе перепрыгнул с Basic на Pascal (какой я старый), а потом на Си, а в универе вообще стало практически без разницы, на чем писать. Были и остались предпочтения (на чем писать “приятнее”), но если надо, то можно написать на чем угодно. Язык — это все-таки инструмент для решения задачи. В мейнстримовых Си-подобных языках настолько все похоже, что “я не знаю язык X” звучит как “я умею пользоваться только крестиковой отверткой, а плоской — не знаю как”.
ИИ-перевод
Попробовал перевести статью про техдолг клодом, в основном ради любопытства, насколько хорошо это делают нейронки. Сохранится ли стиль? Будут ли добавлены load-bearing словечки и прочие LLM-измы? Сможет ли нейронка использовать оригинальные термины (потому что я их перевел с английского изначально)?
Я запустил 2 сессии параллельно: в первой промпт был буквально “Translate this for an international audience”, во второй это был уже абзац с несколькими подсказками, на что стоит обратить внимание. Обе сессии были с Opus 5 High.
Первый перевод был в целом норм с точки зрения сохранения смысла, но выбор слов был местами довольно странный (например, “logical” вместо “makes sense” или “take seriously” вместо “take into consideration”), и порядок слов иногда тоже был непривычным. Разумеется, картинки остались без перевода, и было предоставлено ровно ноль контекста для локальных мемов. Цинизм и мат были смягчены. Больше всего досталось определению техдолга — оно стало более многословным и менее точным. Стиль оригинала местами сохранился, но в основном был потерян, а перевод воспринимался как машинный. Короче, мне не понравилось.
Однако я доволен этим отрицательным результатом, потому что большинство этих проблем “предсказал” во втором варианте промпта. На удивление, разница между вариантами перевода была не очень большой, в основном отличия в синонимах, числе и временах — не очень даже понятно, повлиял ли как-то на это промпт или это “естественная” случайность модели (или вообще фингерпринтинг). К сожалению, второй вариант перевода оказался не сильно лучше первого: да, там были переводы картинок и сноски про мемы, потому что я это попросил, и они в целом были средненькие, но сам перевод был местами лучше, местами хуже, и клод не смог передать стиль (“вайб”?).
Далее, я сбросил сессиям варианты друг друга и спросил что они думают (“меняемся листочками”). Первый чат ответил прямо: бери второй, с минорными правками. А второй похвалил первый за “better English rhythm”, посетовал на отсутствие перевода картинок и сносок и нашел нестыковки. Наконец, я попросил “лУчШуЮ” модель Fable 5.1 Max выдать лучший результат, даже немного помог ей, и все равно на выходе получилось не то, как будто ИИ-рулетку в третий раз покрутил.
В общем, для передачи смысла сойдет, а так средне. Сейчас модно говорить, что “ИИ заменит Х-чиков” — штош, если вы хотите качественный перевод, то ИИ еще не способен заменить переводчиков.
UPD: попробовал по совету экспертов — чистый контекст, второй промпт + нэтив спикер + косяки которые нашлись в переводах:
- Определение техдолга — все еще говно, и совпадает с определением из “грязного” контекста буква в букву.
- Опять рулетка с выбором слов/синонимов/порядка/временами/числами и т.п. Но, справедливости ради, реже спотыкаешься об такое.
- Перевод картинок хуже: например, “Можно, а зачем” в этом варианте переведен как “You could. But why?”, а Opus из второго варианта перевел как “Sure, but why?”, и в “грязном” контексте то же самое.
- У Opus “костыль” остался костылем с контекстом про слэнг, а у чистого Fable — тупо “hack”.
- Да в и целом опус интереснее/лучше объяснил мемы (чемодан, “если бы мы знали”, ТЗ и т.п.)
Короче, я не увидел качественного улучшения по сравнению с Opus — все равно надо доделывать.
Хорошая спецификация — долго, дорого и не всегда возможно
Занятная статья про 2 основных аспекта формальной верификации: дороговизну и сложность формализации. И если первый аспект можно закидать деньгами, вычислительными ресурсами и теперь еще ИИ (который уже может нагенерить, например, формальное доказательство для теоремы Ферма), то понять, а как, собственно, система должна себя вести, все еще очень тяжело (за редкими исключениями). Спецификацию сейчас не для каждого языка программирования можно найти, а для чего-то более приближенного к бизнесу если что-то и будет задокументировано, то информация о поведении будет размазана по документации, тикетам, коду, презентациям, тестам и т.п. Но даже если собрать это все в одном месте и начать формализовывать, то появляется куча вопросов про сценарии. А бизнес на большинство и не знает ответа, или ему вообще пофиг, что будет в этом случае.
Еще одна проблема заключается в том, что строго следовать спецификации тупо невыгодно. Это разобрано на примере PDF читалок: лучше как-то криво отобразить сломанный документ, чем сказать “ой, не по спецификации” и ничего не делать (закон Постела).
В общем, хотя формальная верификация и стала более доступной, она все еще слабо применима на практике.
Игры OverTheWire
Когда учился в универе, CTF был не очень популярен и прошел мимо меня. Игры OverTheWire как раз в таком формате: чтобы пройти на следующий уровень надо найти спрятанный флаг (пароль), выполняя задания.
Минимальную базу по Linux можно получить, пройдя Bandit. Даже с учетом того, что я все там знаю, все равно было занятно.
Азы по web-безопасности покроет Natas, а по шифрованию — Krypton. Остальные — про реверс-инженеринг.
Культуры зависимостей
Неплохой доклад с тезисом, что различные подходы к управлению зависимостями в индустрии — это культурный вопрос, а не технический. Начинается, разумеется, с веба, а другим примером служат игры. Жирные зависимости — плохо (потому что большая часть вам не нужна), мелкие — тоже (left-pad). Не забыт и вопрос обновления — и часто плохо, и редко плохо (но это решаемо). Короче, перед добавлением зависимости надо думать, шансы прикидывать.
Сравнение Proguard и R8
ProGuard — это обфускатор и минификатор для java, а R8 — тоже минификатор для java и де-факто стандарт для Android. У последнего сложилась репутация как более мощной замены для ProGuard, некоторые не-Android проекты даже рассматривают замену ProGuard на R8 (например, Kotlin), благо формат настроек у них почти совпадает. У меня раньше в голове был образ, что R8 — это то же самое, что и ProGuard, только новее и лучше. Реальность полна разочарований, как говорится:)
Я буду рассматривать только вопрос минификации (обфусцировать java — очень странная идея), и стоит обговориться, что область ее применения очень ограничена — на серверах места полно, а на пользовательские системы java сейчас редко доставляют (Gradle тут скорее исключение).
Вот что я обнаружил, когда попробовал заменить кастомную минификацию в Gradle сначала на R8, а потом на ProGuard:
- Вывод R8 зависит от версии JVM, а ProGuard для работы нужна JVM с jmods, которые могут быть не включены в JDK.
- R8 не позволяет ничего настроить относительно ресурсов и, как следствие, мультирелизных jar, манифестов, неактуальных контрольных сумм и т.п.
- R8 нельзя заставить удалить класс или метод, если на него есть ссылки (даже если знаете, что это логически мертвый код).
- R8 переписывает метаданные Kotlin и ломается, если ваш Kotlin новее.
- R8 переписывает методы, и, с одной стороны, код получается меньше, но может из-за этого сломаться на новейших JVM/Kotlin с чем-то вроде
java.lang.VerifyError: Bad invokespecial instruction: interface method reference is in an indirect superinterface.
При этом R8:
- Сжимает лучше, потому что работает на уровне методов, а ProGuard — на уровне классов. Т.е. ProGuard может оставить класс с 1 живым методом и сотней неиспользуемых.
- Требует меньше настроек “корневых” классов.
- Не такой строгий — если не видны какие-нибудь родительские классы, то предполагает, что они есть.
- Поддерживает Kotlin.
- Не имеет зависимостей — это один jar.
Мой сценарий довольно особенный и ProGuard оказался лучше, но для Android описанные минусы R8 слабо релевантны, так что для него он будет лучше.
ИИ-переписывание Bun
Многие наверняка слышали историю про то, что bun переписали с Zig на Rust с помощью тучи ИИ-агентов: впечатляющие графики, “новый уровень ИИ-инженеринга”, blazing fast™, убедительные результаты и т.п. Спонсирует все это Антропик, который купил bun в декабре. Эксперимент успешный, переписанный bun уже внутри Claude Code, аж с июня. А еще автор Bun на досуге запромптил Claude, чтобы сделать прорыв по гипотезе Римана. Короче, гений, миллионер, еще и плейбой наверняка.
Мало кто видел ответ создателя Zig на эту статью: “вы не разобрались”, проблема не в Zig и не в языках, потому что вопросики к автору bun были и до этой эпопеи.
А тем временем с момента статьи прошло больше месяца. Автор в режиме “ща, ща” все никак не зарелизит лучшую версию 1.4.0. На гитхабе при этом почти 5 тысяч открытых PR, большинство от агентов.
Выводы делайте сами, как говорится.
UPD: таки зарелизили.