(java || kotlin) && devOps
342 subscribers
13 photos
2 videos
7 files
419 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Долговременное хранение контекста AI агента - можно ли попроще?

Я уже писал про GAB - систему хранения контекста диалога https://t.me/javaKotlinDevOps/495.
Напомню тем кто забыл - там два компонента Memorizer (фоновое сохранение данных) и Researcher (компиляция ответа под конкретный запрос пользователя) и два вида памяти: весь диалог и короткие сводки (кэш).
И в целом GAB - это научная работа без практического внедрения.
Когда появится реализация - это будет отдельный сервис а-ля RAG.

Так вот - как можно сделать примерно тоже самое проще? Например, в контексте агента для разработки

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

Как говорится - ближе к делу.
Встречаем Cline Memory Bank https://docs.cline.bot/prompting/cline-memory-bank

На что стоит обратить внимание:
1) лично я когда говорил о долговременной памяти - я подразумевал контекст сессии.
На самом деле не обязательно им ограничиваться.
В случае разработки - сессия = таска, а контекст по проекту имеет смысл хранить постоянно.
Т.е по сути он разделяется на постоянную часть и текучку.

На примере Cline Memory Bank постоянная часть:
`projectbrief.md`
- Foundation document that shapes all other files
- Created at project start if it doesn't exist
- Defines core requirements and goals
- Source of truth for project scope

`productContext.md`
- Why this project exists
- Problems it solves
- How it should work
- User experience goals

`systemPatterns.md`
- System architecture
- Key technical decisions
- Design patterns in use
- Component relationships
- Critical implementation paths

`techContext.md`
- Technologies used
- Development setup
- Technical constraints
- Dependencies
- Tool usage patterns


Текучка:

`activeContext.md`
- Current work focus
- Recent changes
- Next steps
- Active decisions and considerations
- Important patterns and preferences
- Learnings and project insights

`progress.md`


2) Является ли такая структура памяти оптимальной? Хз, но я бы с нее начал.

3) что с поддержкой данной штуки в других агентах? без проблем, т.к. по сути memory bank = промт + способность агента читать и писать в файл.
Пример для Cursor https://www.youtube.com/watch?v=azXNHRtzd5s
Roo Code решили обвернуть это в MCP сервер https://skywork.ai/skypage/en/roo-code-memory-bank-ai-agent/1980851096638783488
Почему бы и нет, поможет при разработке других агентов, причем это могут быть агенты с разным назначением.

4) в Memory Bank хранится информация по архитектуре\структуре проекта, его стек, команды запуска, code style и прочие правила работы.
И если они уже есть в проекте - можно просто их переиспользовать, стандартизировав место хранения.

5) что из этого хранить в git? постоянную часть точно, текучку - наверное нет, т.к. у каждого она своя, но надо смотреть

В общем - рекомендую, сам буду настраивать на всех проектах.

P.S. если сравнить два подхода GAB vs Memory Bank - это как JIT и AoT компиляция

#ai #ai_agents #mcp
Что не докрутили в record-ах?

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

Получаем аналог data class в Kotlin. Или структур из древних языков типа C.

Но с небольшим отличием - не хватает метода копирования "из коробки".

Почему так сделали? Есть же Cloneable интерфейс. И его даже можно добавить в record руками.

Думаю, причина в иммутабельности record и скажем так скомпрометированности clone().

Для иммутабельной сущности нет смысла в полной копии - можно использовать текущий объект.

С clone() основная проблема в том, что "из коробки" он делает поверхностную копию, но при переопределении может дать полную. И вообще полную копию автоматически сделать сложно. А это приводит к путанице - метод один, а поведение может отличаться.

В итоге приходим к тому, что иметь возможность быстро сделать поверхностную копию записи с изменением пары полей было бы неплохо. И такой JEP есть https://openjdk.org/jeps/468
Надеюсь внедрят.

#java #иммутабельность
AI как база знаний.

