Читать в телеге. Когда-то там были посты не только от меня.
Прецедент с Bitnami
Хороший разбор про недавнюю историю с Bitnami. Кто не в курсе, они предоставляли довольно качественные VM-образы и докер-контейнеры для популярного ПО, и если у вас есть кубер, то с большой вероятностью, что-то от них используется (например, какой-нибудь helm-chart).
К ним пришел терминальный капитализм в лице Broadcom, который купил VMware, владеющую ими, в ноябре 2023. Broadcom — те самые ребята, которые пойдут на все ради 500% прибыли. И все, чего касается их рука, рано или поздно лишается бесплатного варианта. Эффект сравним с leftpad, только тут все осознанно — чтобы все пользователи платили большие бабки, потому что альтернатива (писать свое) еще дороже.
Джавистам стоит немного напрячься от этой новости, потому что Spring’ом сейчас владеет тот же Broadcom, и, судя по тому, как развиваются события, скоро он может перестать быть бесплатным. Да, можно форкнуть, но на поддержку и развитие что Bitnami, что Spring нужно будет много ресурсов.
Постепенное внедрение изменений
Неплохой доклад про подход к внедрению изменений в компании.
Основная идея основана на теории диффузии инноваций и заключается в том, что, по-хорошему, изменение должно плавно накатываться: сначала на тестовую группу (инноваторов), потом на ранних последователей и т.д до максимальных консерваторов (лаггардов). В докладе есть пара жизненных историй и подробности про то, на что обращать внимание на каждом этапе.
Синтаксический героин
Забавный термин, на который случайно наткнулся в вики Haskell (я туда попал по запросу “cons in Haskell”, который оператор для вставки в начало). Сама статья — про недостатки синтаксического сахара и опасность его чрезмерного использования: нужно помнить много конструкций; иногда не знаешь, во что транслируется выражение с сахаром; хуже ошибки компилятора; тяжело переписывать код, когда он не укладывается в “сахарный” сценарий; сложнее писать инструменты типа линтеров; разработчики “подсаживаются” на сахар, и просят добавить еще.
Однако это только одна сторона медали, и на эту статью есть довольно выдержанный ответ, который указывает на специфичность некоторых моментов именно для конкретной реализации компилятора и вообще, надо с умом его использовать, для того, чтобы код был более читаемым, а не просто ради удобства.
На мой взгляд, основная проблема некоторый языков в этом плане — консистентность и единообразность. В Scala 2 коде, написанном в Haskell-стиле, сахар превращается в то, что тебе нужно помнить отличия всяких +:+, ++: и прочих (_*_). Кроме того, не хватает единого стиля, в Scala 3 с этим стало в целом получше. А вот в Kotlin проблема с сахаром в том, что тебе нужно выбрать тот самый вспомогательный метод из миллиона других, который подходит именно под этот конкретный случай. “Практично”, но иногда безсистемно. В идеале должен быть хороший, но минимальный “фундамент” языка с хорошими возможностями для расширения.
А вообще стоит почитать оригинальную статью от 2005 года, где синтаксическим героином называют… перегрузку функций.
Переносы в тексте
Узнал недавно о CSS свойстве hyphens, которое управляет переносами слов, и немного с ним поэкспериментировал на сайтике. Видимо, из-за того, что отвык от печатного книжного текста, переносы слов выглядели очень непривычно, даже немного отталкивающе. Не помню такого, когда, например, вычитывал свой диссер (там переносы есть и довольно много). Да даже по ссылке с документацией разметка с выравниванием по левому краю, а правый край — “рваный”, и уже не обращаешь на это внимания.
При этом выравнивание по ширине (text-align: justify;) таких чувств не вызывает и вроде смотрится прилично.
Минификация Javascript
Я попробовал два инструмента для сабжа — esbuild и rollup.
esbuild типа быстрый, написан на Go и автономный, не нужен npm, чтобы его поставить. Еще импонирует, что проблемные импорты, ведущие в никуда, он рапортует и требует явно обозначить. Однако в итоге в итоге его побороть не получилось.
Итоговый JS-модуль для поиска, сгенерированный Koka, увы, был не очень высокого качества из-за того, как я понял, что у таргета нет четкого разделения между фронтом и бэком с нодой. Я пробовал пометить нодовские модули (os, fs) как внешние, пометить импорты Koka как внешние, выбрать platform=node и даже руками удалить “неправильные” импорты, но ничего не помогло: undefined в итоговом скрипте.
Rollup сработал с первого раза, и размер бандла получился меньше. Однако он менее строг, и что было ошибкой у esbuild, у него было просто предупреждением. И автономного варианта нет — только npm install (впрочем, распространенная проблема для JS экосистемы).
Релизы на GitHub
Окей, сделать релиз на GitHub оказалось проще, чем ожидалось.
Главное — выбрать правильно action :) Официальное — в архиве, одно из 4 рекомендованных — тоже, еще 2 имеют странные настройки по умолчанию, и методом исключение было выбрано softprops/action-gh-release.
И настраивать особо не понадобилось — самому действию нужен только список файлов для загрузки, а для workflow достаточно настроить права на запись и триггер на обновление тэгов.
Кэширование GitHub Actions
Когда добавил на сайт поиск, скорость сборки, которая была подвергнута сумасшедшей оптимизации год назад, и занимала около 30 секунд (из них 6 секунд сама сборка сайта), просела до катастрофических 2,5 минут — из-за того, что теперь надо качать еще один репозиторий для поиска, собирать его и индексировать данные.
Решение — довольно очевидное: закэшировать все что можно! Для многих популярных языков/билд систем и т.п. уже скорее всего есть какие-то действия, где все подумали за вас. Например, у меня в сборке уже был ruby/setup-ruby, который использовал кэш.
Однако, если ваш стек технологий не очень зрелый и/или в нем есть экзотические шаги, то можно сделать все самому с помощью actions/cache. По сути, вас должны волновать две вещи: какую папку кэшируем и что будет ключом в кэше.
Однако есть и нюанс. Не получится закэшировать системные папки из-за проблемы с правами. Поэтому всякие установленные инструменты надо будет класть в “рабочую папку”. При этом для менеджера пакетов APT есть действие, которое помогает обойти это ограничение.
Разумеется, если у вас очень стабильное окружение для сборки, можно и докер-образ собрать. Но это уже более продвинутый уровень. Аналогично можно поиграться и с GitHub Releases.
Ну и ключи для кэша надо подбирать, хотя бы немного подумав.
В итоге у меня получилось вот такой workflow, который при горячих кэшах собирает сайт за те же 30 секунд что и раньше (опять оптимизация в 5 раз, кек), несмотря на добавленную функциональность.
Метрики сложности кода не отражают реальность
Годная обзорная статья про метрики кода (всякие там цикломатические сложности и т.п.). Статья не претендует на полноту, а является скорее призывом к осознанному подходу в этой области.
Некоторые тезисы:
- Код сам по себе слабо влияет на “читаемость” — другие факторы, возможно, более важны.
- Метрики кода коррелируют с его сложностью, но довольно слабо.
- Одно из исследований показало, что ни одна из 121 метрик сложности кода не отражает сама по себе “простоты”/“понимаемости” кода.
- Процессные метрики лучше (и правда, если ваши процессы говеные, то качество кода не спасет разработку).
- Паттерны имеют большое значение: принцип наименьшего удивления, безобразно, но единообразно и т.п.
- Сложность/понятность кода зависит от контекста™ (причем в самом широком смысле).
- Неправильные имена переменных и типов хуже отсутствия имен. При этом “неправильные” имена переменных для одного разраба могут быть “правильными” для другого.
- Существующие метрики основаны на интуиции, а не на науке/нормальных эмпирических исследованиях.
Отличие SQL Merge от Upsert
Облако напомнило, что пора бы обновить Postgres, потому что используемая версия (14) невероятно устарела. Я осознал, что мои знания о функциональности в новых версиях остановились где-то в районе 10 версии (работает и ладно же), а сейчас уже скоро будет 18, решил почитать, что нового там появилось.
Среди кучи улучшений производительность и новых фич, которые я вряд ли использую, с удивлением обнаружил, что в 15 версии добавили MERGE. Оказывается, эта команда так вообще часть стандарта SQL, причем достаточно старого, 2003. При этом проблема upsert в Postgres издревле решалась через INSERT ... ON CONFLICT ....
Нюанс в том, что MERGE решает немного другие проблемы, чем upsert. MERGE лучше предназначен для пакетных операций, и при параллельных транзакциях все равно может быть проблема, ON CONFLICT надежнее в этом плане, поскольку он атомарный.
Вроде полезно релиз-ноты читать, но у устоявшихся технологий мало что революционного происходит.
А еще SQL стандарт развивается. Теперь там еще и JSON есть, в 2023.
Pattern guards в Kotlin
В Kotlin 2.1 завезли pattern guards. Джва года (9) ждали, как говорится: после Scala очень не хватало их. Так скоро и до деструктуризации/экстракторов доживем :)