Эргономичный код
825 subscribers
93 photos
3 videos
24 files
434 links
Канал о разработке поддерживаемых бакэндов - про классическую школу TDD, прагматичное функциональное программирование и архитектуру и немного DDD.

Группа: https://t.me/+hrqD87p0Oa

Канал в Max: https://max.ru/id544512614458_biz

https://azhidkov.pro
Download Telegram
Привет!

Я решил провести аттракцион невиданной щедрости!
Да, с появлением третьего ребёнка у меня резко появилось свободное время 😂

Одному счастливчику сделаю бесплатно* полезный микро-продукт** под рабочую или личную задачу.

Напишите в личку - @d_r_q - я расскажу в чём подвох и вы решите подходит ли вам это или нет😄

Большинство из вас, конечно, само может провести свой аттракцион, но может вам лень, а кому-то из ваших знакомых надо:)

* не публичная оферта, подробности в личке :)

** приложение / сайт / бот / ИИ-агент / сервис
🔥3🤯2
Привет!

Не устали ещё от "(ещё большего) затишья на пару месяцев"? 😂
Самому страшно представить, что через два месяца начнётся 🤯

В общем я тут ещё немного поупражнялся с нагрузкой Проекта Э:

1. 2 ноды в cloud.ru по 4К в месяц
2. 1 менеджед постгрес по 4К в месяц
3. 2 пода с лимитами в ~25-50% от ресурсов ноды (1 из 4 гб РАМ на всё, 1 из 2 CPU)
4. при 70 rps в течении 10 минут - медианное время ответа 175мс, 99 персентиль - 616мс, максимум - 1470мс.
5. на 100 RPS под упёрся в CPU - скорее всего за недорого можно и на 100 RPS выйти, но для меня это уже явный оверкилл.

Попутно отловил прикольный косяк в реализации, которым на мой взгляд многие могут грешить.

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

Вобщем проверьте у себя - не делаете ли вы хэширование паролей (или другие чистые, но алгоритмически тяжёлые операции) внутри транзакций:)

#project_e@ergonomic_code
👍6
Привет!

Не спрашивайте как я нашёл на это время, но я тут посмотрел новую документалку про Рича Хикки и создание Clojure.

Если вы уже фанат Рича - посмотрите обязательно.

Если вы ещё не фанат Рича - надо им срочно становиться. На мой вкус это самый крутой визионер и инженер современности.

Для этого сначала посмотрите Simple Made Easy
Потом - Are We There Yet
Потом - Database as a Value
Потом все остальные его видосы в хронологическом порядке от корки до корки.

Ну и в конце - документалку из начала поста:)

#talks@ergonomic_code
5
Привет!

Я тут подбил немного пугающей статистики:
1. во второй половине 25-ого года, когда я ещё львную долю кода писал сам, у меня было ~1 баг на 175 строк кода.
2. а в 26-году, когда я перешёл на ИИ-разработку - уже примерно по багу на 80 строк кода 😱

Помним, конечно, что есть ложь, наглая ложь и статистика, но... Двукратный рост... Двухкратный, Карл.

И это при том, что у меня ещё достаточно хорошая страховочная сетка на регрессии - без неё у агентов поди было бы ещё хуже.

#ai@ergonomic_code
😭5
Привет!

Ну, моя эпопея с нагрузкой продолжается (часть 1, часть 2) и продолжает генерять материал для канала.

Я перешёл к этапу проверки работы с БД целевого размера (600М строк).
Идти решил постепенно: для начала залил 50М строк в целевую таблицу, запустил нагрузку и... Опять упрёся в ЦПУ на хэшировании паролей в транзакции при логине -> забитый пул подключений -> тормоза аутентификации целевого запроса на получении подключения для проверки активности токена.

Решил, что хватит извращений и надо залечить проблему с хэшированием в транзакции.

Залечил, запустил нагрузку, иии... Бэк начал 500-ить на логине. То есть стало хуже чем до фикса.

Полез копаться. Выяснилось:
1. я из транзакции вытащил чтение SDJ-агрегата, у которого было две связанных коллекции
2. т.е. это был не 1 запрос, а 1 + 2 (чтение корня + чтение коллекций).
3. при том второй запрос выполняется до завершения первого
4. и так как транзакции не было, SDJ для второго запроса захватывал новое подключение
5. в итоге 10 потоков логина на чтении корня агрегата выбирали все подключения из пулла, потом пытались сделать второй запрос и блокировались навечно, потому как все подключения уже были заняты предыдущим запросом корня, который ждал результатов запроса коллекций, который ждал... ну вы поняли:)

