
Всем привет! Меня зовут Виктор, и более трёх лет я работаю с языковыми моделями – от проприетарных решений вроде ChatGPT до open-source систем, которые можно разворачивать локально и встраивать в собственные продукты. Я застал времена жутких галлюцинаций GPT-3.5 и ждал обещанного GPT-5 – того самого почти AGI, которое, казалось, вот-вот появится.
За это время многое изменилось: появились десятки моделей, которые можно запустить даже на слабом ноутбуке, и мощные коммерческие системы, способные понимать с полуслова.
Но одно остаётся неизменным – AGI так и не случилось.
Если два года назад нам уверенно обещали, что «всё уже близко», то теперь даже крупнейшие компании признают: ждать придётся долго.
И это не плохо. Просто разработчикам, приходится учиться работать с тем, что есть, и извлекать максимум пользы из нынешних LLM.
За годы работы с моделями я выработал ряд принципов, которые помогают получать стабильные и читаемые результаты.
На первый взгляд – ничего нового: это те же инженерные практики, которые мы применяем в обычном программировании.
Но в контексте LLM они начинают работать совсем иначе.
В этой статье я сосредотачиваюсь на задаче генерации кода – самой близкой и понятной разработчику.
Эта область особенно показательна: в ней сильнее всего проявляются ограничения моделей – трудности с контекстом, архитектурой и интеграцией кода.
Возможно, часть этих принципов уже частично реализована в инструментах вроде Cursor или GitHub Copilot, но большинство разработчиков по-прежнему работают напрямую – через GPT, DeepSeek, Qwen или локальные модели.
А с учётом ограничений по оплате и доступу к проприетарным сервисам такой способ взаимодействия, скорее всего, останется основным.
Поэтому все наблюдения, описанные ниже, относятся именно к «ручному» взаимодействию с LLM – когда разработчик сам строит диалог и управляет процессом генерации.
Принцип 1. Сначала архитектура системы – потом код
Контекст, в который будет встроен генерируемый код, критически важен для модели. Без него редко получается что-то пригодное к интеграции. Здесь есть два пути: описать уже существующую архитектуру или спроектировать новую, с чётко выделенными и изолированными модулями.
Перед тем как просить модель реализовать функционал, полезно сформулировать структуру: какие входные и выходные данные предполагаются, из каких элементов состоит модуль, как они связаны между собой. Не стоит сразу требовать «сгенерировать код» – сначала обсудите с моделью саму структуру, даже если вы уже её придумали. Это даст ей контекст для дальнейшей генерации и поможет вам взглянуть на архитектуру под другим углом.
Без такой подготовки модель часто начинает додумывать детали: создаёт классы, интерфейсы и зависимости, которых в проекте никогда не было. В итоге код выглядит логично, но не стыкуется с остальной системой. Поэтому стоит заранее задать архитектурные границы, в которых модель будет работать.
В научных работах этот эффект исследуется довольно активно. Подтверждено, что предоставление LLM информации об окружающей кодовой базе и её структуре заметно повышает качество результатов. Многие современные подходы основаны на поиске и систематизации нужных фрагментов из репозитория, а также на автоматическом подмешивании зависимых сигнатур в промпт [1–3].
Принцип 2. Спроектируй код – прежде чем генерировать
Этот принцип близок к первому, но фокусируется на локальном уровне. Если первый касается архитектуры всей системы или крупного блока, то здесь речь идёт о детальном планировании именно того фрагмента, который вы собираетесь сгенерировать.
Если возникает желание попросить модель «сделать всё сразу», лучше не торопиться. Двигайтесь поэтапно – так проще контролировать качество, тестировать и корректировать код. К тому же модели гораздо сложнее испортить небольшой метод, чем целый модуль.
Хороший приём – сначала попросить модель написать короткий план, псевдокод или набор сигнатур функций, а уже потом переходить к их реализации. Такой способ снижает вероятность логических ошибок и делает результат более предсказуемым.
В научной литературе подход «plan-then-generate» считается одним из наиболее перспективных. Он особенно полезен в ситуациях, когда исходные требования сформулированы не полностью – что, по сути, и является нормой при общении с LLM [4–6].
Принцип 3. Придерживайся чистой архитектуры
Самое трудное для LLM – следить за эволюцией собственного кода при работе с большими контекстами. Когда в одном методе или классе сосредоточено слишком много задач, модель теряет нить. Она начинает смешивать уровни абстракции, добавляет ненужные зависимости и в итоге создаёт код, который трудно читать, тестировать и поддерживать.
Чтобы этого избежать, достаточно соблюдать несколько простых правил.
Во-первых, избегайте всезнающих классов: каждый должен знать только о своём функционале, а всё остальное передаваться через инъекции зависимостей.
Во-вторых, один класс – одна ответственность. Остальные функции стоит вынести в отдельные утилиты или сервисы.
В-третьих, зависимости должны направляться внутрь – детали реализации внешних слоёв (интерфейс, база данных) не должны определять поведение бизнес-логики.
Если этого не делать, модель склонна объединять всё подряд. В результате рождаются классы вроде DatabaseLoggerWithUIHandler, которые и пишут в базу, и обновляют интерфейс. На вид всё выглядит “умно”, но архитектурно это катастрофа.
Мне ближе всего принципы чистой и гексагональной архитектуры – «порты и адаптеры». Но, по сути, это неважно: можно использовать любую архитектуру, главное, чтобы модель придерживалась той же дисциплины. Всё, что обеспечивает структуру и управляемость кодовой базы, помогает LLM писать лучше. Эмпирические исследования подтверждают: нарушение архитектурной чистоты ведёт к деградации кода и накоплению технического долга [7, 8].
Принцип 4. Генерируй пошагово
Даже при наличии общей структуры не стоит просить модель сразу сгенерировать весь модуль. На этом этапе у вас уже есть описание архитектуры и понимание того, за что отвечает каждый класс. Этого достаточно, чтобы двигаться постепенно – генерируя код по методу, а не целиком.
Для LLM избыточный контекст – не благо, а источник ошибок. Чем больше кода подаётся на вход, тем выше риск, что модель начнёт путаться или повторяться. Короткие шаги позволяют контролировать процесс, вовремя уточнять требования и тестировать код сразу после генерации.
Это согласуется с тем, как обучались модели: они лучше справляются с локальными задачами, чем с крупными структурами. Исследования вроде ClassEval [9] показывают, что при переходе от функций к классам точность генерации падает в разы, а пошаговый подход помогает это компенсировать. Разработчики фреймворка FunCoder [10] подтверждают: деление задачи на функции и сборка решения по частям повышает стабильность и качество.
Принцип 5. Размер кода
Хорошо написанный код – это не только читаемость, но и управляемость. В классическом программировании давно существует понятие «хорошего тона»: избегать слишком длинных методов и перегруженных классов. Сегодня это уже не просто рекомендация, а есть статистика и эмпирические доказательства.
В исследовании [11] на выборке из 784 тысяч методов показано, что оптимальная длина метода составляет не более 24 строк. Разделение больших методов на меньшие существенно снижает затраты на поддержку. Другое исследование [12], охватившее 30 проектов и 395 релизов, показало: “всезнающие классы” и длинные методы чаще содержат ошибки и сложнее в сопровождении.
LLM идеально ложатся на эту идею, потому что обучались именно на коротких и локальных примерах кода. Для себя я сделал простое правило: класс не должен содержать больше десяти методов, а метод – больше двадцати строк. Если код разрастается, значит, пора пересмотреть архитектуру и вынести избыточное в отдельные компоненты.
Принцип 6. Пиши тесты как требования к генерации
Предыдущие принципы касались контекста и структуры, но сами требования к коду часто формулируются слишком неформально: «сделай функцию, которая…», «учти крайний случай…». Модель может понять это как угодно. Гораздо надёжнее использовать тесты.
На практике это значит следующее: сначала просите модель сгенерировать юнит-тест, описывающий поведение, а затем уже код, проходящий этот тест. Такой подход не только формализует задачу, но и резко снижает вероятность логических ошибок.
Исследования подтверждают эффективность TDD в связке с LLM. Добавление тестов до генерации повышает точность выполнения задач на 15–18% для GPT-4, а у открытых моделей вроде Llama-3 – до 38% [13]. Фактически это позволяет сократить разрыв между проприетарными и open-source-моделями только за счёт правильной методики.
Принцип 7. Проси комментарии и докстринги
Модель может генерировать синтаксически правильный код, который логически не решает задачу. Чтобы избежать этого, стоит добавить в процесс генерации дополнительный уровень размышления – попросить LLM сопровождать код короткими объяснениями.
Когда модель вынуждена пояснять свои действия, она фактически использует встроенную цепочку рассуждений. Вероятность ошибок снижается, а сам код становится понятнее. В моей практике это один из самых простых и эффективных способов стабилизировать результат: если перед генерацией попросить модель описать, почему следующие строки будут такими, итоговый код получается аккуратнее и лучше отражает замысел.
Исследование [14] подтверждает этот эффект: генерация комментариев повышает pass@k на бенчмарках MBPP и HumanEval, а в некоторых случаях даже превосходит традиционный подход chain-of-thought.
Без чёткого плана генерации почти всегда получается не то, что хотелось. Подход “сгенерируй что-нибудь рабочее” приводит к усложнению – не потому, что LLM «глупая», а потому, что она не знает ваших намерений. Механизмы уточнений и активной обратной связи только начинают появляться, поэтому пока самый надёжный способ – самому быть архитектором, аналитиком и заказчиком, а модель использовать как исполнителя, которому вы чётко ставите задачу.
Если тема кажется вам интересной, я продолжаю разбирать подобные вещи у себя в Telegram короткими постами, экспериментами и примерами из практики: «надо разобраться | заставляем LLM работать».
Изначально я планировал собрать под каждый принцип отдельный список ссылок, но оказалось, что все они тесно связаны между собой. На практике они образуют единый пайплайн – от проектирования до тестирования и документирования. Поэтому ниже – общая библиография, на которую опираются все принципы.
Self-planning Code Generation with Large Language Models – самопланирование генерации кода с использованием LLM
Sketch Then Generate: Providing Incremental User Feedback and Guiding LLM Code Generation through Language-Oriented Code Sketches – об эффективности подхода "сначала план - затем генерация"
Planning In Natural Language Improves LLM Search For Code Generation – об эффективности генерации множества планов и выбора из них оптимального для кодогенерации.
????????: Repository-level Coding using LLMs and Planning – о кодировании с LLM на уровне репозитория, в том числе миграция кода на другие языки
ChatCoder: Human-in-loop Refine Requirement Improves LLMs’ Code Generation – о планировании требований к коду в цикле "человек-LLM"
Enhancing Repository-Level Code Generation with Integrated Contextual Information – о интеграции в промпт информации из больших репозиториев о связях на уровне типов/классов/интерфейсов/структур, показывающие, какие типы упоминаются и требуются другим типом или функцией.
Design First, Code Later: The AI Era Mantra – о различных архитектурах в виде краткой справки.
Evolution patterns of software-architecture smells: An empirical study of intra- and inter-version smells – это не про LLM, но принцип тот же. Работа о том, что нарушение принципов архитектурной чистоты ведет к деградации кодовой базы продукта, снижению качества самого продукта и наращиванию технического долга. Ровно то, что произойдет, если не требовать от LLM придерживаться принципов вашего проекта при генерации кода.
Evaluating Large Language Models in Class-Level Code Generation – о разнице в эффективности между генерацией методов и классов, насколько падает производительность и метрики
Divide-and-Conquer Meets Consensus: Unleashing the Power of Functions in Code Generation – фреймворк помогает модели вводить вспомогательные функции и писать решение по частям, а затем собирать итог.
An Empirical Study on Maintainable Method Size in Java – исследования о том, что проектирование методов не более чем в 24 строки помогают снизить будущие расходы на сопровождение кода.
On the diffuseness and the impact on maintainability of code smells: a large scale empirical investigation – масштабное исследование по 13 различным "запахам" кода и их влиянии на качество кода и последующее его обслуживание.
Test-Driven Development for Code Generation – о влиянии практики TDD на генерацию кода.
Comments as Natural Logic Pivots: Improve Code Generation via Comment Perspective – о влиянии генерации встроенных комментариев на эффективность генерации кода.
Комментарии (4)

