Трендовые github проекты в нашем телеграм канале. Подпишись → Аспекты как спецификации для генерации кода
Аспектно-ориентированное программирование долго выглядело как правильная идея с неудобной реализацией. В реальных проектах почти всегда есть логирование, авторизация, трассировка, ретраи, валидация, кэширование и обработка ошибок. Эти правила проходят поперёк доменной логики: один и тот же паттерн приходится размазывать по контроллерам, сервисам, очередям и фоновых задачах. Классический ответ АОП — вынести такие сквозные требования в аспекты и «вплести» их в код автоматически.
Проблема в том, что традиционный механизм точек соединения и advice часто требовал слишком много неявной магии. Разработчик читает метод, но фактическое поведение появляется где-то ещё: в прокси, аннотациях, байткоде или runtime-перехватчиках. Это усложняет отладку и ревью, особенно в backend-системах, где ошибка в авторизации или транзакции быстро становится инцидентом.
Новая волна интереса к АОП связана с тем, что роль «плетельщика» можно переосмыслить. Вместо сложного runtime-механизма аспекты описываются отдельными документами, а LLM использует их как правила генерации и изменения кода. На выходе получается обычный, читаемый код без скрытого слоя исполнения.
Что меняется по сравнению с классическим АОП
В классическом подходе аспект обычно привязывается к точкам соединения: вызовам методов, созданию объектов, обработчикам исключений или аннотациям. Это мощно, но требует точной модели выполнения. Любое изменение сигнатуры или структуры проекта может сломать привязку, а смысл программы оказывается распределён между несколькими уровнями.
В варианте с LLM аспект становится спецификацией. Например, можно завести документ security.md, где описано, что все мутации пользовательских данных должны проверять владельца ресурса, фиксировать причину отказа и не раскрывать детали существования чужих объектов. Отдельно может быть observability.md с правилами логирования, метрик и trace-id. Ещё один документ — performance.md, где перечислены ограничения на N+1 запросы, размер батчей и таймауты.
Модель не добавляет невидимый advice в runtime. Она читает целевой код и документы-аспекты, после чего генерирует явную реализацию: проверки доступа, структурированные логи, метрики, обработку ошибок. Это ближе к code generation и guided refactoring, чем к классическому AOP-фреймворку.
Почему это полезно для backend и homelab-проектов
В небольших self-hosted и backend-проектах часто нет отдельной платформенной команды, которая построит идеальные middleware, политики и шаблоны сервисов. При этом сквозные требования никуда не исчезают. Даже домашний сервис для медиа, мониторинга или автоматизации умного дома нуждается в аккуратной обработке секретов, понятных логах и защите API.
Документы-аспекты помогают сохранить правила рядом с проектом и сделать их проверяемой частью разработки. Вместо того чтобы держать требования в голове, можно описать их в репозитории:
- какие операции требуют аутентификации;
- какие поля нельзя логировать;
- какие ошибки возвращаются пользователю, а какие остаются только в логах;
- где нужны таймауты, ретраи и circuit breaker;
- какие метрики должны появляться у HTTP-эндпоинтов и фоновых задач.
LLM в таком сценарии работает как ассистент, который применяет эти правила к конкретным файлам. Разработчик получает diff и ревьюит обычный код. Это важное отличие: ответственность остаётся у человека и CI, а не у чёрного ящика в продакшене.
Пример рабочего процесса
Практичный процесс можно построить без сложной инфраструктуры. В репозитории создаётся каталог aspects/:
aspects/
security.md
observability.md
reliability.md
style.md
В security.md фиксируются правила доступа и приватности. В observability.md — формат логов, обязательные поля и требования к метрикам. В reliability.md — поведение при сбоях внешних сервисов, таймауты, идемпотентность и retry policy. В style.md — соглашения по структуре кода, ошибкам и тестам.
Когда появляется новый endpoint, задача для ассистента формулируется не как «добавь логирование», а как «реализуй обработчик согласно security.md, observability.md и reliability.md». После генерации стоит попросить модель отдельно показать, какие правила были применены и где. Это превращает аспект из абстрактного документа в чек-лист для ревью.
Для существующего кода подход похож на миграцию. Сначала выбирается небольшой модуль, затем модель применяет один аспект, например наблюдаемость. После ревью и тестов можно переходить к безопасности или отказоустойчивости. Не стоит просить переписать весь сервис за один проход: чем меньше diff, тем проще заметить ошибку.
Где находятся риски
Главный риск — ложное чувство формальной гарантии. LLM может убедительно применить правило не полностью, неправильно понять границы домена или пропустить нестандартный путь выполнения. Поэтому аспекты не заменяют тесты, статический анализ и ручное ревью.
Второй риск — конфликт правил. Безопасность может требовать не раскрывать детали ошибки, а наблюдаемость — логировать максимум контекста. Производительность может подталкивать к кэшированию, а актуальность данных — запрещать его для части операций. Такие конфликты нужно явно описывать в самих аспектах: какие правила приоритетнее и какие компромиссы допустимы.
Третий риск — деградация документов. Если аспект однажды написали и забыли, он быстро перестанет соответствовать архитектуре. Хорошая практика — ревьюить изменения в aspects/ так же внимательно, как изменения в коде, и добавлять проверки в CI: линтеры, unit-тесты, интеграционные тесты на критические сценарии.
Как сделать подход менее хрупким
Аспекты должны быть конкретными. Формулировка «пиши безопасный код» бесполезна. Лучше перечислить проверяемые правила: «все admin-операции требуют роль admin», «секреты и токены не попадают в логи», «ошибки доступа возвращают одинаковый ответ без подтверждения существования ресурса».
Также полезно отделять обязательные правила от рекомендаций. В документе можно использовать секции MUST, SHOULD и MAY. Это снижает двусмысленность и помогает модели не превращать каждую рекомендацию в жёсткое требование.
Ещё один приём — просить ассистента генерировать не только код, но и тесты на применённые аспекты. Если добавляется авторизация, рядом должны появиться тесты на доступ владельца, чужого пользователя и анонимного запроса. Если добавляется retry policy, нужны тесты на таймаут и исчерпание попыток.
Итог
Идея аспектов остаётся актуальной, потому что сквозные требования никуда не исчезли. Изменился способ их применения. Вместо неявного runtime-плетения можно использовать LLM как инструмент генерации явного кода по набору проектных спецификаций.
Для разработчика это не магическая замена архитектуры, а способ дисциплинировать повторяющиеся требования. Если аспекты конкретны, лежат в репозитории, проходят ревью и подкреплены тестами, они помогают удерживать безопасность, наблюдаемость и отказоустойчивость на одном уровне во всём проекте. А главное — результат остаётся обычным кодом, который можно прочитать, запустить и проверить без специальной магии в продакшене.