Завернул чтение агрегата обратно в транзакцию, запустил нагрузку (на 50М строк) и...

70 rps в течении 10 минут - медианное время ответа 137мс, 99 персентиль - 314мс, максимум - 1644мс.
т.е. в итоге корректный фикс срезал мидиану на 15%, а 99персентиль - на 30.

Мораль басни:
1. Не держите тяжёлые вычисления внутри транзакций
2. Но держите все обращения к SDJ внутри транзакций:) Вообще это прямым текстом написано в оф. доках - осталось только не забывать, не тупить и не лениться.
3. SDJ безусловно на порядок-два проще Hibernate, но всё равно слишком сложен для кожанного мешка

#spring_data_jdbc@ergonomic_code #project_e@ergonomic_code #ergo_approach@ergonomic_code
3
Отдельно стоит рассказать про "Полез копаться".

Сначала я методом пристального вглядывания начал втыкать в свой первый фикс, пытаясь понять что за фигня. Повтыкал минут 5 иии... пошёл к Codex-у с гопатычем 5.5 medium-xhigh.

Гопатыч долго (часа 3 в разных сессиях) и упорно втирал мне про, то что проблема в количестве запросов подключений и длине очереди, рисовал мне всякие формулы в духе "время обработки запроса = размер очереди * время обработки одного запроса" и т.п. И в целом был довольно убедителен.

Но у меня в голове картинка не сходилась - я не мог понять как эта очередь выстраивается так, что какой-то из запросов не получал подключение в течение 30 секунд.

Я уже было отчаялся и решил забить - проблема-то решена, по факту, пока можно жить дальше.
Но решил попробовать в вебе GPT-5.6 Sol/Xhigh и он сразу ткнул меня носом в то, что подключения тупо заканчиваются и загрузка агрегатов уходит в дедлок.

Тут у меня картинка в голове сошлась, я быстро пробежался дебаггером, увидел, что действительно всё так и происходит и успокоился.

Мораль этой басни:
1. Все врут. В том числе и топовые ЛЛМки.
2. Если в чате с ЛЛМкой вы чувствуете себя дебилом - скорее всего дебилом является ЛЛМка.

#ai@ergonomic_code
👍3💯2
Я никогда на самом деле не работал с историями, но общий посыл на сто процентов плюсую - задачи надо ставить в терминах и интересах пользователей.
3👍2
Про те самые Story

Термин "история", как синоним фичи, думаю, известен всем. Спасибо джире. Но вот что за этим словом кроется, как я вижу, понимают далеко не все.

Я очень люблю рассказывать вот такое. В 90-е наш любимый Кент Бек садился с заказчиком и просил рассказать свою историю. Что не так, что нужно починить. И вот это и есть та самая история, unit of work.

Я часто вижу, что работа бьётся так: таска на подключение к базе, таска на то, чтобы приделать Кафку, сделать "движок", "ядро", "фабрику", "стейт-машину", "скелет" или что там ещё любят делать. Оно понятно, как так получается: все сели, подумали, как решать проблему, нарезали на более-менее независимые куски, создали под них задачи и раздали программистам.

Это, конечно, никакие не пользовательские истории. Трудно представить, что ты приходишь к таксисту, оператору или, не знаю, врачу, спрашиваешь, что у него болит, а он в ответ: "ну, в базе данных нет того-то, Кафка не подключена, ядра нет".

Вроде бы и пофиг, да? В целом, да, отчасти. Это работает, но кое-что ломается. Сделав так, вы потеряли интент, намерение, заменив его инструментом. Мой последний пост был как раз про это: вы заменили цель средством. Если со средством все ок, то и цель будет достигнута. Поэтому обычно и работает.

А вот что не работает. Потерянное на этапе реализации намерение приводит к тому, что самые умные люди в процессе, инженеры, задают не те вопросы. Обычно вопрос звучит так: "мать твою, да как же это сделать". Хотя, если намерение не было потеряно, то этот вопрос меняется на: "блин, зачем так сложно, можно же по-другому". И свои драгоценные мозги инженер тратит не на покорение горы, а на ее обход.

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

В общем, технические детали - это первое, что выдаёт "неправильную историю" или, если корректнее, проблемную постановку задачи.

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

