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

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

Action для коммита в другой репозиторий

У меня на сайте сейчас два способа подключения чего-то стороннего: через git read-tree и через git submodule. Теперь появился еще один: через github action, который пушит коммит в другой репозиторий. Этот вариант оказался самым удобным, жаль, что я на него наткнулся недавно и случайно.

Пример настройки и сгенерированный коммит.

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

Будьте проще

Неплохой, хотя и немного спорный доклад про то, что надо делать вещи простыми, но это нелегко. Простота почти синонимична с изолированностью, модульностью. У простых вещей масса преимуществ — ими проще оперировать, их проще понимать, поддерживать, комбинировать (привет, UNIX-way).

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

Есть более полная версия, там чуть подробнее и есть юморок.

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

Запросы к jsonb

На SQL можно написать порой ужасные вещи, например:

SELECT id, jsonb_path_query_array(value, '$[*].entityType')
FROM demo WHERE value @? '$[*] ? (@.entityId == 1 &&
    (!exists (@.childrenIds) || exists (@.childrenIds ? (@[*] == 4))))';

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

А в запросе выбираются элементы массива, в которых есть объект с entityId и либо пустым childrenIds, либо содержащим 4. В ответе выдаются только значения полей entityType.

Полный пример
CREATE TABLE demo(
  id int not NULL,
  value jsonb Not NULL
);
INSERT into demo VALUES
  (1, '[{"entityId": 1, "entityType": "type1", "childrenIds": []}, {"entityId": 10, "entityType": "type5", "childrenIds": [30, 20]}]'::jsonb),
  (2, '[{"entityId": 1, "entityType": "type2"}, {"entityId": 15, "entityType": "type1", "childrenIds": [4, 20]}]'::jsonb),
  (3, '[{"entityId": 1, "entityType": "type3", "childrenIds": [5, 4]}, {"entityId": 25, "entityType": "type4", "childrenIds": [30, 4]}]'::jsonb),
  (4, '[{"entityId": 35, "entityType": "type4", "childrenIds": [35, 4]}]'::jsonb);

SELECT id, jsonb_path_query_array(value, '$[*].entityType')
FROM demo WHERE VALUE @? '$[*] ? (@.entityId == 1 && (!exists (@.childrenIds) || exists (@.childrenIds ? (@[*] == 4))))';

Поиграться можно тут. В ответе будет 2 и 3 запись.

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

Local-first приложения

Мартин Клеппман (тот самый, который автор книги с кабанчиком) продвигает подход Local-first. Основная идея довольно простая: все действия должны по максимуму производиться локально на машине пользователя, а облако нужно использовать для синхронизации изменений. Когда программисты работают над кодом, им не нужно, чтобы постоянно было подключение к git-серверу, а если он взорвется, то можно будет поднять свой, и данные полностью под контролем. Так почему в каких-нибудь гугл-документах все не так?

Кроме того, с точки зрения архитектуры классический путь данных в приложении сейчас — это HTML-DOM → JS-фреймворк → REST → модель → сущность ORM → запись на диске, и большая часть кода — это конвертеры одного слоя в другой. В Local-First подходе предлагается оставить HTML-DOM → JS-фреймворк, оттуда писать в локальное хранилище, а всю синхронизацию вынести в фоновый режим. Сеть — ненадежный способ передачи данных, и в классических приложениях надо решать как технические проблемы, так и проблемы с UX. Для синхронизации данных можно использовать не привязанный ни к конкретной компании, ни к конкретным моделям данных фреймворк, основанный на CRDT (conflict-free replicated data type). Группа Клеппмана разрабатывает свой фреймворк для этого, Automerge.

Идея классная, технически интересная, и как пользователю мне она нравится, но увы, непонятно, кто готов будет за это платить. Тут, как минимум, две проблемы: единство протокола и бесконтрольность компании. С единым протоколом/форматом данных можно привести в качестве канонического примера миллиард мессенджеров, при этом плюс-минус единый стандарт — это XMPP (пользуетесь jabber’ом?), а еще и рабочий при этом — это email (которому уже больше 35 лет). А когда контроль над данными переходит от сервиса к пользователю, то автоматом всплывают проблемы с моделью продажи (это уже не SaaS, в котором можно сделать что угодно), тайной алгоритмов, стоимостью поддержки старых версий, аналитикой, обновлениями и т.п.. Кажется, что подобный подход к ПО пока могут позволить себе только энтузиасты от Open Source и единичные производители.

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

JMX в Kubernetes

