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

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

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

https://azhidkov.pro
Download Telegram
Открытый опрос - пишите в комменты.

Чем вы занимаетесь пока ИИ агент работает?
Привет!

Сыновей я делаю в три раза лучше, чем пишу книги и блоги - в канале снова будет (ещё большее) затишье на пару месяцев, пока жизнь переустоканится:)
142🎉9👍2🏆2🔥1
Привет!

Потока создания пост.

Мысль первая

Пока мне было не до интернета, практически одновременно Саша Гранин написал, что разработка с ИИ у него не работает, а Макс Морев, что у него работает.

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

По этим двум причинам (текущий подход меня тормозит, а не научиться в ИИ страшно) я сейчас мигрирую от хайпового SDD к максимально ленивой спецификации - на входе брифы задачи и решения на 1-2 странички максимум, а дальше весь дизайн just in time™️ в цикле ТДД.
И всё это на ручной тяге - брифы я пишу сам руками, потом агент их ревьювит на предмет дыр/недоспецификации, а потом цикл ТДД я веду руками же с ручным ревью каждого шага.

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

Хотя... Мой велосипед в части спеки/дизайна - это как раз то, как весь мейнстрим работал ещё пару лет назад. Так что может я просто быстрее остальных вернулся к адекватности:)

Да и SDD - выглядит как тот самый пресловутый водопад, от которого отказались 30 лет назад.

Мысль вторая

Судя по всему, для того чтобы получить кратный рост скорости разработки от применения ИИ - надо параллелить работу. При том на 3-4+ потока.

И тут я вижу две проблемы:

1. не все люди (я - точно) готовы работать в параллельном режиме. Я ещё готов вести одновременно с основной задачей разработки 1-2 мелких утилитарных задачки или баг фикса. Но вести одновременно две (или 6 🤯) полноценных задачи - нет, не мой путь.
2. не все организации (моя - точно) готовы обеспечить команду 3-4 x <размер команды> независимыми потоками работы.



Вобщем продолжаем наблюдать за ситуацией. Я всё ещё не берусь предсказать куда эта вся эпопея вырулит.

#ai@ergonomic_code #dot_agents@ergonomic_code
👍3
Привет!

У меня периодически спрашивают как обстоят дела с перформансом у ЭП в целом и функционального стиля + Spring Data JDBC в частности.

Я всегда говорил "У меня не хайлоад и мне всегда хватало - никогда не приходилось ничего целенаправленно оптимизировать".

И вот у нас Проекте Э планируется переход от ~0.5 (3 в утренний пик) RPS к штатным 17 RPS 24/7. Тоже, конечно, не супер хайлоад, но уже существенная нагрузка, которая откровенных косяков не простит.

Я соответственно пошёл мерить и к моему небольшому удивлению, бэк сходу вывез эти 17 rps в течение 10 минут. При том по прочим метрикам вывез без проблем и мог бы больше, но я пока не стал искать предел - есть более приоритетные задачи.

Единственное что, это был только первый этап и БД была практически пустая - 100К записей в целевой таблице, а с такой нагрузкой рабочий объём будет 500-600М записей.

Тестируемая операция:
0. авторизация по JWT-токену с парсингом и верификацией и с SELECT-ом его активности в БД
1. нетривиальный парсинг json-а с поддержкой 8 минорных версий схемы, валидация и дедупликация данных
2. один SELECT с локом по юзеру
3. пара простых SELECT-ов в целевую таблицу
4. маппинги DTO -> Domain -> Persistence
5. один INSERT в целевую Postgres inherited table
6. один простой INSERT в таблицу transactional outbox-а
7. асинхронно - публикация события в rabbit mq и удаление по id из outbox-а

Чутка цифр по запросам:
0. всего запросов - 14199
1. ошибок - 0
2. avg - 115.5ms
3. p90 - 151.8ms
4. p95 - 296.5ms
5. p99 - 511.9ms
6. max - 1968.9ms.

И по железу:
1. бэк - 1 pod в k8s с лимитами 400m CPU/750Mi RAM, JVM heap 550Mi. Вот тут я прям удивился, что spring-то оказывается мохёт на 500мб РАМ О_О. А в проде уменя зачем-то 2гб стоит.
2. Postgres - cloud.ru-шный менеджед на rds.pg.x1.large.2 | 2 vCPUs | 4 GB

17 RPS - тоже нифига не хайлоад, конечно, но уже и не стыдно людям рассказать.

#ergo_approach@ergonomic_code #spring_data_jdbc@ergonomic_code #project_e@ergonomic_code
2👍2😁1
Привет!

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

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

Напишите в личку - @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