В разработке есть проблема передачи знаний.
Есть разные способы - обучение, книги, онлайн контент, конференции, поиск, общение с коллегами и вот теперь AI.
Я уже писал, что AI вытесняет поиск и т.об. приводит к смерти сайтов типа stack-overflow.
Но может ли общение с LLM заменить все источники?
Т.е. есть разраб и сильная LLM, и больше ничего не надо?

Если говорить про источник знаний, то ключевые его характеристики IMHO - скорость поиска, структурированность изложения, полнота и точность.
В плане скорости AI пожалуй лучший. Это реальный прорыв
Структурированность - по большинству ответом LLM видно, что системные промты у них заточены на это.
Там и объяснения, и ссылки, и вывод, и рекомендации. А это важно, чтобы разработчик понимал, почему предлагается то или иное решение.

А вот с полнотой и точностью вечные проблемы.
Почему вечные?
В LLM по определению не может попасть вся информация.
Части нет в цифровом виде вообще. Часть "под замком" - во внутренних сетях компаний.
Часть потерялась при "сжатии" как несущественная. И к слову теряется еще сильней при квантизации LLM для запуска на более слабых устройствах.
С точностью главная беда в том, что модели почему-то не могут четко разграничить - вот точные знания, вот предположение и ссылки для самостоятельного изучения.
А пытаются скомпилировать ответ из того, что знают. Не знаю, насколько это можно пофиксить в будущем обучением, но пока так.
В теории можно фиксить проверками кода перед выдачей пользователю, хотя это более трудоемкий и концептуально плохой путь (перебор по сути).
Второй источник проблем с точностью - отсутствие метаданных в модели, в частности по версиям ПО, ОС, железа ... для которых применим тот или иной код.

В итоге что мы получаем?

LLM может сильно ускорить разработку, если ты в целом знаешь предметную область, умеешь искать информацию и критически ее анализировать.
Т.е. поможет миддлам и синьорам.
А вот джуну кажется AI способен навредить, убив в зародыше навыки, упомянутые выше.
Ну и разраб, принципиально отказывающий от AI, будет в проигрыше.

И подытоживая - в любом случае AI сильно ускоряет обмен знаниями.

#ai #knowledge_base
👍1
Может ли бюрократия быть полезной?)

Я про разработку.
И если вы сейчас думаете, что я расскажу, как многочисленные согласования:
1) текстовок и дизайна
2) рисков
3) квоты на железо (а планировать ее надо было в прошлом году...)
4) концептуальной архитектуры и архитектуры АС
5) тестовой модели
6) профиля нагрузки
7) требований сопровождения
8) и конечно же безопасности
приносит пользу команде... то нет.
Определенную пользу команде они приносят, но сейчас хочу сделать акцент на другом.

Мы живем в мире победивших микросервисов.
Я наблюдаю за тем, как люди проектируют новые сервисы и как говорят о существующих.
Вот несколько зарисовок:
1) давайте условный сервис блогов сразу разделим на микросервисы для создания постов, чтения постов, работы с комментами...
И при этом пусть работают с одной БД(
2) ну вот тут у нас большой сервис, можно сказать монолит (и чувствуется, что это слово ругательное), т.к не дали нам его нормально на микросервисы распилить.

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

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

#microservices #arch
AI везде...

В Windows Task Manager появилась отдельная вкладка, отображающая нагрузку на NPU. Удобно.
Так вот - при включении Zoom там появляется стабильная загрузка в районе 10-15%.
Это наверняка Zoom AI companion.
Но... размытие фона у меня выключено, как и в принципе видео камера, расшифровка речи тоже, нагрузка не только во время разговора (убрать шумы).

Вопрос - что он там считает?)

#ai
Немного о протекании абстракций.

Сегодня про кейс из жизни - как работает Docker на Windows.
Есть тестовый проект суть которого - работа с локальной LLM. Модель - gemma с 4b параметров. Для запуска модели (инференса) используется Docker образ ollama. Запускаю все это в Windows, что немаловажно.
Да, Docker в Windows это не тоже самое, что Docker в Linux, т.к. появляется новый слой трансляции.
Ну ок, не 5% накладные расходы, а скажем 10. Или 15.

А что в реальности?

