И снова минутка цитат, подтверждающая мысль, что Стив (Макконнелл) умеет жечь глаголом сердца людей:
P.S. Справедливости ради отдельно обсуждается кейс mixin - классов, не являющихся самостоятельной сущностью, а лишь содержащих какой-то общеупотребительный функционал. Пример: Comparator. То, что в Java реализуется как интерфейс с методами по умолчанию. Этот вариант - ок, но здесь множественное наследование лишь средство подмешать данный функционал в класс
#book_review
Наследование — мощный и... довольно опасный инструмент. В некотором смысле наследование похоже на цепную пилу: при соблюдении мер предосторожности оно может быть невероятно полезным, но при неумелом обращении последствия могут оказаться очень и очень серьезными.
Если наследование — цепная пила, то множественное наследование — это старинная цепная пила с барахлящим мотором, не имеющая предохранителей и не поддерживающая автоматического отключения. Иногда такой инструмент может пригодиться, но большую часть времени его лучше хранить в гараже под замком.
P.S. Справедливости ради отдельно обсуждается кейс mixin - классов, не являющихся самостоятельной сущностью, а лишь содержащих какой-то общеупотребительный функционал. Пример: Comparator. То, что в Java реализуется как интерфейс с методами по умолчанию. Этот вариант - ок, но здесь множественное наследование лишь средство подмешать данный функционал в класс
#book_review
Небольшое дополнение по принципу Liskov к https://t.me/javaKotlinDevOps/182
Краткое содержание предыдущей серии - основной способ нарушить это принцип в Java - сломать семантический контракт метода.
А еще нарушение этого принципа может подсветить плохую абстракцию, выбранную для класса.
Пример: кто-то решил сделать один базовый класс для вклада и кредита. В этом случае метод monthlyPayment() покажет, что это была плохая идея. Т.к. в вызывающем классе нам в зависимости от конкретного потомка - вклада или кредита - придется брать знак минус или плюс для вычислений. Что нарушает принцип Liskov.
P.S. Это тоже Совершенный код)
#solid #book_review
Краткое содержание предыдущей серии - основной способ нарушить это принцип в Java - сломать семантический контракт метода.
А еще нарушение этого принципа может подсветить плохую абстракцию, выбранную для класса.
Пример: кто-то решил сделать один базовый класс для вклада и кредита. В этом случае метод monthlyPayment() покажет, что это была плохая идея. Т.к. в вызывающем классе нам в зависимости от конкретного потомка - вклада или кредита - придется брать знак минус или плюс для вычислений. Что нарушает принцип Liskov.
P.S. Это тоже Совершенный код)
#solid #book_review
Telegram
(java || kotlin) && devOps
Всем привет!
Продолжая тему SOLID. Есть в этой аббревиатуре буква L - принцип Барбары Лисков, который гласит, что все потомки должны соблюдать контракт, объявленный в родителе.
Объясню на пальцах. Есть класс или даже скорее интерфейс А. Есть класс Б, как…
Продолжая тему SOLID. Есть в этой аббревиатуре буква L - принцип Барбары Лисков, который гласит, что все потомки должны соблюдать контракт, объявленный в родителе.
Объясню на пальцах. Есть класс или даже скорее интерфейс А. Есть класс Б, как…
Нужно ли читать чужой код?
Я не про код-ревью, а про процесс разработки.
С одной стороны я всегда топлю за то, что если есть проблемы с внешней библиотекой - не понятно, почему метод возвращает такой результат или ошибку - залезь в ее код, и посмотри.
Но Стив Макконнелл (да, снова Совершенный код) подсвечивает один интересный момент - разницу между синтаксической и семантической инкапсуляцией внутренностей класса.
С синтаксической все просто - объявил по максимуму все private и профит.
А вот семантическая, а точнее ее нарушение - это наши догадки насчет внутреннего устройства чужого класса, влияющие на наш код.
А догадки эти обычно возникают после чтения кода...
Тут еще хорошая аналогия с работой разработчика и тестировщика. Для тестировщика код - это черный ящик. А для разработчика - нет. Или все же да?)
Я для себя решил так. Если найдено неочевидное\неправильное поведение внешней библиотеки, ее автор доступен и готов к сотрудничеству - стоит по советам Стива зайти к нему и попросить поправить.
А если хотя бы одно из этих условий не выполняется - можно и нужно залезть в код. И возможно даже завязаться на его внутренние детали реализации. Да, да, я про костыль.
Но с обязательными условиями:
1) proxy слой
2) с подробным комментарием что делается и почему.
И с заведением техдолга если все же есть возможность поправить эту проблему прямым образом.
#book_review #code
Я не про код-ревью, а про процесс разработки.
С одной стороны я всегда топлю за то, что если есть проблемы с внешней библиотекой - не понятно, почему метод возвращает такой результат или ошибку - залезь в ее код, и посмотри.
Но Стив Макконнелл (да, снова Совершенный код) подсвечивает один интересный момент - разницу между синтаксической и семантической инкапсуляцией внутренностей класса.
С синтаксической все просто - объявил по максимуму все private и профит.
А вот семантическая, а точнее ее нарушение - это наши догадки насчет внутреннего устройства чужого класса, влияющие на наш код.
А догадки эти обычно возникают после чтения кода...
Тут еще хорошая аналогия с работой разработчика и тестировщика. Для тестировщика код - это черный ящик. А для разработчика - нет. Или все же да?)
Я для себя решил так. Если найдено неочевидное\неправильное поведение внешней библиотеки, ее автор доступен и готов к сотрудничеству - стоит по советам Стива зайти к нему и попросить поправить.
А если хотя бы одно из этих условий не выполняется - можно и нужно залезть в код. И возможно даже завязаться на его внутренние детали реализации. Да, да, я про костыль.
Но с обязательными условиями:
1) proxy слой
2) с подробным комментарием что делается и почему.
И с заведением техдолга если все же есть возможность поправить эту проблему прямым образом.
#book_review #code
👍1💯1
assert в боевом коде?
На всякий случай напомню https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html
Пример:
Важной особенностью assert-ов является тот факт, что по умолчанию проверки, выполняемые в assert, выключены.
Для включения нужно передать опцию -ea (-enableassertions) при запуске, просто или с указанием пакета.
И как раз этот факт является одним из двух гвоздей, убивающих данную фичу.
Второй гвоздь - позиционирование assert-ов примерно такое же, как и у отладочных логов.
Это я читаю как в Совершенном коде (да, он спонсор данного поста))), так и в статье выше обсуждается как бы их удалить из кода.
Теперь на отойдем на шаг назад.
Все ошибки во входящих данных можно разделить на две категории - ожидаемые и нет.
Это деление не дискретное, а непрерывное, но в любом случае приводит к вопросу - как проверять данные на неожиданные ошибки? Нужно ли это делать вообще?
С ожидаемыми все понятно - пишем проверку, if и далее возврат кода ошибки\exception\запись в лог.
А неожиданные - что с ними делать?
Самый яркий пример - проблема nullability в Java. Объекты у нас везде, тогда в теории почти в любом методе нужна проверка на null.
Или не нужна, т.к. это сильно увеличивает объем кода и ухудшает читаемость.
Тут как раз предлагается использовать assert - для ошибок, которых при нормальной работе программы быть не должно.
Почему мне это не нравится.
Как раз по двум причинам выше - мы решили проверить данные и вроде закрылись assert-ом.
А потом кто-то убрал -ea в ПРОМе. И вообще говоря такой путь часто рекомендуют - разработка с assert, ПРОМ - без.
Или кто-то удалил assert посчитав его отладочным. Проект то живет долго, люди меняются.
Решением проблемы является созданием слоя валидации (или расположение валидации на сервисном слое). До того, как данные прошли этот слой считаем, что там может быть что угодно.
После - минимум проверок.
Да, часть валидации происходит в API (маппинг API в объекты), но не вся и не всегда только API является поставщиком данных.
Еще предлагаемый кейс для assert - pre-condition, post-condition, инварианты - т.е. реализация контрактов для методов и классов в коде.
Причем часто основная идея здесь не проверка условий, а именно документирование контракта с помощью assert.
И тут снова вступает в силу ненулевая вероятность удаления assert-ов, т.к. в боевом коде они выглядят чужеродно.
Я тут за JavaDoc, т.к. по JavaDoc можно построить отдельный jar с документацией, который может использовать IDE.
С контрактами есть отдельный важный момент: контракт - это часть класса, выносить его в слой валидации плохо.
Но в таком случае лучше его реализовать или обычным кодом или JavaDoc.
Итого остается редкий кейс разработки кусочка монолита, когда нет уверенности, что контракты соблюдаются. Условно разработка чего-то уровня JDK.
Используете ли assert в боевом коде? Для чего?
#book_review #contracts #validations
На всякий случай напомню https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html
Пример:
Connection conn = getConnection();
assert conn != null;
Важной особенностью assert-ов является тот факт, что по умолчанию проверки, выполняемые в assert, выключены.
Для включения нужно передать опцию -ea (-enableassertions) при запуске, просто или с указанием пакета.
И как раз этот факт является одним из двух гвоздей, убивающих данную фичу.
Второй гвоздь - позиционирование assert-ов примерно такое же, как и у отладочных логов.
Это я читаю как в Совершенном коде (да, он спонсор данного поста))), так и в статье выше обсуждается как бы их удалить из кода.
Теперь на отойдем на шаг назад.
Все ошибки во входящих данных можно разделить на две категории - ожидаемые и нет.
Это деление не дискретное, а непрерывное, но в любом случае приводит к вопросу - как проверять данные на неожиданные ошибки? Нужно ли это делать вообще?
С ожидаемыми все понятно - пишем проверку, if и далее возврат кода ошибки\exception\запись в лог.
А неожиданные - что с ними делать?
Самый яркий пример - проблема nullability в Java. Объекты у нас везде, тогда в теории почти в любом методе нужна проверка на null.
Или не нужна, т.к. это сильно увеличивает объем кода и ухудшает читаемость.
Тут как раз предлагается использовать assert - для ошибок, которых при нормальной работе программы быть не должно.
Почему мне это не нравится.
Как раз по двум причинам выше - мы решили проверить данные и вроде закрылись assert-ом.
А потом кто-то убрал -ea в ПРОМе. И вообще говоря такой путь часто рекомендуют - разработка с assert, ПРОМ - без.
Или кто-то удалил assert посчитав его отладочным. Проект то живет долго, люди меняются.
Решением проблемы является созданием слоя валидации (или расположение валидации на сервисном слое). До того, как данные прошли этот слой считаем, что там может быть что угодно.
После - минимум проверок.
Да, часть валидации происходит в API (маппинг API в объекты), но не вся и не всегда только API является поставщиком данных.
Еще предлагаемый кейс для assert - pre-condition, post-condition, инварианты - т.е. реализация контрактов для методов и классов в коде.
Причем часто основная идея здесь не проверка условий, а именно документирование контракта с помощью assert.
И тут снова вступает в силу ненулевая вероятность удаления assert-ов, т.к. в боевом коде они выглядят чужеродно.
Я тут за JavaDoc, т.к. по JavaDoc можно построить отдельный jar с документацией, который может использовать IDE.
С контрактами есть отдельный важный момент: контракт - это часть класса, выносить его в слой валидации плохо.
Но в таком случае лучше его реализовать или обычным кодом или JavaDoc.
Итого остается редкий кейс разработки кусочка монолита, когда нет уверенности, что контракты соблюдаются. Условно разработка чего-то уровня JDK.
Используете ли assert в боевом коде? Для чего?
#book_review #contracts #validations
"Цените легкость чтения кода выше, чем удобство его написания"
Я постоянно топлю за то, что код должен легко читаться.
И цитата из сами понимаете какой книжки как нельзя лучше иллюстрирует данный принцип.
Т.к. если не задумываться об удобстве чтения кода, то мы получаем:
1) дублирование - проще же скопировать, чем выделить новую абстракцию
2) вечный техдолг из-за не проведенного вовремя рефакторинга
3) божественные классы: пусть новый метод Y не вписывается в абстракцию класса Х, но чтобы это заметить - надо выйти за рамки текущей задачи
4) огромные иерархии классов, длинные методы - проще добавить код по аналогии
5) слабое покрытие тестами - код работает, мне он на данный момент абсолютно понятен. Зачем тесты писать - ради тестов?
6) методы и классы с неактуальными названиями
7) все методы в классе являются public - они же вызываются из public метода, да и вдруг кому-то пригодятся
8) спагетти-код
...
и получаем легаси в итоге.
#book_review
Я постоянно топлю за то, что код должен легко читаться.
И цитата из сами понимаете какой книжки как нельзя лучше иллюстрирует данный принцип.
Т.к. если не задумываться об удобстве чтения кода, то мы получаем:
1) дублирование - проще же скопировать, чем выделить новую абстракцию
2) вечный техдолг из-за не проведенного вовремя рефакторинга
3) божественные классы: пусть новый метод Y не вписывается в абстракцию класса Х, но чтобы это заметить - надо выйти за рамки текущей задачи
4) огромные иерархии классов, длинные методы - проще добавить код по аналогии
5) слабое покрытие тестами - код работает, мне он на данный момент абсолютно понятен. Зачем тесты писать - ради тестов?
6) методы и классы с неактуальными названиями
7) все методы в классе являются public - они же вызываются из public метода, да и вдруг кому-то пригодятся
8) спагетти-код
...
и получаем легаси в итоге.
#book_review
Рубрика "Загадки от Стива".
Если объект класса создается в коде всего один раз - стоит заменить такой класс объектом.
О чем речь?
Пример:
у нас есть разные виды карт: Мир, Мир Supreme, Visa Classic, Visa Digital..
Можно завести под каждый тип отдельный подкласс, но в большинстве случае достаточно параметризовать все отличия разных типов карт и просто создавать объекты одного класса.
#book_review #oop
Если объект класса создается в коде всего один раз - стоит заменить такой класс объектом.
О чем речь?
у нас есть разные виды карт: Мир, Мир Supreme, Visa Classic, Visa Digital..
Можно завести под каждый тип отдельный подкласс, но в большинстве случае достаточно параметризовать все отличия разных типов карт и просто создавать объекты одного класса.
#book_review #oop
Ещё один элемент AI-пазла.
Сделаю паузу по книжке.
У нас есть стандарт для получения данных из внешних источников (MCP), потенциальный протокол для запросов в LLM (TOON, хотя тут борьба ещё впереди), паттерн локальной БД (RAG), стандарт памяти для контекста (GAM), навыков агентов (Agent Skills), реестр opensource-моделей (HuggingFace).
Если смотреть на агентов и их локальные возможности, в частности для разработки и смежных активностей, то тут можно найти место для стандарта. Что делает такой агент: читает файлы (код), создаёт и меняет их, запускает команды в консоли и анализирует вывод, возможно запускает браузер. Также — запуск скилов и вызов MCP. Плюс мультиагентность — отдельно агенты для планирования, правки кода, возможно тестирования...
Вот всё это реализует OpenCode https://opencode.ai/. Плюс работа с сессиями, аутентификация у разных провайдеров LLM, выбор модели и даже sharing сессии. Обещают, что, кроме режима sharing, на сервер ничего не шлют.На свой сервер, на сервер LLM конечно же шлют.
Но есть поддержка Ollama для запуска моделей локально. Ещё интересно, что инструмент поддерживает три типа взаимодействия: консоль, web и TUI. TUI — Terminal User Interface. И это реальный консольный UI. Вспоминается MS DOS и Norton Commander. Попробовал — мне понравилось. Удобно.
Минусы:
- для установки и шаринга сессии нужен VPN;
- кроме основной модели есть ещё так называемая small model, в качестве которой у меня OpenCode сам выбрал платную модель OpenRouter (а основную модель я выбрал там же бесплатную) и втихаря сожрал 5 центов за час.
А вообще классная штука, в первую очередь для разработки, но вижу пользу и для анализа данных, тестирования...
P. S. Вот только в стане фреймворков разработки агентов всё ещё разброд и шатание, хотя для Python выделяется пара LangChain/LangGraph.
#ai #llm
Сделаю паузу по книжке.
У нас есть стандарт для получения данных из внешних источников (MCP), потенциальный протокол для запросов в LLM (TOON, хотя тут борьба ещё впереди), паттерн локальной БД (RAG), стандарт памяти для контекста (GAM), навыков агентов (Agent Skills), реестр opensource-моделей (HuggingFace).
Если смотреть на агентов и их локальные возможности, в частности для разработки и смежных активностей, то тут можно найти место для стандарта. Что делает такой агент: читает файлы (код), создаёт и меняет их, запускает команды в консоли и анализирует вывод, возможно запускает браузер. Также — запуск скилов и вызов MCP. Плюс мультиагентность — отдельно агенты для планирования, правки кода, возможно тестирования...
Вот всё это реализует OpenCode https://opencode.ai/. Плюс работа с сессиями, аутентификация у разных провайдеров LLM, выбор модели и даже sharing сессии. Обещают, что, кроме режима sharing, на сервер ничего не шлют.На свой сервер, на сервер LLM конечно же шлют.
Но есть поддержка Ollama для запуска моделей локально. Ещё интересно, что инструмент поддерживает три типа взаимодействия: консоль, web и TUI. TUI — Terminal User Interface. И это реальный консольный UI. Вспоминается MS DOS и Norton Commander. Попробовал — мне понравилось. Удобно.
Минусы:
- для установки и шаринга сессии нужен VPN;
- кроме основной модели есть ещё так называемая small model, в качестве которой у меня OpenCode сам выбрал платную модель OpenRouter (а основную модель я выбрал там же бесплатную) и втихаря сожрал 5 центов за час.
А вообще классная штука, в первую очередь для разработки, но вижу пользу и для анализа данных, тестирования...
P. S. Вот только в стане фреймворков разработки агентов всё ещё разброд и шатание, хотя для Python выделяется пара LangChain/LangGraph.
#ai #llm
opencode.ai
OpenCode | The open source AI coding agent
OpenCode - The open source coding agent.
Еще одна загадка "от Стива":
Цитата:
"В одном исследовании 450 методов было обнаружено, что дефекты отсутствовали у 50% методов, обладающих высокой связностью,
и только в 18% методов с низкой связностью (Card, Church, and Agresti, 1986).
Другое исследование методов (это просто совпадение, хотя и весьма необычное) показало,
что в сравнении с методами, имеющими самое низкое отношение «сопряжение/связность» (coupling-to-cohesion),
методы с максимальным отношением «сопряжение/связность» содержали в 7 раз больше ошибок,
а исправление этих методов было в 20 раз более дорогим (Selby and Basili, 1991)."
Речь про то, что в методах с высокой связностью кода - single responsibility по сути - меньше ошибок.
Почему?
Мой ответ:
низкая связность может иметь одну из двух причин:
1) плохое проектирование = больше ошибок
2) плохо проведенный\не проведенный вовремя рефакторинг, когда в метод добавили новый функционал, "гвоздями прибив" его к существующему без нормального тестирования
Ну и банально - больше функционала в методе = больше метод = больше вероятность ошибки. Но тут зависимость не линейная, при нормальном проектировании.
#book_review #quest
Цитата:
"В одном исследовании 450 методов было обнаружено, что дефекты отсутствовали у 50% методов, обладающих высокой связностью,
и только в 18% методов с низкой связностью (Card, Church, and Agresti, 1986).
Другое исследование методов (это просто совпадение, хотя и весьма необычное) показало,
что в сравнении с методами, имеющими самое низкое отношение «сопряжение/связность» (coupling-to-cohesion),
методы с максимальным отношением «сопряжение/связность» содержали в 7 раз больше ошибок,
а исправление этих методов было в 20 раз более дорогим (Selby and Basili, 1991)."
Речь про то, что в методах с высокой связностью кода - single responsibility по сути - меньше ошибок.
Почему?
низкая связность может иметь одну из двух причин:
1) плохое проектирование = больше ошибок
2) плохо проведенный\не проведенный вовремя рефакторинг, когда в метод добавили новый функционал, "гвоздями прибив" его к существующему без нормального тестирования
Ну и банально - больше функционала в методе = больше метод = больше вероятность ошибки. Но тут зависимость не линейная, при нормальном проектировании.
#book_review #quest
🔥1
И еще один вопрос от Стива, попроще:
"Используйте утвердительные имена булевых переменных"
Почему?
Ответ:
чтобы избежать вот такого кода:
if ((!notFound) && ... )
затрудняющего чтение.
А кстати в Python все еще хуже:
if (not notFound) and ...:
#book_review
"Используйте утвердительные имена булевых переменных"
Почему?
чтобы избежать вот такого кода:
if ((!notFound) && ... )
затрудняющего чтение.
А кстати в Python все еще хуже:
if (not notFound) and ...:
#book_review
Долговременное хранение контекста 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 постоянная часть:
Текучка:
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
Я уже писал про 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
Telegram
(java || kotlin) && devOps
Заметка про стандартизацию AI.
Вот и кандидат на стандарт для хранения контекста диалога подъехал https://habr.com/ru/companies/bothub/news/972054 Не факт, что именно эта технология, см. Китай и Гонконг, но сам принцип вполне может стать стандартом.
P.S.…
Вот и кандидат на стандарт для хранения контекста диалога подъехал https://habr.com/ru/companies/bothub/news/972054 Не факт, что именно эта технология, см. Китай и Гонконг, но сам принцип вполне может стать стандартом.
P.S.…
Что не докрутили в record-ах?
Для начала кратко о том, что докрутили:
1) компактное объявление, типовые методы из коробки
2) иммутабельность
3) минимально необходимая расширяемость - дополнительные конструкторы, интерфейсы, обычные методы, статические поля, методы и внутренние классы.
Получаем аналог data class в Kotlin. Или структур из древних языков типа C.
Но с небольшим отличием - не хватает метода копирования "из коробки".
Почему так сделали? Есть же Cloneable интерфейс. И его даже можно добавить в record руками.
Думаю, причина в иммутабельности record и скажем так скомпрометированности clone().
Для иммутабельной сущности нет смысла в полной копии - можно использовать текущий объект.
С clone() основная проблема в том, что "из коробки" он делает поверхностную копию, но при переопределении может дать полную. И вообще полную копию автоматически сделать сложно. А это приводит к путанице - метод один, а поведение может отличаться.
В итоге приходим к тому, что иметь возможность быстро сделать поверхностную копию записи с изменением пары полей было бы неплохо. И такой JEP есть https://openjdk.org/jeps/468
Надеюсь внедрят.
#java #иммутабельность
Для начала кратко о том, что докрутили:
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
В разработке есть проблема передачи знаний.
Есть разные способы - обучение, книги, онлайн контент, конференции, поиск, общение с коллегами и вот теперь 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
Я про разработку.
И если вы сейчас думаете, что я расскажу, как многочисленные согласования:
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
В 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
Сегодня про кейс из жизни - как работает 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
Что такое совершенный код?
Читая одноименную книгу (да, я снова о ней), у меня возникла уверенность в том, что автор практикующий и очень педантичный, уделяющий внимание деталям разработчик.
В принципе, для этого книгу можно не читать, достаточно оценить ее размер)
Но тем не менее.
По каждой казалось бы простой теме: переменные, методы, классы, условные операторы, циклы... - есть есть что сказать, и сказать много.
И в большинстве случаев - по делу.
Пример.
Я всегда топлю за читаемость кода.
Но никогда не задумывался о расположении локальных переменных.
А Стив задумывался)
Он вводит понятия частоты использования и времени жизни переменной.
Вот это неправильно, т.к.
1) не нужно разделять объявление, инициализацию и использование переменной, т.е. переменную для цикла надо объявлять прямо перед ним.
2) блоки по поиску файла и заметки нужно четко разделить, чтобы было легко рефакторить и при необходимость разделить на методы.
3) еще один риск - если между объявлением переменной и ее использованием много кода - возрастает вероятность "случайно" ее поменять так, что сломается использующий переменную код.
Т.е. частота использования (число строк с переменной/число строк от первого до последнего появления переменной) должна быть высокой, время жизни - коротким.
И это увеличивает читаемость. Вроде простая штука, но отдельно о ней не задумываешь.
#book_review
Читая одноименную книгу (да, я снова о ней), у меня возникла уверенность в том, что автор практикующий и очень педантичный, уделяющий внимание деталям разработчик.
В принципе, для этого книгу можно не читать, достаточно оценить ее размер)
Но тем не менее.
По каждой казалось бы простой теме: переменные, методы, классы, условные операторы, циклы... - есть есть что сказать, и сказать много.
И в большинстве случаев - по делу.
Пример.
Я всегда топлю за читаемость кода.
Но никогда не задумывался о расположении локальных переменных.
А Стив задумывался)
Он вводит понятия частоты использования и времени жизни переменной.
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
Почему важное и зачем вообще это нужно?
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
OpenCode
Tools
Manage the tools an LLM can use.
И чтобы "зафиналить" тему с множественными отрицаниями в условиях пару цитат из книжки)))
P.S. Кто-нибудь пробовал вычислить вторую фразу?)
#book_review
Я не не нетупой.
Гомер Симпсон
(Homer Simpson)
Не немногие люди не имеют проблем с непониманием некоротких неположительных фраз, т. е. большинство людей имеют трудности с пониманием большого количества отрицаний.
P.S. Кто-нибудь пробовал вычислить вторую фразу?)
#book_review
🔥1
Каким должен быть AI autocomplete?
Для начала - почему я задался этим вопросом?
Потому что перед глазами есть autocomplete, который слишком часто выдает ерунду, и у каждого, кто им пользовался, появляется желание его отключить)
Необходимые условия:
1) у агента autocomplete должен быть свой RAG, где он хранит контекст всего проекта. В идеале - с подключением внешних папок с кодом или других репозиториев git.
2) агент должен быть быстрым, т.е. это отдельная модель с небольшим числом параметров. Т.к. скорость модели прямо пропорциональна числу ее параметров.
3) агент должен оперативно подхватывать изменения - т.е. если я изменил класс А, перешел в класс Б и хочу вызвать новый код класса А - он сразу же должен быть доступен.
Достаточное условие: агент должен уметь молчать) Т.е. должен быть хороший механизм расчета вероятности подсказки, и при низкой вероятности агент просто не должен ничего предлагать.
Я бы даже не выносил это в параметры. Если этого не сделать - получается резко негативный клиентский опыт.
Приведу хорошие примеры, когда autocomplete должен работать:
1)
2)
3)
Когда лучше ничего не предлагать - при добавлении новых полей и методов в класс. Или на первой строке нового метода, если по его названию не понятно, что он будет делать.
Итого - иногда лучше молчать, чем говорить)
#ai #ide #ai_agents
Для начала - почему я задался этим вопросом?
Потому что перед глазами есть 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
Два подхода к написанию функций (методов).
Первый - у метода должна быть одна точка выхода (в общем случае - как можно меньше точек выхода). А точка входа по умолчанию одна. Потому что это улучшает читаемость.
Второй - если у метода есть предусловия, то их нужно проверять с помощью охранных выражений в начале метода. Т.к. это уменьшает вложенность и опять же улучшает читаемость)
Т.е
vs
Второй вариант может быть длиннее первого, если всегда "как завещано" использовать фигурные скобки. Но в данном случае видится, что можно их убрать для улучшения читаемости.
И тогда мы получаем более понятный из-за линейной структуры.
В Совершенном коде автор тоже за второй вариант. Присоединяюсь и считаю, что второй вариант - это база.
Возможно есть кейсы, когда он не применим, но я пока придумать не могу.
Даже на 2 условиях. Даже если недостаточно простого return.
#book_review #code
Первый - у метода должна быть одна точка выхода (в общем случае - как можно меньше точек выхода). А точка входа по умолчанию одна. Потому что это улучшает читаемость.
Второй - если у метода есть предусловия, то их нужно проверять с помощью охранных выражений в начале метода. Т.к. это уменьшает вложенность и опять же улучшает читаемость)
Т.е
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
Снова минутка цитат на канале:
Лично я вижу смысл в этом термине. И суть его в том, что в мире победившего ООП не стоит забывать об операциях (функциях) и о простоте.
Я уже писал про структурный дизайн - 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
«Термин „структурное программирование“ был введён в 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
Telegram
(java || kotlin) && devOps
Всем привет!
Я уже писал про то, что не люблю код, в котором интерфейсы делаются ради интерфейсов. Самый яркий антипаттерн: интерфейс с единственной реализацией, лежащей рядом. Подозреваю, одной из причин такого проектирования является принцип Dependency…
Я уже писал про то, что не люблю код, в котором интерфейсы делаются ради интерфейсов. Самый яркий антипаттерн: интерфейс с единственной реализацией, лежащей рядом. Подозреваю, одной из причин такого проектирования является принцип Dependency…