(java || kotlin) && devOps
339 subscribers
14 photos
2 videos
7 files
429 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Хочется сказать пару слов о практике T-Shape. Это когда помимо своей основной специальности - например, разработки, ты развиваешь навыки в чем-то еще. Какие именно - если рассмотреть состав типовой команды, то это будут аналитик или тестировщик. Если смотреть дальше - это могут быть DevOps, НТ. Даже может быть сопровождение ПРОМа. Насколько я знаю, такая практика применяется в лидере российского ИТ - Яндексе. Рассмотрим плюсы и минусы на примере разработчика-тестировщика.

Плюсы в целом очевидны:
1) взаимозаменяемость членов команды
2) упрощение найма за счет унификации

Но есть и минусы.
1) суть работы тестировщика - найти проблемы в коде, рассматривая его как черный ящик. Именно потому, что тестировщик не знает, что внутри, но знает, как оно должно работать - ему удается найти те баги, которые не увидел разработчик. У которого просто замылился глаз, т.к. код он знает, он его проектировал, рефакторил, отлаживал. По тому же принципу, к слову, работает ручное код-ревью. Код-ревью и тестирование можно рассматривать как 2 последовательных рубежа обороны. Конечно, можно ввести практик: код пишет один разраб, а тестирует обязательно другой. Но это уже усложнение и влияние человеческого фактора.

2) как я вижу развитие инженерных специальностей и ИТ в частности - все развиваются в сторону более узкой специализации. Да, любую технологию, фреймворк, библиотеку или платформу можно изучить. Проблема в том, что их очень много. Много и в разработке, и в тестировании. И "распыляя" свои усилия на 2 направления есть риск полноценно не научится ни тому, ни другому.

3) этот пункт субъективный из-за того, что я разработчик. Сравнивая разработку и тестирование как две области деятельности я бы всегда выбирал разработку. IMHO, подчеркну, что INHO, она сложнее, имеет больше направлений и поэтому интереснее. Отсюда риск, что на тестирование будет выделяться меньше времени. Вопрос в уровне инженерной культуры и дисциплине, но это снова человеческий фактор. В каких-то командах баланс будет найден, в каких-то нет и в итоге мы получим недотестированный код и баги ПРОМа.

Итог - практика интересная, но требует высокой инженерной культуры в компании и подвержена рисками человеческого фактора.

#t_shape #devops #testing #dev
🔥3
Всем привет!

В комментариях к предыдущему посту (на Хабре) справедливо заметили, что T-Shape может быть вынужденный.
Это когда есть приложение на редкой технологии, а команда создавшая его разбежалась. Примеры технологий, которые вспомнил: Informatica, Erlang, хранимые процедуры в БД... Или legacy монолит и снова команды уже нет. А точнее остались 1-2 человека. На рынке людей или не найдёшь (редкая технология) или они не тянут распил незнакомого им монолита (второй кейс).
В этом случае оставшиеся члены команды поневоле становятся t-shape-ми.
Случай грустный. При выборе технологий была допущена ошибка, ее приходится исправлять. Но есть и плюсы. Опыт трансформации legacy, да ещё в T-shape режиме - задача очевидно сложная. А бизнесу нужны люди, умеющие решать сложные задачи.

#dev #t_shape
Всем привет!

Когда заходит речь о разделении обязанностей между разработчиками и DevOps споры возникают в двух моментах - манифесты k8s\Openshift и CI джобы - они же джобы сборки.
Поговорим про первые.
Мое мнение - за манифесты k8s\Openshift должны отвечать разработчики. Почему? А вот почему:
1) liveness\readiness probes - только разработчики знают, по каким URL они находятся. Конечно в мире Spring Boot и Actuator есть некая стандартизация, но не везде и не всегда. Не всегда actuator подходит для healthcheck, но это отдельная тема
2) liveness\readiness timeouts - разработчики точно знают сколько времени старт пода. DevOps-ы могут это время эмпирически определить, но это требует времени)
3) timeouts, circuit breaker, retry - опять же только разработчики могут сказать, реализовали они их на прикладном уровне или требуется настройка на уровне Service Mesh. Могут быть требования корпоративной архитектуры\сопровождения ПРОМ, их определяющие, но опять же не везде и не всегда.
4) куда пишутся логи, какие существуют метрики, где их можно взять. Снова можно повториться, что где-то это стандартизировано, где-то нет.
5) любые другие тонкие облачные настройки - переменные среды, значение параметров в ConfigMap, поддержка graceful shutdown ....
Наверняка DevOps или сопровожденец со всем этим разберется. Но будет ли это эффективно?

#dev #devops
👍2❤1
Всем привет!