Но опять же, воспользуемся все той же проверочной эвристикой. Представьте себе, что пользователь формулирует свою боль так: я хочу войти и загрузить картинку. Нифига! Пользователь хочет просто удалить фон. А если это ещё можно сделать без авторизации и загрузки картинки, он будет только рад.

И вот эти детали реализации, как раз, опять приводят разработчика к вопросу, как победить авторизацию и загрузку, а не к вопросу, нужны ли они вообще. Где-то нужны, где-то, а где-то, весьма вероятно, нет.

Так что правильнее всего так: обычно история - это какая-то боль.

Ну хорошо, но тогда как делать, например, оплату? Представляете себе пользака, который хочет удалить фон, но при этом хочет обязательно за это заплатить. Ещё и не просто заплатить, а подписку за тыщу рублей купить. В неделю.

Это явно не боль пользователя. Но ответ элементарный: истории бывают не только у пользователей, но и у маркетологов, аналитиков, админов, начальников. И даже у разработчиков.
5
Привет!

Внимание, реклама!

Одним из потенциальных микропродуктов моего аттракциона невидной щедрости стал сервис управления закладками с оплатой российской картой (аля Firefox-овский Pocket).

С этим микропродуктом связана небольшая история.

Участник аттракциона написал, что ему нужен сервис управления закладками.
В ответ на это у меня, естественно, первый порыв был разработать сервис с нуля - как собственно я и обещал в посте про аттракцион.
Но в рамках проработки идеи я пошёл смотреть аналоги и... нашёл готовое рабочее опенсорсное self-hosted решение - https://linkding.link/.

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

И я решил проверить идею рублём - я запущу этот сервис, если до 29 июля соберу 10 возвратных депозитов по 350 рублей.

Сервис я запущу в течении 14 дней после собора нужного количества депозитов, либо верну деньги 29 июля.

Linkding позволяет вам:

1. Собрать все свои закладки в одном месте;
2. Быстро добавлять закладки с помощью расширений Firefox и Chrome;
3. Тэгировать и отмечать прочитанными закладки;
4. Сохранять копии страниц на случай удаления источника;
5. И ещё несколько минорных фич.

В дальнейшем стоимость сервиса будет 350 рублей в месяц навсегда.

Если вам нужен такой сервис - напишите мне в личку (@d_r_q).
🔥52
Привет!

Историческая минутка.

Если вы интересуетесь функциональным стилем, то возможно слышали про статью "Can Programming Be Liberated from the von Neumann Style?"

Это опубликованная версия лекции Бэкуса - автора Fortran-а и соавтора Backus-Naur Form - прочитанной при вручении ему премии Тюринга (нобелевки в мире информатики), в которой он критикует императивное программирование с операторами присваивания и в качестве альтернативы предлагает функциональный (хотя и довольно своеобразный) стиль программирования.

И если вы интересуетесь ФП, но не слышали про статью - это уже первая полезняшка:)
Но не последняя - я тут недавно около этой статьи накопал ещё пару интересных ссылок.

Во-первых, Бэкус занимался этой темой до своего выхода на пенсию в 91-ом году и было несколько попыток реализовать систему из статьи, чему в интернете посвящена целая страница :)

На этой же странице есть ссылка на сайт некоего Paul McJones, который кажется, может быть интересен любителям ИТ-археологии, но я в него не закапывался - меня таки накрыл дефицит времени, после рождения третьего ребёнка.

Во-вторых, Дейкстра (тоже лауреат премии Тюринга и мужик, который решил, что Go To плохо, придумал стурктурное программирование, семафоры, "separation of concerns", слоёную архитектуру и много ещё чего) для своих "подписчиков" сделал разгромное ревью доклада Бэйкуса, которое дошло до самого Бэйкуса через третьи руки, после чего у них случился научный махач.

Много лет спустя, разбирая свои бумаги для Библиотеки Конгресса, Бэкус каталогизировал эту переписку с комментарием:
This guy’s arrogance takes your breath away
--
От высокомерия этого парня захватывает дух


А Дейкста и правда был тем ещё токсиком:

Object-oriented programming is an exceptionally bad idea which could only have originated in California.

Объектно-ориентированное программирование — исключительно плохая идея, которая могла зародиться только в Калифорнии


The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offence.

Использование COBOL калечит разум; поэтому его преподавание следует считать уголовным преступлением