gemma, запущенная напрямую в Windows выдает на глаз 10 токенов\с.
А в Docker скорость падает раз в 10, что уже больно.
При этом - все ядра процессора загружены на 100%.
И по использованию памяти видно, что модель полностью загрузилась в RAM. Это, кстати, отдельная тема - как LLM используют RAM.
Но вернемся к проблеме.
В 10 раз отличие по скорости...
Диалог с ChatGPT и Gemini дал пару наводок - все мимо. Docker видит все ядра процессора, доступна вся RAM, все наборы инструкций процессора, требуемые LLM, ollama видит.
Есть подсказка, что плохо пробрасывать том файловой системы с хоста Windows в Docker - ок, качаем модель внутрь файловой системы Linux. Не помогает. Да и сомнительно, т.к. модель полностью грузится в RAM, иначе LLM не работают, т.е. замедление должно быть лишь в момент запуска.
В итоге я забил смирился.

Но факт остается - по всем признакам Docker в Windows жизнеспособен для задач отладки. И в большинстве задач вполне себе нормально работает. А в конкретном кейсе - разница в 10 раз.

#docker #win #ai
Что такое совершенный код?

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

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

Пример.
Я всегда топлю за читаемость кода.
Но никогда не задумывался о расположении локальных переменных.
А Стив задумывался)
Он вводит понятия частоты использования и времени жизни переменной.

var fileFound = false;
var noteFound = false;
...
while (!found) {
...
}
...
noteFound = findNotes(...);


Вот это неправильно, т.к.
1) не нужно разделять объявление, инициализацию и использование переменной, т.е. переменную для цикла надо объявлять прямо перед ним.
2) блоки по поиску файла и заметки нужно четко разделить, чтобы было легко рефакторить и при необходимость разделить на методы.
3) еще один риск - если между объявлением переменной и ее использованием много кода - возрастает вероятность "случайно" ее поменять так, что сломается использующий переменную код.

Т.е. частота использования (число строк с переменной/число строк от первого до последнего появления переменной) должна быть высокой, время жизни - коротким.
И это увеличивает читаемость. Вроде простая штука, но отдельно о ней не задумываешь.

#book_review
Как-то я упустил важное обновление в IDEA - MCP сервер.

Почему важное и зачем вообще это нужно?

AI агенты уже умеют многое. Их стандартные навыки, они же тулы - просмотр содержимого каталогов, чтение и изменение файлов, выполнение скриптов в CLI. Примерный список навыков можно посмотреть на примере OpenCode https://opencode.ai/docs/tools/

Но IDEA умеет гораздо больше - поиск проблем в коде, разного рода рефакторинги, более умный поиск, уже готовые конфигурации запуска... Вот это бы это все агент мог делать за нас...

Ну собственно уже может)
https://www.jetbrains.com/help/idea/mcp-server.html

Список тулов на старте неплох:
- create_new_file
- execute_run_configuration
- execute_terminal_command

- find_files_by_glob
- find_files_by_name_keyword
- get_all_open_file_paths
- get_file_problems
- get_file_text_by_path
- get_project_dependencies
- get_project_modules
- get_repositories
- get_run_configurations
- get_symbol_info
- list_directory_tree
- open_file_in_editor
- reformat_file
- rename_refactoring
- replace_text_in_file
- search_in_files_by_regex
- search_in_files_by_text

Особенно радуют reformat_file и get_file_problems - даже за самым хорошим агентом приходилось исправлять.
rename_refactoring конечно же надо делать средствами IDE, т.к. в этом случае вероятность ошибок стремится к нулю.
execute_terminal_command - меня постоянно раздражают мелкие окошки терминала в чате, по возможности стоит использовать терминал IDEA.
ну и работа с run configuration ускорит процесс отладки для агента (не нужен подбор команд) и уберет дублирование (не надо дублировать в промте)
Фича в Community, поэтому появится в форках - уже в OpenIDE https://habr.com/ru/articles/989716/ и скоро в GigaIDE.

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

Лично я буду тестить. Да, по умолчанию MCP сервер выключен. И для части агентов есть автоконфигурация - Cline, Cloude Code. Но даже там, где ее нет это не проблема.