В продолжение предыдущего поста попробую сам себе возразить.
Предположим, что наши DevOps инженеры так круто настроили pipeline, что все атрибуты, которые должен настроить разработчик, параметризованы и могут быть легко настроены разработчиком. Например, с помощью чартов Helm. Хотя по большому счёту не важно.
Значит ли это, что разработчик может расслабиться и не изучать все эти ваши Deployment, Service, EnvoyFilter, VirtualService и прочие? Мой ответ - нет. И вот почему.
1) если рассуждать дальше, то и Docker разработчику не нужен. Пусть его же DevOps-ы настраивают. А я на Tomcat встроенном запущу. Но вспомним в чем суть Docker - единая среда у разработчиков, тестировщиков и ПРОМа. Что позволяет избежать большой части ошибок, возникающих из-за разницы настроек окружения. Не всех, но большого числа
2) окей, Docker пусть будет. А k8s? Но идея та же. Приложение в облаке ведёт себя по другому, чем в standalone. Его может в любой момент прибить k8s и поднять на другой node. А это ограничивает возможности локального кэширования. В облаке несколько приложений может работать параллельно. Это нужно учитывать, например, при чтении из топика Kafka. Более того число подов может меняться - см. HorizontalPodAutoscaler. Еще момент - по умолчанию у нас ephemeral storage и надеятся на то, что те же логи сохранятся после перезапуска, нельзя. Ещё момент - одно из Cloud Native требований - быстрый старт приложения, опять же из-за потенциального перезапуска в любой момент. На этот момент не всегда обращают внимание, хотя варианты улучшения времени запуска есть, см. серию постов выше. И это я вспомнил навскидку, возможно что-то ещё упустил.

Надеюсь, я вас убедил. Если нет - жду в комментах)

#dev #devops
👍3❤‍🔥1
Всем привет!

Ну что, началось)
https://www.piter.com/collection/all/product/programmirovanie-na-python-s-pomoschyu-github-copilot-i-chatgpt
Ок, ещё одна книжка про ChatGPT. Смотрим аннотацию: «Используя GitHub Copilot, можно простым языком описать, что должна делать программа, а искусственный интеллект тут же сгенерирует ее.
Узнайте, как создавать и улучшать программы на Python с помощью ИИ, даже если прежде вы не написали ни строчки компьютерного кода.». И ещё: « Глава 4 — первая из двух глав, в которых вы научитесь читать код на языке Python. Действительно, Copilot будет писать код за вас, но вам нужно уметь читать его, чтобы определить, будет ли он делать то, что вы хотите. И не волнуйтесь: Copilot поможет вам читать код!»
И это не ролик, не статья, целая книга...
Войти в IT, если с первого раза не получилось) Интересно, на собесах Copylot уже используют?)
Меня только один вопрос мучает: если человек не захотел или не смог освоить язык программирования (фреймворк) - как хорошо он сможет спроектировать сервис или алгоритм?

#llm #dev
😁4
Всем привет!

Увидел у коллеги аналитика в канале интересное мнение. Разработка - это синоним унижения, т.к. у вас всегда что-то не будет работать.
С мнением не согласен)
Посыл, что что-то всегда не будет работать - абсолютно верный. Причём сломаться может даже сборка проекта, в котором ещё ничего не меняли. Т.е. разработка ещё не началась, а уже что-то сломалось) Обидно)
Только на мой взгляд такая ситуация - это вызов. И в т.ч. и в этом кайф от разработки.

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

#dev
👍2🔥1
Всем привет!

Одна из моих любимых тем: разработка - искусство компромиссов. Поиск по тэгам #dev_compromises и #arch_compromises. Следствие этого подхода, принцип, который я бы назвал - "не все так однозначно".

Вопрос - как вы относитесь к рефлексии в Java?

Досрочный ответ: рефлексия - это плохо, лучше не использовать. Если дошло до рефлексии - значит в архитектуре проблема.

Чтобы лучше разобраться в теме надо ответить на вопрос: а почему плохо?
Ответа два:

1) рефлексия позволяет нарушить принципы ООП и, следовательно, архитектуру приложения. Автор скрыл внутренности класса через private, а мы туда лезем своими "грязными руками")))

2) снижение производительности. Тут частично работает тот факт, что делаются лишние вызовы кода. Но самое главное - JIT компилятор плохо умеет оптимизировать такой код, т.к. он слишком динамический. Изменится может сам класс, который приходит на вход метода с рефлексией

Окей, инкапсуляция нарушается, код работает медленно. Не используем?

А что с поиском аннотаций по коду? Не своих - до них мы еще дойдем - чужих, чтобы получить некие метаданные об объекте. Большинство вариантов вот тут основано на рефлексии https://www.baeldung.com/java-scan-annotations-runtime

Или у нас есть аннотации, созданные с помощью Spring AOP - это проще, чем AspectJ, если у вас используется Spring. А Spring AOP использует динамические прокси, создаваемые в runtime https://www.baeldung.com/spring-aop-vs-aspectj А с помощью чего создается прокси в runtime - правильно, рефлексии.