Fascination with the equipment is the hallmark of the amateur

Очарованность оборудованием — отличительный признак дилетанта




В общем если у вас есть время - покопайтесь в этих ссылках от души за меня:)

#fp@ergonomic_code #papers@ergonomic_code
4👌3
Привет!

Простые CRUD-приложения - это сложно

Если помните, я уже писал, что с момента перехода GPT-4 -> GPT-5 я качественных скачков в росте пользы от агентов больше не видел.
И вот, вышел GPT-5.6-sol - а я снова не чувствую никакой разницы с GPT-5.*

Недавно я в Проекте Э сделал с ним не совсем тривиальную задачку: обеспечить синхронизацию обработки аутбокса на нескольких репликах, которая требует обращения во внешнюю систему.

И это был ужас.
В процессе он мне в несколько заходов генерял код, который напомнил мне тот ужас, с которым я работал в "лихих нулевых и десятых" и почему я вообще начал делать Эргономичный подход.
Но мне в итоге удалось свести этот ад к простому и изящному коду.
Ну точнее - мне удалось заставить гопатыча сделать это.

И при этом грешил гопатыч ровно тем, чем грешили мои коллеги из "лихих годов":

1. локальные хаки и костыли, вместо пересмотра модели
2. привнесение надуманной сложности, которая не имела практической пользы
3. бездумный перенос кусков кода из одного контекста в другой, где они теряли смысл и вообще ломали смысл окружающего кода
4. хрестоматийные ошибки дизайна - дублирование смысла кода, с немного разным выражением (DRY); нарушение CQS; нарушения баланса захвата/освобождения ресурсов; неуместные граничные/специальные случаи; нарушение зон ответственности (do one thing)

И вся эта история с чудесным преображением кода, решающего одну и ту же по сути задачу, навела меня на другую мысль.

В прошлом году я скрипя сердцем признал, что всю жизнь занимаюсь всего лишь "простыми CRUD-приложениями", которым не нужны ни ЧА, ни DDD, ни ФА, ни любая-другая-крутая-аббревиатура. И чтобы как-то повысить ценность и значимость своей работы, а так же обосновать необходимость ЭП, я даже придумал термин "сложные CRUD-приложения".

Так вот глядя на то, как гопатыч делал какую-то невероятно ацкую реализацию для простого CRUD-приложения, я понял, что простота моего кода - это не данность, не повсеместное состояние дел и не то, что доступно любой макаке - это достижение которым можно и нужно гордится и которое основывается на моих 20+ годах практической работы и поиска способов писать простой код.

И всё это сподвигло меня начать писать новый большой пост с разбором этих сессий кодирования с вариантами названия "Шок! GPT-5.6 не может написать простую крудилку!" и "Простые CRUD-приложения - это сложно".

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

Полезняшка №1: роллауты сессий

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

А у этого скилла куча применений:
1. основное у меня - я большинство правок своего фреймворка (он, кстати, жив и развивается) идёт через скилл fix-framework-context куда я пишу ид сессии, что агент сделал не так и как надо было. И гопатыч по роллауту разбирает почему агент в сессии сделал не то что надо и как это поправить.
2. я сейчас отчёты о работе за месяц для заказчика формирую гопатычем и на вход среди прочего подаю эти сессии
3. ну и в контексте этого поста - по этим сессиям гопатыч восстановил мне хронологию наших с ним метаний во время решения задачи с привязками ко времени, промптам и бэкапам - без чего я бы вряд ли смог так подробно разобрать что происходило, как это делаю сейчас

Полезняшка №2: Бэкапы

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

И одна из особенностей этой системы заключается в том, что для моих рабочих директорий у меня снимаются снапшоты каждые 5 минут и для 8 последних дней хранятся все эти снапшоты.

Благодаря этому для каждого своего промпта за последние 8 дней я могу откопать точный код на котором его писал и понять почему я его написал.
Что оказалось так же супер ценным, для написания поста, о котором я говорил выше.

Для бэкапов системы (на Linux) я использую Timeshift, для бэкапов рабочих файлов, архивов и конфигов - backintime, а для синка файлов между устройствами - syncthing с нодой с шифрованием на VDS-е за 900р/мес.



Вобщем - пишите простой код, учитесь делегировать тупую работу агентам, следите за агентами и делайты бекапы:)

#ai@ergonomic_code #tips@ergonomic_code
👍7