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

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

Самые сложные проблемы в разработке

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

Разработчики не могут исправить все проблемы управления. Типичные примеры: спускание идей или требований сверху; ответственность без соответствующей возможности; контроль без помощи; фиксация на сроках, а не пользе; отсутствие четких целей и т.п. Когда руководство умеет воспринимать обратную связь, это можно исправить, но с “я начальник, ты дурак” бороться, имхо, бесполезно.

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

Чисто функциональный QuickSort

Интересный доклад про оптимизации в функциональном языке программирования Roc от его автора. Он написал QuickSort в чисто функциональном стиле, который обошел по скорости мутабельный вариант на haskell. Перед перед этим автор дал очень хороший вводный обзор про варианты исполнения кода, боксинг, управление памятью и управление эффектами.

Доклад легкий, никаких эндофункторов, все сугубо практично. Конечно, пример немного синтетический, и релиза еще нет, но классно, что “обещания” ФП писать в функциональном стиле, “а компилятор разберется” становятся все более реальными.

Ссылка

Service mesh

Типичная микросервисная архитектура тащит за собой много обвязки для обнаружения сервисов, распределенной отладки (трейсинг, метрики), решения проблем надежной и безопасной отправки запросов, балансировки нагрузки и т.п. И для поддержки всего этого надо пихать библиотеки в микросервис, да еще и настраивать их. Даже если это единая экосистема (Spring Boot, Finagle, Akka, …), то она не решит всех проблем, особенно для гетерогенного стека.

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

Разумеется, за все нужно платить: дополнительные абстракции жрут ресурсы, появляются дополнительные точки отказа, все это все равно нужно настраивать и отлаживать. Да и технология пока не очень зрелая, со всеми вытекающими. Подобно тому, что кубер-кластер не надо разворачивать ради полутора микросервисов, service mesh вряд ли оправдан в рамках одной команды: он скорее для больших организаций, которые в том числе могут себе позволить платформенную команду для его поддержки.

Чуть подробнее можно посмотреть в презенташке.

Ссылка

Как энтерпрайз убивает программирование

Жизненная заметка про то, как проекты по разработке ПО для больших организаций болезненно отражаются на разработчиках. TLDR:

  • Проектная работа для нужд организации с фиксированным временем и бюджетом уныла. Иногда делают подпорку в виде команды эксплуатации, которая продолжает развивать ПО после завершения проекта, но это не решит проблему.
  • Повальная стандартизация и спускаемая сверху архитектура препятствуют развитию разработчиков и приводят к неоптимальным решениям и вялому коду.
  • Продуктовый подход лучше проектного, потому что ориентирован на долгосрочное развитие, и там можно позволить “риск” сделать что-то не квадратно-гнездовым методом.

От себя замечу, что тут отличие скорее не между “проектом” и “продуктом”, а в отношении к процессу разработки всех его участников: можно и в “продукте” пилить проекты, а “проект” продлевать каждый год. Это скорее спектр между “каждый выполняет только свою работу и строго по инструкции, никакого творчества” в бюрократическом и зарегулированном корпоративном монстре и “ты один, но зато швец, жнец и на дуде игрец, пофиг как и что будет завтра, лишь бы работало сейчас” в молодом, динамично развивающемся стартапе. Истина, как всегда, где-то посередине. А еще, разумеется, всегда актуален вопрос обратной связи: ПО нужно продавать. В энтерпрайзе его априори “купят”, потому что других вариантов нет, а сторонний покупатель может пройти мимо.

Ссылка

Слои API

В плохой микросервисной архитектуре сервисы обращаются друг к другу довольно беспорядочно. Чтобы как-то обуздать это безобразие, можно разделить все API на уровни.

Например, можно разделить все API на три слоя:

  • системный (System), который инкапсулирует доступ к (каноническим) данным;
  • процессный (Process), который инкапсулирует бизнес-процессы и бизнес-логику;
  • слой представления (Experience), который представляет данные в удобном виде для потребителя.

Очень похоже на классический паттерн трехслойной архитектуры сервиса (база, сервис, контроллер), да и идеи в основе лежат те же. Стоит оговориться, что как и любой паттерн, стоит рассматривать этот подход как идею и термин, но не как постулат.

Ссылка

Безопасность SELECT

Что может страшного случиться, если вызвать SELECT? С лимитом, разумеется. Он же только читает данные, ничего не случится?

Через SELECT можно:

  • вызвать хранимую процедуру/функцию, в которой может быть что угодно;
  • обратиться к вычисляемому полю, которое в свою очередь может дергать функцию;
  • выбрать запись для обновления и не отпустить лок;
  • просто захватить лок (например, advisory-lock);
  • вызвать агрегирующую функцию, которая затрагивает все данные, но возвращает один результат (поэтому лимит не поможет снизить нагрузку, но она может быть не критичной);
  • вызвать перезапись данных: при записи большого числа блоков часть из них может быть вытеснена из кэша с необновленным битом статуса транзакции, любое последующее обращение к таким блокам (например, select) вызовет обновление этого бита (чтобы он соответствовал действительности) и, как следствие, вызовет перезапись всего блока;
  • тупо “вымыть” данные из кэша;
  • стриггерить триггеры (например, аудит).

Вдохновлено кусочком этого доклада, во многом помог Игорян, за что ему большое спасибо!

Ссылка

Мои впечатления от Svelte