kuza2000
06.11.2025 11:00Ждать от LLM появление AGI - это примерно так же, как пытаться собрать автомобиль, имея только коробку передач)
А статью спасибо! Вообще, я баловался генерацией кода в обычных LMM. Что-то получалась, но результат был так себе. Читал новости "у нас 70% кода пишет LLM" и не понимал, как такое может быть. А потом поставил Cursor и офигел от результата :) Там действительно решены проблемы контекста и многое другое. Теперь и у меня стало так) Помощь огромная.

victor_shev89 Автор
06.11.2025 11:00Да, cursor - это мощный инструмент, но имеет свои ограничения, из за которых многие или не могут или не готовы им пользоваться на постоянной основе. Как раз и хотелось поисследовать суть этой мощи. По хорошему, если заморочиться, можно подручными open source средствами собрать свой "домашний курсор".))
qnquro
использовал LLM для ускорения написания кода и методом проб и ошибок сам пришёл к таким же принципам. главная ошибка новичков, которые используют LLM для генерации кода, заключается в том, что они могут просить генерировать то в чём сами не разбираются и нормального ТЗ дать не могут, что приводит к ошибкам. и тут даже эти 7 принципов не помогут)
victor_shev89 Автор
Полностью согласен! Проблема вайбкодинга в том, что он завирусился как «кодинг для домохозяек» - мол, можно писать целыми приложениями, ничего не зная о программировании.
Но на практике наоборот. Вайбкодинг тяжелее обычного кодинга - хотя бы морально.
В какой-то момент всё равно придётся залезть в код и разобраться, чтобы оно действительно заработало.