#ide #idea #ai #mcp
И чтобы "зафиналить" тему с множественными отрицаниями в условиях пару цитат из книжки)))

Я не не нетупой.

Гомер Симпсон
(Homer Simpson)


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


P.S. Кто-нибудь пробовал вычислить вторую фразу?)

#book_review
🔥1
Каким должен быть AI autocomplete?

Для начала - почему я задался этим вопросом?
Потому что перед глазами есть autocomplete, который слишком часто выдает ерунду, и у каждого, кто им пользовался, появляется желание его отключить)

Необходимые условия:
1) у агента autocomplete должен быть свой RAG, где он хранит контекст всего проекта. В идеале - с подключением внешних папок с кодом или других репозиториев git.
2) агент должен быть быстрым, т.е. это отдельная модель с небольшим числом параметров. Т.к. скорость модели прямо пропорциональна числу ее параметров.
3) агент должен оперативно подхватывать изменения - т.е. если я изменил класс А, перешел в класс Б и хочу вызвать новый код класса А - он сразу же должен быть доступен.

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

Приведу хорошие примеры, когда autocomplete должен работать:
1)
List<String> someList = // тут бы лучше помолчать, если подходящего list нет в контексте метода
List<String> someList = someSet. // вот тут уже можно предложить например stream().collect(Collectors.toList());


2)
BigInteger md5Hash(String text) {
// вот тут autocomplete вполне бы мог добавить расчет хэша с помощью java.security.MessageDigest с конвертацией в int
// а вот пока я пишу объявление метода - лучше помолчать


3)
var someEntity = SomeEntity.builder(). // тут в теории можно предлагать методы билдера в порядке следования полей в классе, но не критично
// а вот следующим шагом вполне можно предложить someEntityRepocitory.save(someEntity);


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

Итого - иногда лучше молчать, чем говорить)

#ai #ide #ai_agents
Два подхода к написанию функций (методов).

Первый - у метода должна быть одна точка выхода (в общем случае - как можно меньше точек выхода). А точка входа по умолчанию одна. Потому что это улучшает читаемость.
Второй - если у метода есть предусловия, то их нужно проверять с помощью охранных выражений в начале метода. Т.к. это уменьшает вложенность и опять же улучшает читаемость)

Т.е

public void processUser(User user) {
if (user != null) {
if (user.getAge() >= 18) {
if (validateEmail(user.getEmail()) != null && user.getEmail().contains("@")) {
if (user.isVerified()) {
performUserRegistration(user);
}
}
}
}
}


vs

public void processUser(User user) {
if (user == null) return;

if (user.getAge() < 18) return;

if (user.getEmail() == null || !user.getEmail().contains("@")) return;

if (!user.isVerified()) return;

performUserRegistration(user);
}


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

В Совершенном коде автор тоже за второй вариант. Присоединяюсь и считаю, что второй вариант - это база.
Возможно есть кейсы, когда он не применим, но я пока придумать не могу.
Даже на 2 условиях. Даже если недостаточно простого return.

#book_review #code
Снова минутка цитат на канале:
«Термин „структурное программирование“ был введён в 1969 году.
С тех пор термин „структурный“ применялся к любой деятельности в области ПО, включая структурный анализ, структурный дизайн и структурное валяние дурака».
Стив Макконнелл «Совершенный код».


Лично я вижу смысл в этом термине. И суть его в том, что в мире победившего ООП не стоит забывать об операциях (функциях) и о простоте.

Я уже писал про структурный дизайн - https://t.me/javaKotlinDevOps/400.
Напомню, там была речь о том, что не обязательно заводить кучу объектов и интерфейсов, если процесс простой и раскладывается на три части - подготовка данных, преобразования и запись.
Тогда его можно реализовать в виде сервиса, "чистой" модели и репозитория. К слову: ETL = Extract -> Transform -> Load - о том же.