Да что там AOP - создание бинов из @Configuration - это тоже вызов методов @Bean через рефлексию.

Почему же рефлексию используют и предлагают к использованию, если это такая проблемная технология?

Вернемся к двум ее недостаткам:

1) не надо использовать рефлексию для вызова private методов или доступа к private полям. Если такая задача встала - у вас в самом деле проблемы с архитектурой

2) не надо использовать рефлексию часто и при этом в высоконагруженных приложениях. Тот же Spring использует рефлексию только при старте приложения для инициализации прокси и бинов. И к слову в т.ч. и поэтому старт Spring приложения может быть долгим, и люди с этим борются, см. мой цикл статей про ускорение старта #java_start_boost Более того, разработчикам Spring для поддержки native image пришлось серьезно допиливать его в т.ч. из-за динамических прокси и @Configuration https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html

Итого: рефлексию стоит рассматривать как возможность получить метаданные о классе. Помня при этом о производительности.

И если вопрос упирается в производительность - всегда есть альтернативы рефлексии. Это работа с байт-кодом не в runtime:
1) compile time (свой компилятор, см. статью про AspectJ)
2) post-compile time (свой плагин для сборки, см. Jandex для поиска аннотаций https://smallrye.io/jandex/jandex/3.2.2/index.html)
3) load-time (свой агент+classloader, см. статью про AspectJ)
Все варианты сложнее в реализации и подключении к проекту, зато вносят минимальное влияние в runtime.

P.S. Да, если при упоминании о динамических прокси вы вспомнили про задачку с собесов о вложенном @Transactional - это оно. И ответ на этот вопрос не так очевиден https://habr.com/ru/articles/347752/

#java #reflection #dev_compromises
👍1
Всем привет!

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

Мы все используем библиотеки. Когда нужно написать тест (а книга в основном про то, как упростить написание тестов для legacy) - часто в наш класс передаются библиотечные объекты. И тут могут быть две проблемы:
1) объект-синглтон
2) final объект или объект с методами, которые не возможно переопределить.
Эти две проблемы приводят к одному и тому же - мы не можем создать mock, приходится инициализировать для тестов реальный объект из библиотеки. А он может быть "тяжелым", превращающий модульный тест в интеграционный.

Как вариант решения этой проблемы предлагается:
1) для singleton создавать фасад, который разрешает замену объекта через setter, т.е. по сути нарушать суть паттерна singleton
2) для final методов и объектов - убрать final, объявляя их логическими final в документации.

Оба предложения по сути об одном - нарушаем принципы проектирования на уровне языка, зато делаем возможным тестирование кода.

В целом - "соль" в этом есть. Код без тестов опаснее кода, нарушающего принципы проектирования. Но я бы уточнил, что оба метода - это крайние меры. А по хорошему, если вы разрабатываете код, который кто-то когда-то будет тестировать - надо заранее озаботится о тестируемости, и именно:
1) не создавать singleton самому, использовать для этого IoC контейнер. Spring если библиотека внутренняя, и у вас используется Spring, или CDI аннотации в других случаях. Причем это актуально не только для разработчиков библиотек, а для всех.
2) создавать интерфейсы для классов, вынесенных в клиентское API. Есть интерфейс - всегда можно создать mock в тесте без всяких ухищрений. Я против создания интерфейсов всегда и везде https://t.me/javaKotlinDevOps/235, но Java API - это как раз подходящий случай. Одних интерфейсов конечно же мало, нужно еще и четкое разделение на модель и сервисы. Первые можно использоваться AS IS в тестах, вторые часто приходится мокать. Да, спроектировать все правильно не так-то легко, но как говорится - дорогу осилит идущий.

#book_review #ood #dev_compromises #libraries
👍3
Всем привет!

Прочитал сегодня хороший пост (английский): https://korshakov.com/posts/death-of-best-practices

Чем же он хорош?
Иллюстрирует 2 мысли, про которые я топлю в своем блоге (и топлю в целом):

1) разработка - искусство компромиссов. Важно не абсолютное следование ... микросервисной архитектуре, принципам Single Responsibility или Dependency Inversion. Нет никакой магии в цифре 80% когда мы говорим о покрытии кода тестами. Денормализация таблиц из той же оперы. Важно выдавать результат, требуемый бизнесом. На код-ревью можно поспорить о принципах, но Pull Request должен быть влит в течение дня

2) быстрые, а следовательно частые релизы - наше все. Или по-менеджерски - Deployment Frequency и Lead Time (а это две грани одной и той же идеи). Это позволяет быстро править баги и уязвимости, адаптироваться к изменениям архитектуры и нормативным требованиям, проводить бета и A\B тестирование, соблюдать сроки выхода фичей.

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