Решил попробовать, “каково это там на фронте” и выбрал Svelte, как самый любимый разработчиками веб-фреймворк по результатам опроса StackOverflow (к которому стоит относиться с опаской, потому что на втором месте ASP.NET Core, а на седьмом — Spring). Ключевая фишка — отсутствие виртуального DOM. Я вообще сначала думал, что он будет чуть ли не SSR и компиляция, но в процессе изучения понял, что ошибался.

В качестве пет-проекта сделал небольшую страничку, где можно визуально сравнить свое произношение английских слов с эталонным. Заодно немного изучил работу с микрофоном через браузер. Код можно посмотреть тут.

Основа интерактивности компонентов — во взаимодействии через привязку к переменным. Привязка может быть односторонней, а может быть двухсторонней. Двухсторонняя может быть только в случае, если свойство элемента может быть изменено пользователем — это не очень очевидно и немного не универсально: если компонент написан на чистом JavaScript, то привязаться к нему надо будет через события или через промежуточный слой. А еще не будет работать привязка на изменяемое свойство контролируемой переменной, потому что обновление вызывается при присваивании самой переменной. Условной привязки нет, поэтому чтобы заставить по галочке двигаться два ползунка одновременно, а при ее снятии — независимо, мне пришлось накостылить решение через Store, который обычно используется для сложных взаимодействий по PubSub модели. Можно было сделать через события, но получилось бы не сильно лучше, имхо.

Синтаксис шаблонирования в HTML выглядит довольно простым, жаль только, что в условиях обычный JavaScript, а не TypeScript. Впрочем, ничего сложного я с ним и не делал, в Jekyll’овском шаблонизаторе на грабли тоже не сразу наступил.

Не очень очевидным оказался процесс инициализации, потому что некоторые вещи надо вызывать через onMount, после привязки к DOM, а некоторые, например, код в “статическом” блоке — сразу.

С SSR ждало разочарование — из коробки его нет, настраивать не очень понятно как, одобренный способ еще не устаканился. “Compile-centric” тоже не ощутил: да, все зависимости в dev, но почти все ошибки все равно ловить через отладку в браузере.

По дороге меня еще ждали типичные проблемы с CORS, которые я обошел использованием allOrigins, сложности CSS (я по-прежнему офигеваю, как там тяжело сделать “простые” вещи, например, вертикальное выравнивание текста по середине). Немного споткнулся и о TypeScript — работа с undefined оставляет желать лучшего, импорт типа ведет себя не очень очевидно, но тут я скорее не все понял.

Отдельной болью была отладка и адаптация под планшет. Вляпался в отличия обработки нажатий мышкой и пальцем, неработающую запись без https, отсутствие поддержки MediaRecorder (пришлось использовать полифилл, который поддерживает API не полностью). Изюминкой было то, что если не сработала привязка в элементе, то браузер показывал просто пустой экран — и попробуй догадайся, что ему не понравилось — на планшете нет консоли разработчика, подключать ее геморройно. А проблема была в том, что не поддерживался оператор ?..

В целом впечатления остались скорее приятные. От Svelte у меня почти не горело :). Идея инкапсулировать компонент в одном файле и потом использовать как HMTL-тег на мой взгляд довольно прикольная. Выглядит похоже на Vue (как он мне смутно запомнился по мимолетному знакомству).

P.S. Не знаю, зачем я продолжаю заниматься этим мазохизмом с фронтендом :) Но возможность показать что-то доступное для интерактива немного греет.

Ссылка

Git read-tree

В гите достаточно много способов скопировать данные из одного репозитория в другой. Один из них — read-tree. Ключевая идея: берем поддерево одного репозитория и вставляем его в дерево другого. Демка для зумеров.

Мне это пригодилось для подключения репозитория на сайт. Делал на основе п.2 отсюда:

git remote add -f -t main --no-tags github-pr-stats git@github.com:ov7a/github-pr-stats.git
git read-tree --prefix=github-pr-stats -u github-pr-stats/main:build/distributions
git commit

Итоговый коммит.

Ссылка

Проверяемые исключения в Scala

В релизе 3.1.0 появилась интересная фича: проверяемые исключения. Да-да, как в java, но немного по-другому.

Во-первых, проверка не обязательная, а специально подключаемая в импортах. Оборачивать все в try-catch чисто ради удовлетворения компилятора не обязательно. Во-вторых, команда разработчиков попыталась хотя бы частично решить проблемы java-подхода.

Одна из основных проблем с непроверяемыми исключениями в том, что… их не проверяет компилятор, они вне системы типов. Поэтому некоторые упарываются и отказываются от исключений совсем, заворачивая все в монады. Это генерирует много новых проблем, например, вопрос композиции этих монад (куча паттернов типа Final Tagless или Free Monad и супермонады типа ZIO — это все варианты решения). Но код с исключениями легко писать и читать, да и “happy path” не несет накладных расходов.

В новой версии компилятор проверяет, что если в теле функции есть throw, то исключение прописано в сигнатуре. Внутри сахара — контекстные параметры, которые будут удалены после компиляции. Это как неявные параметры (implicit val) в scala 2. Т.е. если функция может кидать исключения, то она требует неявный параметр, который свидетельствует о том, что оно будет обработано. А генерится этот параметр уже в блоке try-catch.

Увы, не все так идеально, как хотелось бы: есть проблема с объявлением по-настоящему чистых функций и с оборачиванием в try-catch лямбд. Но выглядит все довольно перспективно, на мой взгляд. Если эффекты можно будет выразить через исключения, то читать код будет легче.

UPD: Видос от Одерски по теме.

Ссылка