Так вот - структурное программирование тоже раскладывает любой процесс на три части, только уже на уровне метода: последовательный код, условия и циклы.
Казалось бы - ну да, очевидно это так. Но есть нюанс - тут речь идет о проблеме множественных выходов из метода.
Структурное программирование появилось как ответ на широкое распространение оператора go to и призвано бороться с ним.
Опять вопрос - go to мы победили, тогда зачем эти "предания старины глубокой"?
Нет, не победили. Точнее победили, но не до конца.
Т.к. у нас есть множественный return и throw. А если копнуть глубже, и расширить проблему на любые прыжки по коду - break и continue.
Надо ли от них отказываться, учитывая что парадигма то вроде полезная?
На мой взгляд - нет.
Охранные выражения (guard pattern) - однозначно полезны.
throw для сигнализации об ошибке - тоже ок, они для этого создавались.
С рядом условий:
1) unchecked
2) не надо просто для передачи управления
3) должен быть определен слой\процесс перехвата исключений на уровне сервиса
break и continue - если без них код сложнее читать - тоже допустимы. Прыжки то небольшие, в рамках цикла. Хотя я ими пользуюсь редко, кейсов мало.

Т.е. снова приходим к искусству компромиссов.
Всякий раз, перед написанием такого оператора стоит подумать - нет ли других вариантов? Не усложняется ли он чтение и понимание кода?
Суть структурного программирования - код каждой операции должен просто читаться. Нет "спагетти-коду"!

#book_review #java #code #structure_programming #structure_xxx #principles
Медленная загрузка сервиса - значит Java?)

Есть такая проблема у Java, в частности при использовании Spring Framework. А точнее когда поверх Spring-а накручиваются свои компоненты.
Загружается куча классов, много кода инициализации = много времени. Как с этим борются - см. по тэгу #java_start_boost
Но только ли это проблема Java?

Запускал небольшую AI модельку на Python. Один скрипт. Скрипт стартует более 20 секунд.
Ну, думаю, модель так долго грузится. Померил - нет, модель порядка 3 секунд. Она уже в виде кэша скачана на локальный компьютер, да и не LLM это.
Антивирус? Интерпретатор Python?

Нет, всего лишь библиотека sentence-transformers.
На самом деле конечно не всего лишь. Она под капотом грузит движок для инференса - PyTorch. Плюс другие зависимости.
Но за 20 секунд старта ответственна именно строчка c импортом:

from sentence_transformers import SentenceTransformer


Тут еще видится "выстрелил" высокий уровень абстракции, принятый в Python.
Пишешь пару строк - у тебя все работает. Удобно? Да. Но есть нюансы)

Вывод - высокий уровень может превратится в айсберг. Ты видишь вершину, а под ней еще...

#python #java
Сколько багов в час делает разработчик?

И снова цитата из Совершенного кода:

Исследования, проведенные в Институте
разработки П0 (Software Engincering Institute), показали, что разработчики допускают в среднем
от 1 до З дефектов в час при проектировании и от 5 до 8 дефектов в час при кодировании
(Humphrey, 1997). Ясно, что устранение дефектов — обязательное условие эффективного конструирования.


Хоть ты код не пиши!)))

А если серьёзно - рулит код-ревью и тесты. Именно в таком порядке.

Типичный концер процент нахождения багов в ПО при использовании различных практик из той же книги:

Неформальный обзор дизайна (тех. проекта) 35% 
Формальные инспекции дизайна (тех. проекта) 55% 
Моделирование и прототипирование 65% 
Неформальный обзор кода (code review) 25% 
Самостоятельная проверка код 35%
Формальные инспекции кода 60% 
Юнит-тестирование 30% 
Регрессионное тестирование 25% 
Тестирование новой функции (компонента) 30% 
Интеграционное тестирование 35% 
Системное тестирование 40% 
Бета-тестирование (<10 тестеров) 35% 
Бета-тестирование (>1000 тестеров) 75%