P.S. Тоже что ли кликбейтные заголовки начать делать)

#principles #dev_compomisses
Что возвращать при ошибке?

Какие есть варианты?
1) exception
2) false
3) Optional и аналоги
4) NullObject
5) null

Для начала я бы отбросил (ну или отложил для особых случаев) вариант с null. Он давно уже "проклят", как приводящий к NPE.

Оставшиеся варианты я предлагаю разделить на две группы - NullObject vs все остальные

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

exception vs false vs Optional.

false плох тем, что не дает ничего вернуть из метода. Иногда это ок, но в общем случае - не ок. Ну и method chaining конечно же ломает.

exception vs Optional
Да начала exception бывают checked и unchecked. Первые с точки зрения обработки ошибок надёжнее - сложнее забыть обработать ошибку (если только использовать @sneakythrows и аналоги). Но checked exception имеют свои минусы, не даром нигде, кроме Java, они не прижились.

unchecked exception vs Optional
У exception есть один большой минус. Unchecked exception из чужого, плохо документированного кода, да к тому же возникающий редко - проблема, т.к. может дойти не до того уровня обработки ошибок, который планировался. Еще одна проблема - прерывание потока выполнения. Если этим злоупотреблять - имеем аналог go to, антипаттерн.С другой стороны exception  достаточно гибкий - можно отлавливать локально, на уровне сервиса или контроллера или на уровне фреймворка (Spring @ExceptionHandler). Код обработки ошибок может быть любым.
Плюсы Optional - его можно свести либо exception, либо к NullObject. Минусы - если захочешь тащить его выше - читаемость кода ухудшается. Т.е. Optional - это как правило короткоживущий объект. Он не совместим с JPA и с Java сериализацией. Еще важный минус - Optional не предоставляет средств для хранения деталей ошибки. Для меня минусы перевешивают плюсы. И судя по коду, который я вижу - не только для меня)

Если поставить вопрос: "Когда лучше обработать ошибку?" - то мой ответ: "Лучше сразу, это надёжнее".

Ну и традиционный итог: разработка - искусство  компромиссов)

#dev_compromises #null_safety #error_handling
Должен ли код быть сложным?

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

Естественная сложность кода будет всегда, т.к. причина ее появления - сложность предметной области. Это может быть сложная логика бизнес-процесса. Или возьмем Spring Core - там достаточно сложный жизненный цикл бинов, множество способов описания этих бинов, способов конфигурации, профили.... Я уже не говорю про JDK: модель байт-кода, компиляция, виртуальная машина, classloading, верификация байт-кода, JIT и оптимизации\отмена оптимизаций, сборка мусора, модель памяти, многопоточка и синхронизация доступа, поддержка различных архитектур процессора и ОС, отладка, профилирование, версионирование и обратная совместимость...
Есть понятные пути борьбы с естественной сложностью - микросервисы, слоистая архитектура, DDD и собственно объектно-ориентированное проектирование. Особенность этой сложности - она будет всегда.

Чего быть не должно - так это искусственной сложности. Причем тут бы я снова выделил две подкатегории:
1) то, на что указывают такие штуки как "большой ком грязи" или "божественный класс". Т.е. когда логика выполнения запутана потому, что за этим перестали следить. Или, в худшем случае, изначально не уделяли внимания проектированию. Усугубляет ситуацию здесь отсутствие базовой документации или ее неактуальность, огромное число ненужных настроек, плохой нейминг. Особенность этой категории сложности - вряд ли кто-то, кто увидит такой код, будет его хвалить. Все признают проблему, в т.ч. авторы. Решение - рефакторинг или переписывание кода с нуля.

2) искусственная сложность, сделанная с соблюдением принципов SOLID, ООП и слоистой архитектуры. Типичный пример здесь: микросервис с минимумом бизнес-логики, который можно сделать с использованием паттерна Transaction Script, но вместо этого появляется 3+ слоя, доменная модель, куча интерфейсов с одной реализацией, цепочка из вызовов сервисов, каждый из которых отвечает за одну функциональность по SOLID - авторизация, валидация, маппер, мониторинг, аудит, инициализация сетевых параметров, еще маппер, интеграционные логи, Circuit Breaker... Вроде все по правилам, а из простого сервиса сделан монстр, разобраться в котором очень сложно. Хотя на самом деле - правила нарушается. Как минимум правило KISS - Keep It Simple Stupid. Как максимум - не надо в том же Single Responsibility из SOLID идти до конца и для каждой функциональности, занимающей одну строчку код, делать класс. Как минимум делать это прямо сейчас. У нас же архитектура в коде. Код можно менять. В отличие от архитектуры здания, например. А разработка - это искусство компромиссов. Ну а главная проблема этой категории сложности - авторы кода точно ее не признают. Раз пишут такой код)