JMX (Java Management Extensions) — технология, с помощью которой можно подключится к java-процессу через сокет и посмотреть использование ресурсов, снять дамп памяти или даже поменять какие-нибудь значения в памяти через управляемые ресурсы (MBeans). Есть два плюс-минус стандартных инструмента для этого: jconsole и jvisualvm.

Но сейчас нельзя просто так взять и подключиться к чему-то по сокету — все в кубере лежит. А поды еще и со сгенерированными айдишниками. Сначала надо получить id пода от желаемого сервиса:

POD=`kubectl -n $NAMESPACE get pods -l app=$APPNAME -o name`

флаг -l фильтрует по метке пода, -o — оставляет в выводе только имя.

Потом получаем случайный порт (можно заморочиться и проверить, что он свободен):

PORT=`echo $(( $RANDOM % (65550 - 15000) + 15000))`
# или
PORT=`shuf -i 15000-65550 -n 1`

Перенаправляем порт ($JMX_PORT — номер порта, через который JMX выставлен локально в поде):

kubectl -n $NAMESPACE port-forward $POD $PORT:$JMX_PORT &

процесс запускается в фоне, и перенаправление будет работать только пока он запущен.

Наконец, запускаем jconsole или jvisualvm… и получаем облом из-за того, что удаленный порт-то не настоящий: нужно больше одного порта для подключения через RMI. Это можно вылечить работой через JMXMP-протокол, но нужно будет добавить его jar и в приложение, и в клиент. А после этого запустить так:

jvisualvm -cp:a jmxremote_optional.jar --openjmx "service:jmx:jmxmp://localhost:$PORT"

Или можно просто добавить в настройки приложения -Djava.rmi.server.hostname=127.0.0.1 и подключаться так:

jvisualvm --openjmx "localhost:$PORT"

PROFIT! Все это, разумеется, можно накидать в баш-скрипт, чтобы не запоминать, а указывать только namespace и имя сервиса.

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

Null в SQL

Сколько результатов вернет этот запрос?

SELECT * FROM ABS(null) as r  WHERE (r >= 0) OR NOT (r >= 0)
Ответ

Правильный ответ — ни одного, потому что условие вернет ложь. А связано это с тем, что null обрабатывается по особому, почти как NaN: почти любое выражение с ним возвращает null: ABS(null) — это null, null >=0 и NOT null — тоже. Можно сказать, что SQL реализует тернарную логику, где любое выражение может быть правдой, ложью или неизвестным. Поэтому стоит очень аккуратно писать запросы для nullable колонок, а еще лучше избегать их, если это возможно.

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

Когда нужно создавать корутины?

Интересный вопрос задали на форуме котлина: а как собственно стартовать корутины, если не рекомендуется использовать GlobalScope и runBlocking? TLDR: suspend fun main или явные CoroutineScope. А вообще в этом плане лучше читать статьи от Елизарова, он неплохо объясняет их нюансы (что неудивительно, с учетом того, что он сейчас лид разработчиков Kotlin).

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

Цвета организаций

Некоторое время назад была популярна тема деления организаций на “красные”, “янтарные”, “бирюзовые” и т.п., во многом благодаря книге 2014 года “Открывая организации будущего”. В ней консультант McKinsey Фредерик Лалу описал разные подходы к управлению компаниями, во многом опираясь на спиральную динамику (модель эволюционного развития людей) из 60-70-х, в которой уровни мышления разбиты на несколько уровней. Спиральная динамика, в свою очередь, во многом опирается на теорию циклического развития систем и теорию иерархических потребностей Маслоу (та самая пирамида, которой не было в оригинальной статье 1943 г.). В некоторых статьях это все еще щедро удобряется типами личности и прочей соционикой.

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

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

Принцип наименьшего удивления

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

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

Суть принципа очень проста: код, дизайн, UI, тесты и т.п. должны быть логичны, последовательны, соответствовать общепринятым (среди пользователей/команды) практикам и вообще не удивлять негативно. Например, строит проектировать код так, чтобы нельзя было написать “неправильно” или “неочевидно”. Если текущие инструменты не позволяют это сделать — обеспечить преодоление порога вхождения в контекст обучением, документацией и т.п., чтобы было хотя бы понятно, “как принято” писать. Другой пример: покрытие тестами не должно вызывать по причине “WTF, все сломал, а тесты еще зеленые”. А постановка задачи не должна вызывать “WTF, кому и нафиг это надо”. Даже на процессы это можно натянуть: новый сотрудник не должен в первые дни WTF’ить: что делать, как это работает, к кому идти, почему это так, кто это вообще такую дичь придумал, у кого это спросить и т.п.

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