Что бросается в глаза:
1) очень высокая эффективность ревью кода. Что понятно, т.к. получаем shift left и, главное, при код-ревью мы не просто находим баг, но ещё и его причину, в отличие от тестирования. Т.е. не тратим время на отладку.
2) формальные ревью сильно эффективнее неформальных. Немного странно на первый взгляд. Но объяснение есть - формальное = выделено время + есть чек-лист + нужен отчёт. Т.е. его сложно провести "формально" (да, велик и могуч русский язык))) Т.е. правильно было бы назвать его глубокое код-ревью
3) Хорошая эффективность у самостоятельного ревью, а его провести проще всего. Например, при подготовке pull request. И снова понятно почему - своей код ты уже знаешь, главное чтобы между кодированием и ревью прошло время, чтобы сменился фокус.
4) низкая эффективность unit тестов, сравнимая с регрессом и тестированием нового функционала. Это странно. Но если подумать - TDD тогда ещё только появилось, да и сейчас его проникновение оставляет желать лучшего. А без него UT - это в основном регресс. И немного тестирование нового функционала, но методом "прозрачного" ящика.
5) высокие цифры у прототипирования и бета-тестах на большом числе людей. Пр сути они связаны - это проверка гипотезы и реализации. Провести сложно, но эффект похоже есть.

Ну и банальная вещь - ни один метод сам по себе не даёт 100%. Только в комплекте.

#code_review #quality_assurance
Хорошо сказано!
Письмо от CTO, который не взял тебя в команду

Привет! Хочу объяснить, почему мы не сделали тебе оффер.

Сразу скажу, с кодом у тебя всё ок. Вопрос вообще не в этом. У меня ощущение, что ты мало сталкивался с тем, что происходит после коммита/мержа.

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

Сейчас без контейнеров почти никуда, особенно если сервис живой и его часто релизят. И docker — это не “инструмент devops”. Это среда выполнения. Если ты понимаешь docker, ты контролируешь архитектуру, ресурсы сервиса, изоляцию процессов, сетевое поведение, стратегию масштабирования. Sidecar, Service Mesh, сетевые политики, безопасность, observability — всё это превращается в набор страшных слов, которые вроде слышал, но не трогал руками.

Да, ты пишешь код, но вся система шире чем код. Мы используем кубер, масштабируемся автоматически, деплоим десятки раз в день. И нам нужен человек, который понимает, что происходит на уровне контейнера. Не «где-то там в инфраструктуре». Рынок меняется. Стартапам нужны T-shape инженеры, нельзя быть “только про код” — приходится лезть во всё. Не потому что мы экономим на devops. Знание инфры вообще и контейнеров в частности — снижает операционные риски до того, как они станут инцидентами. Когда ты понимаешь, как это работает под капотом, сюрпризов становится сильно меньше. Меньше неопределённости — выше скорость команды. А скорость для стартапа — это не "удобство", а шанс выжить.

Инженер, который понимает контейнеризацию, разговаривает на одном языке с инфраструктурой.
Инженер, который не понимает, бьется лбом в стену между «кодом» и «реальностью».

Мы бы с радостью поработали с тобой. Когда будешь понимать не только код, но и прод. Хочешь расти — закрой этот пробел как можно быстрее. ASAP. Не призываю тебя получать черный пояс по YAML-driven development, но базу знать нужно. Сейчас это база для синьоров, а скоро станет обязательным минимумом для мидлов. А docker — не сложнее, чем любой фреймворк, который ты учишь за несколько недель.

--
Если серьезно, мы из этого сделали отдельный курс — потому что с этим реально у многих пробел. Приходи на "Docker и Kubernetes: основы разработки под облачную инфраструктуру". Старт через неделю, курс ведет Коля Ихалайнен, и он, кстати, и меня несколько лет назад докеру обучил (не шутка).
🔥2👍1
Алексей Рыбак: системный дизайн, хайлоад, разработка с агентами
Письмо от CTO, который не взял тебя в команду Привет! Хочу объяснить, почему мы не сделали тебе оффер. Сразу скажу, с кодом у тебя всё ок. Вопрос вообще не в этом. У меня ощущение, что ты мало сталкивался с тем, что происходит после коммита/мержа. Помнишь…
Что интересно - хотя в конце поста есть реклама, я ее заметил только после пересылки в канал. Т.е. пост зацепил.
Что ещё интересно - судя по реакциям и комментариям в канале автора - проблема актуальна.