Итого - с любой сложностью можно и нужно бороться. Но особенно вредна искусственная сложность. По определению)

#arch #solid #complexity #principles #dev_compomisses
Рубрика "Виртуальные споры с Бугаенко")))

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

Сейчас набрел на 2 поста о переменных:
https://www.yegor256.com/2015/01/12/compound-name-is-code-smell.html
https://www.yegor256.com/2015/09/01/redundant-variables-are-evil.html

Краткое содержание первого: составные имена переменных - типа flieName - это code smell. Аргумент - если имя пришлось сделать из 2+ слов, то это значит у нам слишком большая область видимости и нарушение Single Responsibility.
Тут в целом да. Я за небольшие методы, компилятор их оптимизирует. И такой критерий простоты метода как простые имена переменных - не очевидно, не единственный, но красиво.
А вот если говорить про имена полей класса, то тут скорее нет. Да, к классам тоже применим Single Responsibility, да, в имени поля не должно повторяться имя класса. Но видится, что строгое следование данному правилу усугубит "ООП-болезнь" - бесконечное дробление классов. А так мы отходим от структуры предметной области и усложняем чтение кода из-за прыжков между файлами. Вспомним, что разработка - искусство компромиссов)

Второй пост о том, что вынесение результата выполнения строчки кода в одноразовую локальную переменную - зло. Т.к. появляется 2+ строчки вместо одной, и ухудшается читаемость.
С одной стороны да. Но если у нас есть вызов метода, он уже достаточно сложный, а расчёт значений его параметров увеличивает длину строки кода до 100, 200... символов?
Да, можно выделить часть кода в отдельные методы. Методы тоже одноразовые...
А тогда какая разница?
Поэтому я бы сказал так: автоматом выносить все расчитываемые знпченич в переменные точно не надо.
Но и обратное не верно. Иногда такой приём улучшает читаемость, и в этом случае оправдан. Снова компромиссы)))

#clean_code #dev_compromises
Зоопарк консолей в Windows.

Пишу на примере Windows 11, но в 10-ке начиная с определенного билда все также.

1) Стандартная консоль Windows - теперь их стало две:
а) это связано с тем, что в Windows появился т.наз Windows Terminal. Это мультиоконная консоль, и при вызове cmd.exe вызывается именно именно. Но т.к. она мультиоконная - там же можно открыть любую другую консоль. Все в IDEA слизали)
б) а старая добрая однооконная консоль вызывается через conhost.exe. Зачем может пригодится старая консоль? Microsoft любят ломать совместимость, и новая консоль к примеру изменила API в плане скрытия окна консоли при старте программы.

2) Powershell - тоже использует Windows Terminal, и их стало даже три:
a) powershell.exe - это старая добрая синяя консоль (цвет значка остался синий, консоль по умолчанию черная), версия 5.х
б) pwsh.exe - новая черная консоль (я снова про цвет значка), версия 7.x. На данный момент ставится только руками. Ключевое замеченное мной отличие - есть API по считыванию данных в консоли, что удобно при работе AI агента, не надо выводить данные в лог.
в) conhost.exe powershell.exe - это способ запустить powershell в старом однооконном режиме

3) Git Bash - очень удобная штука, получаем консоль Linux с большим количеством утилит. Увы, не полный набор, а главное не хватает pacman - менеджера пакетов, который в свою очередь даст поставить все остальное. Еще важная особенность - там используются linux слэши и набор переменных среды хотя и наследуется, но отличается от Windows.

4) если утилит не хватает - можно поставить MSYS. Git Bash = урезанная версия MSYS без pacman + mingw64 (С-шный комполятор gcc в Windows)

5) еще можно поставить WSL - Windows Subsystem for Linux. Поверх нее работает Docker Desktop, а это значит не можно, а нужно ставить). В этом случае появляется еще консоль wsl.exe

В итоге у меня это выглядит как на картинке.
Но в целом получаем рабочую среду)

#win #dev
🤯2❤1
Отличие сеньора от джуна

Смотрел только что видео, где обсуждается, а точнее критикуется "Чистый код" Мартина.
Критика в отдельных местах справедливая, но в большинстве кажется критикой ради критики.
Т.е. из "Чистого кода" пытаются сделать Библию с заповедями, которые якобы нужно понимать буквально. А это очевидно не так.

Но не суть.
Понравилась одна фраза:
В реальных проектах всегда будет "говнокод", т.к. есть сроки. Ключевой момент в том, что сеньор всегда может аргументировано объяснить, почему он написал именно так, а джун - нет)

#memes #dev
👍3
Что такое промт-инжиниринг?

Прочитал книжку "Промт-инжиниринг для LLM. Искусство построения приложений на основе больших языковых моделей"

Ясно, что в этой области все быстро устаревает, но в целом рекомендовать могу. Кажется книге не хватает структурированности, это скорее набор мыслей идей, инсайтов как сейчас модно говорить)

