Генерация кода
Программирование с ИИ
Проблема: Написание кода занимает много времени, легко забыть синтаксис или допустить ошибки. Как ИИ может помочь писать код быстрее и надёжнее?
Решение: Твой ИИ-джуниор
Генерация кода использует LLM для написания, дополнения и трансформации кода на основе описаний на естественном языке. Модель обучали на миллиардах строк открытого исходного кода, поэтому она усвоила статистические закономерности того, как устроены реальные программы: типичные сигнатуры API, идиоматичные циклы, соглашения по обработке ошибок и то, как обычно выглядят тесты. Когда ты описываешь, что хочешь, модель предсказывает наиболее вероятную последовательность токенов под это описание — она не рассуждает о твоей программе так, как компилятор, она сопоставляет паттерны из всего, что видела. Именно это объясняет и её скорость, и то, почему она бывает уверенно неправа.
Как это работает на практике
Качество результата почти полностью зависит от того, сколько контекста ты дал. Голый однострочный запрос оставляет модель гадать о языке, библиотеках, стиле именования и граничных случаях. Две дешёвые техники решают большую часть этих проблем. С chain-of-thought промптингом ты просишь модель сначала описать подход, а только потом писать код — так ошибочные допущения всплывают рано. С few-shot примерами ты вставляешь одну-две готовых функции из своего проекта, и вывод подстраивается под твои соглашения вместо обобщённого стиля со StackOverflow. Современные кодовые агенты идут дальше: читают соседние файлы, запускают тесты, видят падение и правят код в цикле — поэтому агент с доступом к проекту обычно сильнее одиночного ответа в чате.
Компромиссы и разобранный пример
Опасность в том, что сгенерированный код выглядит правильным. Модель выдаёт правдоподобный синтаксис даже там, где логика тонко сломана, и может выдумать несуществующие функции библиотек — это разновидность галлюцинаций (hallucination), характерная для кода. Относись к каждому ответу как к черновику от стажёра: прочитай, запусти и проверь граничные случаи. Конкретно: попроси «функцию на Python для валидации email», и большинство моделей выдадут однострочную регулярку. Она примет a@b.c, но спокойно отвергнет вполне валидные адреса вроде user+tag@sub.domain.co и примет мусор вроде a@@b.com. Лекарство — задавать спецификацию самому: указать типы входа/выхода, назвать граничные случаи (пустая строка, юникод, plus-адресация, очень длинный ввод) и попросить юнит-тесты. Модель отлично пишет обвязку вокруг точной спецификации; и плохо придумывает эту спецификацию за тебя.
Представьте это как стажёра-программиста:
- 1. Пишем спецификацию: "Напиши функцию валидации email-адресов — возвращает true/false"
- 2. Указываем типы и сигнатуры: Указываем типы входа/выхода, язык и ожидаемый интерфейс
- 3. Добавляем граничные случаи: Пустой ввод, юникод, очень длинные строки, параллельный доступ
- 4. ИИ генерирует код: Полная реализация с типами, обработкой ошибок и документацией
- 5. Ревьюим на скрытые баги: Проверяем гонки данных, null-проверки, ошибки на единицу и отсутствие очистки ресурсов
Где это используется?
- Автодополнение кода: Интеграции в IDE типа GitHub Copilot
- Генерация бойлерплейта: Создание повторяющихся структур кода
- Перевод между языками: Конвертация кода между языками программирования
- Генерация тестов: Написание юнит-тестов по сигнатурам функций
- Подвохи: скрытые баги: Код от LLM может выглядеть правильным, но скрывать гонки данных, ошибки на единицу, отсутствие обработки ошибок или неверную логику граничных случаев — всегда делайте критическое ревью
Интересный факт: Код от LLM имеет опасное свойство: он выглядит правдоподобно с первого взгляда. Исследования показывают, что разработчики принимают код от ИИ с меньшей проверкой, чем человеческий, а он может содержать тонкие логические ошибки — перепутанные операторы сравнения, отсутствие null-проверок или асинхронный код, работающий 99% времени, но блокирующийся под нагрузкой.
Попробуйте сами!
Используй интерактивный пример ниже, чтобы увидеть, как разные подходы к промптингу влияют на качество сгенерированного кода.
Собери промпт — включай фичи:
Включи фичу выше, чтобы увидеть как промпт меняет сгенерированный код
Ключевая идея
- 1. Конкретность = качество. «Напиши функцию» даёт минимум. Каждое требование улучшает результат.
- 2. Если не попросить — не получишь. LLM идут кратчайшим путём.
- 3. Хорошие промпты — чеклисты, а не эссе. Каждый toggle = одна строка в промпте.
Частые вопросы
Как промпт влияет на качество сгенерированного кода?
Кардинально. Указание языка, типов, обработки ошибок, краевых случаев и желаемых паттернов превращает вывод из прототипа в production-ready код с правильной структурой и документацией.
Нужно ли проверять код от LLM?
Всегда. LLM может создать код с тонкими багами, уязвимостями безопасности или неверными предположениями. Относитесь к сгенерированному коду как к черновику от джуниора — ревьюйте, тестируйте и валидируйте.
Какие языки программирования LLM генерирует лучше всего?
Python, JavaScript/TypeScript и Java имеют больше всего обучающих данных и лучшие результаты. Менее распространённые языки (Rust, Haskell, Elixir) работают, но с бо́льшим количеством ошибок.
Как использовать LLM для рефакторинга существующего кода?
Предоставьте код с явными инструкциями: «Рефакторить для уменьшения дублирования», «Конвертировать в async/await», «Добавить TypeScript типы». Включите контекст о паттернах и ограничениях. Тщательно проверяйте диффы.
Попробуй сам
Интерактивное демо этой техники
Напиши функцию для фильтрации массива объектов по нескольким критериям
function filter(arr, key, value) { return arr.filter(item => item[key] === value); }
type Criteria<T> = {
[K in keyof T]?: T[K] | T[K][];
};
/**
* Фильтрует массив объектов по нескольким критериям.
* Критерий может быть значением (точное совпадение) или массивом (вхождение).
*
* @example
* const users = [{ name: "Alice", role: "admin", status: "active" }];
* filterByMultipleCriteria(users, { role: "admin", status: ["active", "pending"] });
* // => [{ name: "Alice", role: "admin", status: "active" }]
*/
function filterByMultipleCriteria<T extends Record<string, unknown>>(
items: T[],
criteria: Criteria<T>
): T[] {
const entries = Object.entries(criteria);
if (entries.length === 0) return [...items];
return items.filter((item) =>
entries.every(([key, expected]) => {
const actual = item[key];
return Array.isArray(expected)
? expected.includes(actual)
: actual === expected;
})
);
}
Структурированный промпт с примером вызова, ограничениями по типизации и явным edge case превращает однострочник без типов в production-ready generic-функцию.
Создайте бесплатный аккаунт для решения челленджей
6 челленджей с AI-проверкой для этого урока
Этот урок — часть структурированного курса по LLM.
Мой путь обучения