Отмазки следующие:
1) в кровавом enterprise меня не пускают к инфре. Видимо не очень то и хотелось)
2) AI придёт и все сгенерит. Видимо вместе с кодом)
3) функции, faas, докера там нет. На самом деле по капотом есть, но главное - я в России работающих faas не видел.
4) ну и очевидное - это все реклама, не нужен мне ваш Docker. Пусть даже и реклама, но написано хорошо, а Docker и k8s - это база!)

#комментарии
Чем заменить сложный if?

В рамках борьбы за читаемость кода, конечно же.
Ответ - табличным методом.
Название, возможно, неизвестное, но многие этим подходом пользовались, не зная как его назвать.

Но ближе к коду)
Вот есть у нас, предположим, такое условие:

int getDaysInMonth(int month) {
if (month == 1) return 31;
else if (month == 2) return 28;
else if (month == 3) return 31;
// ... ещё 9 веток
}


Сложно, т.к. много кода. Но вот так намного проще:

private static final int[] DAYS_IN_MONTH = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

int getDaysInMonth(int month) {
return DAYS_IN_MONTH[month];
}


Собственно таблица - это коллекция, из которой по ключу получаем нужное значение в одну строчку без if.
Хорошо. Но не всегда ключ можно прямо замапить на значение.
Возможно, ключ однозначно преобразуется в требуемый. Тогда получаем табличный доступ с преобразованием:

public int resolveMonth(LocalDate date) {
return date.getMonthValue();
}

int getDaysInMonth(LocalDate date) {
return DAYS_IN_MONTH[resolveMonth(date)];
}


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

@Getter
@RequiredArgsConstructor
enum Zone {
NIGHT ("Ночная", 2.15),
SEMI_PEAK ("Полупиковая", 4.30),
PEAK ("Пиковая", 6.45);

private final String label;
private final double ratePerKwh;
}

private static final int[] HOUR_THRESHOLDS = { 6, 9, 16, 20, 22, 23 };

private static final Zone[] TARIFF_TABLE = { Zone.NIGHT, Zone.PEAK, Zone.SEMI_PEAK, Zone.PEAK, Zone.SEMI_PEAK, Zone.NIGHT };

private static Zone lookupZone(int hour) {
for (int i = 0; i < HOUR_THRESHOLDS.length; i++) {
if (hour <= HOUR_THRESHOLDS[i]) {
return TARIFF_TABLE[i];
}
}
}

public static double getZonePrice(int hour) {
var zone = lookupZone(hour);
return zone.getRatePerKwh();
}



Ну и возвращать можно не только значение, но и лямбду:

private static final Map<String, Consumer<String>> HANDLERS = Map.of(
"ERROR", msg -> System.err.println("[ERROR] " + msg),
"WARNING", msg -> System.out.println("[WARN] " + msg),
"INFO", msg -> System.out.println("[INFO] " + msg),
"DEBUG", msg -> { /* no-op in prod */ }
);

public static void dispatch(String level, String message) {
HANDLERS.getOrDefault(level, m -> System.out.println("[UNKNOWN] " + m))
.accept(message);
}


Важный плюс такого решения - "таблица" легко выносится в файл при необходимости настройки.
Или любой внешний источник.
Таблица может быть мапой, а не только массивом или списком.

P.S. Да, это снова Совершенный код)

#code_review #code
👍2
Сложно не заметить, что последние пару месяцев у меня основная тема блока - "жемчужины" из книги Совершенный код.

Сегодня еще один пост, пост-"мысль вслух".

Самое удивительное в этой книжке - это два факта:
1) на английском она издана в 2004 году
2) она актуальна.