Но сейчас хотел бы обратить внимание на другое.
До сих пор я считал, что промт инжиниринг - это написание текстовых пользовательских промтов, no code.
После прочтения этой книги я бы сказал, что это только первый уровень.

Второй уровень - системный промт, который нужно писать с учетом того, что он должен задавать четкую роль для агента и направлять\ограничивать пользовательские промты. Т.е. встает вопрос совместимости.

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

Системный промт - вступление
Пользовательский промт
История диалога
rules
Список тулов, skills, MCP
Результаты вызова тулов, skills, MCP
Релевантные данные из RAG
Любые другие данные из контекста (например, открытые в IDE файлы для dev агента)
Системный промт - окончание.

Это все нужно собрать, приоритезировать, отфильтровать и встроить в шаблон запроса к LLM. А вначале этот шаблон придумать. А потом верифицировать ответ, если надо сделать цикл обратной связи для обработки некорректных ответов. А далее возможно декомпозировать на отдельные задачи для субагентов. Написать тесты для всего этого дела. Придумать метрики для отслеживания релевантности, точности и полноты ответов в ПРОМе. Наверняка еще задачи отладки и логирования станут актуальными, чтобы понять по каким веткам нашего графа идут запросы.

В общем, работы много.
И это уже не no code.
И быть такие промт-инженером уже не обидно для разработчика)

P.S. Авторы книги - бывшие разработчики GitHub Copilot.

#ai #dev
👍1
Цитата из "красной" книги:

Доля конструирования снижается, потому что с ростом размера проекта все составляющие конструирования:
проектирование, кодирование, отладка и модульное тестирование — пропорционально масштабируются,
однако затраты на другие виды деятельности растут гораздо быстрее (рис. 27-4).
...
В близких по размеру проектах будут выполняться примерно одинаковые операции,
однако при расхождении в размерах виды выполняемых действий тоже начнут различаться.
... если Gigatron Deluxe в 10 раз больше начального Gigatron,
он потребует в 25 раз больше затрат на конструирование,
в 25–50 — на планирование, в 30 — на интеграцию и в 40 — на архитектуру и системное тестирование.


И еще:

Табл. 27-2. Размер проекта и производительность

Размер проекта (число строк кода) Число строк кода на человека в год
1K 2500–25 000
10K 2000–25 000
100K 1000–20 000
1 000K 700–10 000
10 000K 300–5000

Источники: Составлено на основе данных из «Measures for Excellence» (Putnam and Meyers, 1992),
«Industrial Strength Software» (Putnam and Meyers, 1997),
«Software Cost Estimation with Cocomo II» (Boehm et al., 2000),
и «Software Development Worldwide: The State of the Practice» (Cusumano et al., 2003).


Т.е. еще в 2000-х годах было понятно, что с ростом кодовой базы мало того, что нелинейно падает скорость разработки,
так еще и практически экспоненциально растет стоимость проработки архитектуры, требований, интеграции и тестирования. Причем если интеграцию и тестирование до какого-то момента можно победить централизацией и регламентом, то контролировать архитектуру на больших объемах невозможно. Нужны "клоны" главного архитектора, а до таких глубин наука (этика?) пока не дошла)

При этом микросервисы вошли в моду где-то в 2015 году, чему способствовало развитие Docker, k8s и облаков.
А для облачных провайдеров больше сервисов = больше денег)
И они быстро подхватили этот тренд и объявили о поддержке микросервисов.

На самом деле микросервисы - это логическое развитие архитектуры ПО.
C одним но - не всегда shift left выгоден)
Уменьшать размер микросервиса до минимума можно. И формально строк кода в год (день, час) будет написано больше. AI поможет)
Но если бы сделать график стоимости разработки всей системы, а не одного сервиса, то я полагаю, что там будет две экспоненты:
одна при размере микросервиса в 1-5K LoC (строк кода), вторая - 1-5M LoC.

#arch #dev_performance
Только сейчас понял, что в канале ни разу не встречается аббревиатура SDD.
Казалось бы не встречается, ну и ... ладно, но SDD сейчас "на хайпе", придется рассказать.
Да и TDD был, DDD был, BDD был.
Тем более штука интересная. Я бы даже сказал полезная.

Вначале расшифровка: SDD - Spec Driven Development.
Т.е. в основе разработки лежит спецификация.
Казалось бы - любая команда, которая пишет аналитику уже работает по SDD?
Нет.
Ключевое отличие - спецификация хранится вместе с кодом, версионируется и поддерживается в актуальном состоянии.
По сути развитие подхода Everything as Code (EaC).
Были Infrastructure As Code, была Architecture As Code, теперь добрались до аналитики.
Отдельные вопросы:
- мы храним спеки по отдельным фичам, как их объединять если новая фича - доработка старой? AI агент или человек?
- как получить все аналитику из отдельных фичей? Отдельный скил?

Тут может возникнуть вопрос - канал то про разработку? Сейчас все будет)

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

Третий важный момент - именно спека, лежащая в проекте идет на вход AI агенту как основной артефакт.
Наравне с описанием проекта в контексте (AGENTS.md) и проектными скилами.

Еще полезный побочный эффект: при SDD больше вероятность, что ADR - architecture decision records - будут сохранены рядом с кодом в процессе совместной проработки фичи.
Вообще говоря, ADR как практика появилась еще в 2021 году, автор Майкл Нюгард (Michael Nygard), но ее широкого распространения я не вижу.
История принятия ключевых архитектурных решений часто живет в головах сеньоров, которые их приняли.
И теряется с их уходом. Или просто со временем. Что не есть гуд.

Что еще важно - тема SDD идет в паре с AI разработкой, поэтому неудивительно, что уже есть готовые SDD фреймворки.
Их много, но самые известные - OpenSpec и SpecKit.
И они предлагают разные workflow для работы над фичей. Но об этом в следующей серии.
Сейчас хотел бы отметить, что если спека пишется AI агентом совместно с аналитиком, это требует меньше участия разработчика.
Т.к. агент видит код проекта, знает язык и фреймворки, и что-то может подсказать сам.
Но конечно же не все.

P.S. Еще 22 буквы свободно)))

#ai #dev_workflow
👍1🔥1
В продолжение предыдущего поста.
Давайте сравним два AI SDD фреймворка - OpenSpec и SpecKit.

Сравнивать будем в первую очередь по составу скилов, которые выстраиваются в линейный workflow по работе над фичей.

OpenSpec - вот базовый вариант:
- /opsx:propose (написание аналитики в составе: proposal, спецификация, дизайн, план)
- /opsx:apply (реализация)
- /opsx:archive (проверка выполнения плана, слияние спек при необходимости, архивация ненужного, перемещение спеки в статус "выполнено")

Просто, всего три шага, после первых двух нужно ревью со стороне человека.

Вот максимально полный вариант:
- /opsx:explore (предварительное исследование, аналог brainstorming в superpowers)
- /opsx:new (создание только proposal.md - документ с секциями "Зачем?", "Что?", "Что меняем?", "Критерий успеха?")
- /opsx:continue 3 раза - создание остальных документов: spec.md (сценарии и требования), design.md (архитектура), tasks.md (план реализации, минимум технических деталей).
- /opsx:apply
- /opsx:verify (ревью реализации)
- /opsx:archive

Тут добавляется брейнштоминг, контроль аналитики по каждому документу отдельно и шаг ревью реализации по трем критериям: полнота, корректность и консистентность с архитектурными (design.md) и code style требованиями.
Т.е. это некий аналог code-review скила, расширенный проверкой соответствия реализации и архитектуры по фиче.

Теперь SpecKit, базовый flow:
/speckit.constitution (некий аналог /init у AI агентов, выполняется один раз)
/speckit.specify (аналитика без архитектуры и плана - spec.md)
/speckit.plan (создается технический план, по сути - архитектура, разбитый на части: contracts\*, data-model.md, plan.md, quickstart.md, research.md)
/speckit.tasks (полный аналог tasks.md)
/speckit.implement (реализация)

Расширенный flow:
/speckit.constitution
/speckit.specify
/speckit.clarify (уточнение задачи, исследования)
/speckit.plan
/speckit.tasks
/speckit.analyze (ревью консистентности аналитики)
/speckit.checklist "что проверяем" (создание чек-листа для проверки реализации в каком-то разрезе: unit tests, security)
/speckit.implement
/speckit.converge (ревью реализации)

Что хочется отметить:
- названия шагов (скилов) не совпадают от слова совсем
- у SpecKit появляется /speckit.constitution, более интерактивный init проекта, позволяющий задать базовые принципы разработки, а кроме того инициализирующий шаблоны, по которым создается аналитика, адаптируя их под текущий проект.
- у SpecKit даже в базовом варианта аналитика создается пошагово с четким разделением на бизнес и техническую части, плюс план.
- набор создаваемых артефактов отличается: совпадают spec.md и tasks.md. У SpecKit файлов больше, т.к. технические документы разделены: API, схема данных, research и собственно архитектура, почему-то названная планом.
- у OpenSpec брейншторминг идет до создания спецификации, у SpecKit - после.
- у SpecKit есть шаг ревью аналитики и шаг создания evals - приемочных чек-листов
- ревью реализации есть у обоих, но OpenSpec пишет замечания и в зависимости от их критичности предлагает сразу исправить, а SpecKit на основе расхождений заводит новые таски
- у OpenSpec есть понятие архивации, которое помечает спеку как выполненную и перемещает ее в архив.