Я пытаюсь вспомнить, что же там устарело и вспоминаю лишь следующее:
а) в Java появились Enum (на тот момент еще нет)
б) Visual Basic умер (там есть примеры в числе прочего на VB)
в) IDE стали сильно лучше, считать открывающие и закрывающие скобки самому уже не надо
г) кажется все языки получили свой аналог xUnit, "костылить" свой подобный фреймворк тоже не надо
д) у нас есть git и история изменений в IDEA, "прихранивать" копию исходников перед изменением не нужно
е) на способы проверки повреждения данных в памяти из-за кривых указателей смотришь с ужасом, но все такие главы подписаны - для C\C++ и при желании их можно пропускать. Да и скорее всего большая часть этой информации актуальна до сих пор.

И пожалуй это все! За 20+ лет.
Даже обсуждение go to и глобальных переменных лишь на первый взгляд кажется не актуальным для Java...

Вот как надо книжки писать!
Вспоминается: "Новые песни сочиняет тот, у кого старые плохие". И это тот редкий случай, когда фраза справедлива)

#book_review #java
💯5
Есть мнение, что разработка - это молодая отрасль. Этим часто объясняется ее несовершенство. Нет чётких законов, парадигмы меняются, всегда приходится идти на компромиссы. Как пример - те же принципы чистого кода нельзя понимать буквально, за что книгу часто критикуют.

Но посмотрим, когда появились некоторые ключевые идеи.

Закон Конвея, упомянутый ранее https://t.me/javaKotlinDevOps/301 - структура кода повторяет структуру организации - 1967 год. Практически 60 лет назад.

Спор о вреде go to (а это значит, что он уже активно использовался) начал Дейкстра в письме Go To Statement Considered Harmful практически тогда же - 1968 году. 58 лет назад. Дискуссии, к слову, тогда велись через письма в журналы. В рамках дискуссии о go to понятие спагетти-код стало общепринятым примерно в 1977 году. 49 лет назад.

В 1970 году сотрудник IBM Эдгар Кодд опубликовал статью «A Relational Model of Data for Large Shared Data Banks», которая стала фундаментом для всех реляционных баз данных. В 1979 году появилась первая коммерческая реляционная СУБД Oracle. 56 и 47 лет соответственно.

Мифический человеко-месяц был написан в 1975 году. Знаменитый закон Брукса: добавление разработчиков ближе к концу проекта лишь увеличивает его сроки. А на русский книга была переведена в 1979 году, соответственно, в СССР. Это 51 и 47 лет назад. Я ещё не родился)

Совершенный код (да, опять), первое издание - 1993 год. 33 года назад.

Java 1.0 (еще без collections, не говоря уже и стримах, generic, enum, JDBC и многого другого) появилась в 1996 - 30 лет в этом году.

В 1999 был сформирован DRY («Don't Repeat Yourself» — «Не повторяй себя») Эндрю Хантом и Дэвидом Томасом в книге The Pragmatic Programmer. 27 лет назад. Принцип KISS сильно моложе, но его снимем с дистанции - он появится в ВВС США.

Принципы SOLID были сформулированы на год позже Робертом Мартином в статье Design Principles and Design Patterns. 26 лет назад.

Тогда же вышла книжка Кента Бека про XP (экстремальное программирование, включая TDD). И в 2001 Agile Manifest, который с ним связан.

В 2001 году появилась IDEA и рефакторинг стал доступен всем. Ну почти всем , Community появилась на 8 лет позже и началось ее победное шествие. 25 лет назад.

И лишь Docker, Kafka, DevOps, Kotlin можно считать относительно новыми - 15-20 лет назад. K8s так вообще чуть больше 10 лет.

Т.е. отрасль все же достигла зрелости.

И тут пришёл AI и хочет ее отменить)
Но это уже другая история)

#it_history
👍41
Чем похожи LLM и blockchain?

И там, и там для генерации следующего элемента используют данные предыдущих. И LLM также не может изменить уже сгенерированные токены, даже если в какой-то момент поняла, что они не верны. Ну и ещё потому что у нас как правило стриминг и пользователь уже их увидел)
Разница конечно есть - в blockchain вся цепочка живёт вечно, а LLM ограничена длиной контекстного окна и возможностью чата/агента хранить историю

И эта особенность является одной из причин галлюцинаций LLM. Т.к. придя к неверному выводу модель опирается на него в дальнейших ответах.


#ai