Но как раз на архивации видно ключевое отличие этих фреймворков. В OpenSpec спецификация - это дельта-спецификация.
Что это значит? Что вначале анализируется набор существующих спек в проекте, и для новой сразу же определяется что это - новая фича, изменение или удаление существующей. И на этапе архивации происходит синхронизация - правится или удаляется существующая спецификация, или добавляется новая.
Еще одно отличие - в OpenSpec важной считается только spec.md (сценарии и требования), остальные файлы архивируются - остаются в проекте, но помещаются в папку archive.
У SpecKit такого шага нет, соответственно, слияние аналитики происходит вручную и все создаваемые файлы остаются в папке со спецификацией.

Итог - workflow достаточно сильно отличается, при этом оба соответствуют всем принципам, описанным в предыдущем посте.
У каждого есть плюсы и минусы. OpenSpec проще из коробки и частично берет на себя вопрос упорядочивания фич в проекте. SpecKit заточен на контроль и детализацию.
Я лично использую OpenSpec в связке с superpowers, но об этом в следующей серии.

#ai #ai_harness #dev_workflow
🔥1
И последнее по SDD. Последнее, но важное.

SDD - это упор на актуальную спецификацию. Т.е. на аналитику.
Из 5-10 шагов по SDD workflow к чистой разработке относится один - /opsx:apply и /speckit.implement соответственно.

Достаточно ли этого?
Для простых фичей, но при этом имеющих свою аналитику - да.
А для более сложных есть superpowers) С TDD, параллельной разработкой в разных worktree, с расширенным код-ревью и отладкой

И в обоих случаях он отлично ложится в workflow, заменяя собой упомянутые выше шаги. И ложась т.об. в середину процесса.

Два нюанса:
1) brainstorming из superpowers в такой схеме становится ненужным и заменяется аналогами из SDD фреймворков
2) writing-plans (из superpowers) нужно оставить, так как это разная детализация.
В SDD таски написаны на уровне аналитика, а writing-plans пишет классы, импорты, тесты, команды запуска скриптов.
По сути из плана, созданного через superpowers, написать код может любой джун. И ему даже скучно будет)
Или самая дешевая модель.

Тогда может возникнуть другой вопрос - а нужен ли план, созданный SDD фреймворком?
В целом не обязателен. Но если он создается одной командой с другими артефактами - можно и оставить. Или подтюнить соответствующий скил.

Еще одно важное замечание. Я встречал случаи, когда superpowers тоже относят к SDD.
Нет, ну формально конечно спека по итогу brainstorming создается, как и план по итогу writing-plans.
Но это спека и план с деталями реализации, т.к. superpowers прям заточен под разработку.
Поэтому на мой взгляд по факту это не SDD фреймворк. Ну или как минимум - не SDD для команд, где есть явная роль аналитика.

И последний момент - нужно ли использовать два фреймворки - OpenSpec + superpowers - в связке?
А точнее когда нужно их использовать?
Ответ в общем очевиден - для достаточно больших фичей, когда в репозитории по итогу разработки фичи должна остаться спецификация. Либо это требование организации, либо решение команды.

#ai #ai_harness #dev_workflow
🔥1
Давно не было провокационных постов от Егора Бугаенко на канале, ловите https://youtu.be/JRMnoQvFfKU

С чем хочется поспорить?

Первое и самое главное - не уверен, что AI разработка приведёт к исчезновению документации к коду. Аргумент Егора: раз код сейчас не читает человек, а машина быстро разбирается в любом коде - документация не нужна.
Тут сразу уточнение - я бы расширил понятие документация. Документация для меня это и Java, и тот же Openspec, и любые другие документы по архитектуре.

И я, кстати, вижу в AI чатах мнения, что OpenSpec не нужен. Так вот, если документацию убрать - получаем 2 проблемы:

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

2) идея "код содержит всю правду" IMHO утопична. Причем не важно, кто писал этот код. Всегда есть исключения. Всегда есть то, что в код пока (!) не вошло. Намерения пользователя в конце концов. Этому место в документации.

Как по мне основная проблема сейчас - избегать дублирования в документации, делать ее компактной. Чтобы не было - одна и та же мысль есть и в JavaDoc, и ещё в 3 спеках. AI любит дублировать. А идея отказаться от документации вообще красива, но утопична.

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

А вот над чем стоит подумать:

1) с увеличением числа изменений master будет ломаться чаще. Нужны правила для агентов по его автоматической починке.

2) большие pull request всегда были болью, останутся они болью и для AI агентов из-за context rot. Надо думать над тем, как автоматически бить задачи на более мелкие, с AI это сделать должно быть легче

3) настройка своего harness для контроля качества кода - это реально большая задача для всех команд на ближайшие годы.

#ai #dev
💯1