Генерация кода нейросетью: Python, TypeScript, SQL

Генерация кода полезна там, где задача уже понятна: написать черновик функции, тест, SQL-запрос, документацию или план рефакторинга.
Я использую нейросети в разработке почти каждый день, но не как автопилот. Хороший результат получается, когда я даю контекст, ограничения, пример входных данных и критерии проверки. Плохой результат чаще появляется после запроса в духе «напиши мне сервис». Нейросеть уверенно выдаёт код, который выглядит правдоподобно, но может не учитывать версию библиотеки, доменную логику, лимиты базы или внутренние соглашения команды.
В SoftChat для таких задач удобно держать отдельные диалоги под разные рабочие контексты: один для SQL-аналитики, второй для фронтенда, третий для ревью. История сохраняется, ответы отображаются с Markdown, включая блоки кода и таблицы, а шаблоны промптов помогают не писать один и тот же вводный текст заново. Если черновик запроса получился рыхлым, я часто прогоняю его через «Улучшить запрос», смотрю превью и только после этого отправляю.
Какие задачи разработчика реально автоматизируются
Я делю задачи генерации кода на четыре группы.
| Задача | Что отдавать в промпте | Что проверять вручную |
|---|---|---|
| Черновик функции | Типы данных, пример входа и выхода, крайние случаи | Ошибки на пустых данных, сложность, формат результата |
| Unit-тесты | Контракт функции, 3–5 кейсов, ожидаемые исключения | Покрытие негативных сценариев, фикстуры, флаки |
| SQL | Схему таблиц, объём данных, индексы, нужную агрегацию | План выполнения, дубли, фильтры по датам |
| Рефакторинг | Старый код, цель изменения, запреты | Сохранение поведения, совместимость, читаемость |
| Документация | Назначение модуля, примеры запуска, переменные окружения | Устаревшие команды, секреты, неверные пути |
Моя рабочая норма: нейросеть пишет первый вариант за 30–90 секунд, потом я трачу 5–20 минут на ревью, запуск тестов и правки. Для типовых кусков это быстрее ручного старта с пустого файла. Для кода, который трогает деньги, права доступа или персональные данные, экономия часто исчезает, потому что проверка становится длиннее генерации.
Если вы только выстраиваете процесс, полезно начать не с кода в продакшене, а с повторяемых черновиков: тесты, миграции, README, SQL для аналитических витрин. Похожие принципы я описывал в материале про внедрение нейросетей в рабочие процессы, а базовая дисциплина формулирования запроса разобрана в статье про промптинг для нейросетей.
Сценарий: Python для обработки заказов
Возьмём задачу из аналитического пайплайна. Есть список заказов из API. Нужно посчитать выручку по дням, исключить отменённые заказы и округлить сумму до двух знаков.
Промпт
Напиши Python-функцию aggregate_revenue_by_day.
Вход: список словарей с полями order_id, created_at, status, amount.
created_at приходит в ISO-формате, amount может быть строкой или числом.
Нужно вернуть список словарей: date и revenue.
Исключи status = cancelled.
Отсортируй результат по дате.
Добавь 3 unit-теста на pytest: обычный случай, пустой список, amount строкой.
Код должен быть без внешних зависимостей, кроме pytest в тестах.
Ответ нейросети
from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP
def aggregate_revenue_by_day(orders):
# Копим суммы через Decimal, чтобы не ловить ошибки float
revenue_by_day = {}
for order in orders:
if order.get('status') == 'cancelled':
continue
created_at = datetime.fromisoformat(order['created_at'])
day = created_at.date().isoformat()
amount = Decimal(str(order.get('amount', 0)))
revenue_by_day[day] = revenue_by_day.get(day, Decimal('0')) + amount
result = []
for day in sorted(revenue_by_day):
rounded = revenue_by_day[day].quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
result.append({'date': day, 'revenue': float(rounded)})
return result
def test_aggregate_regular_case():
orders = [
{'order_id': 1, 'created_at': '2025-02-01T10:00:00', 'status': 'paid', 'amount': 100},
{'order_id': 2, 'created_at': '2025-02-01T12:00:00', 'status': 'cancelled', 'amount': 50},
{'order_id': 3, 'created_at': '2025-02-02T09:00:00', 'status': 'paid', 'amount': 25.555},
]
assert aggregate_revenue_by_day(orders) == [
{'date': '2025-02-01', 'revenue': 100.0},
{'date': '2025-02-02', 'revenue': 25.56},
]
def test_aggregate_empty_list():
assert aggregate_revenue_by_day([]) == []
def test_aggregate_amount_as_string():
orders = [
{'order_id': 1, 'created_at': '2025-02-01T10:00:00', 'status': 'paid', 'amount': '10.10'},
{'order_id': 2, 'created_at': '2025-02-01T11:00:00', 'status': 'paid', 'amount': '2.20'},
]
assert aggregate_revenue_by_day(orders) == [
{'date': '2025-02-01', 'revenue': 12.3},
]
Что я сразу проверяю: часовой пояс, формат даты, поведение при amount = None, допустимые статусы, возврат float вместо строки или Decimal. В реальном проекте я бы попросил вторым запросом не менять публичный контракт, но добавить валидацию и отдельный тест на некорректную дату. Для финансовых расчётов я чаще возвращаю строку, например '25.56', чтобы не тащить неточность float дальше по цепочке.
Сценарий: JavaScript/TypeScript для фронтенда
Теперь тот же домен, но фронтенд. Нужно получить дневную выручку с API и показать состояние загрузки, ошибку и данные. Я прошу не полноценный экран, а хук, потому что так проще проверить границы ответственности.
Промпт
Напиши TypeScript-хук useRevenueByDay для React.
Он принимает dateFrom и dateTo в формате YYYY-MM-DD.
Делает GET-запрос на /api/revenue?from=...&to=...
Возвращает data, isLoading, error и refetch.
Тип элемента data: { date: string; revenue: number }.
Добавь отмену запроса через AbortController.
Не используй сторонние библиотеки.
Ответ нейросети
import { useCallback, useEffect, useState } from 'react';
type RevenuePoint = {
date: string;
revenue: number;
};
type UseRevenueResult = {
data: RevenuePoint[];
isLoading: boolean;
error: string | null;
refetch: () => void;
};
export function useRevenueByDay(dateFrom: string, dateTo: string): UseRevenueResult {
const [data, setData] = useState<RevenuePoint[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [reloadIndex, setReloadIndex] = useState(0);
const refetch = useCallback(() => {
setReloadIndex((value) => value + 1);
}, []);
useEffect(() => {
const controller = new AbortController();
const params = new URLSearchParams({ from: dateFrom, to: dateTo });
async function loadRevenue() {
setIsLoading(true);
setError(null);
try {
const response = await fetch(`/api/revenue?${params.toString()}`, {
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`Ошибка API: ${response.status}`);
}
const payload = (await response.json()) as RevenuePoint[];
setData(payload);
} catch (err) {
// AbortError не показываем пользователю как ошибку загрузки
if (err instanceof DOMException && err.name === 'AbortError') {
return;
}
setError(err instanceof Error ? err.message : 'Неизвестная ошибка');
} finally {
if (!controller.signal.aborted) {
setIsLoading(false);
}
}
}
loadRevenue();
return () => controller.abort();
}, [dateFrom, dateTo, reloadIndex]);
return { data, isLoading, error, refetch };
}
Здесь результат ближе к рабочему коду, но я всё равно смотрю на четыре вещи. Первое, не нарушает ли хук наш способ работы с авторизацией и базовым URL. Второе, нужна ли проверка схемы ответа. Третье, как компонент поведёт себя при быстрой смене диапазона дат. Четвёртое, не стоит ли вынести сообщения об ошибке в слой локализации.
На практике я часто прошу нейросеть дать не один вариант, а два: простой хук без кэша и вариант под существующий слой запросов. Затем выбираю куски. Такой режим снижает риск, что модель навяжет архитектуру, которая не совпадает с проектом.
Сценарий: SQL для агрегации и проверки гипотез
SQL хорошо подходит для генерации, если в промпте есть схема таблиц и ожидаемый результат. Без схемы нейросеть придумывает поля. Иногда угадывает, чаще тратит ваше время.
Промпт
Есть PostgreSQL.
Таблица orders: id, user_id, created_at, status, amount.
Нужно посчитать дневную выручку, число оплаченных заказов и скользящую среднюю выручку за 7 дней.
Брать только status = paid.
Период: с 2025-02-01 по 2025-02-28 включительно.
Верни SQL и коротко объясни, какие индексы помогут.
Ответ нейросети
WITH daily_revenue AS (
SELECT
created_at::date AS revenue_date,
COUNT(*) AS paid_orders,
SUM(amount) AS revenue
FROM orders
WHERE status = 'paid'
AND created_at >= DATE '2025-02-01'
AND created_at < DATE '2025-03-01'
GROUP BY created_at::date
)
SELECT
revenue_date,
paid_orders,
revenue,
AVG(revenue) OVER (
ORDER BY revenue_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS revenue_7d_avg
FROM daily_revenue
ORDER BY revenue_date;
-- Для фильтра по статусу и дате полезен составной индекс:
-- CREATE INDEX idx_orders_status_created_at ON orders (status, created_at);
В таком ответе мне нравится полуоткрытый интервал дат: меньше риска ошибиться на времени внутри последнего дня. Но есть нюанс. Если нужны дни без заказов, запрос их не покажет. Значит, для графика нужен generate_series и левое соединение. Я обычно сразу задаю уточнение: «Добавь строки для дней без заказов, revenue = 0». Это хороший пример диалога, где первый ответ не финальный, а ступень к корректной версии.
Для таблицы на 20 миллионов строк я бы проверил EXPLAIN ANALYZE, селективность status, тип amount, партиционирование по дате и влияние created_at::date на группировку. Нейросеть может подсказать направление, но план выполнения остаётся источником правды.
Ревью и рефакторинг: как просить разбор, а не переписывание ради переписывания
Код-ревью через нейросеть работает лучше, когда у запроса есть рамки. Я не пишу «улучши код». Я пишу: «найди баги, проблемы конкурентности, риск утечки данных, лишнюю сложность; не меняй публичный интерфейс; предложи патч отдельным блоком».
Пример промпта для ревью:
Проведи code review функции ниже.
Контекст: функция вызывается в обработчике API 200–500 раз в минуту.
Нужно найти риски по производительности, безопасности и корректности данных.
Не меняй сигнатуру функции.
Ответ дай в формате: критичные проблемы, средние проблемы, косметика, патч.
Такой формат удобен для команды: критичные замечания идут в задачу сразу, средние попадают в технический долг, косметику можно отклонить. В SoftChat я для этого держу шаблон промпта, где заранее прописаны уровни серьёзности, запрет на изменение контракта и просьба ссылаться на конкретные строки. Шаблоны особенно выручают, когда ревью повторяется несколько раз в день.
Антипаттерн, который я встречал в командах: разработчик принимает большой сгенерированный рефакторинг без тестов, потому что «код стал красивее». Если изменилось 300 строк и нет стабильного набора тестов, это не рефакторинг, а переписывание с неизвестным поведением. Я предпочитаю дробить: сначала добавить тесты на текущее поведение, потом переименования, потом выделение функций, затем оптимизация.
Документирование кода: README, комментарии и API-описания
Документация даёт быстрый выигрыш, потому что нейросеть хорошо превращает код и заметки в связный текст. Для README я отдаю структуру проекта, команды запуска, переменные окружения и типовые ошибки. Для комментариев прошу не пересказывать каждую строку, а описывать причины решений.
Хороший промпт для README:
Собери README для сервиса revenue-api.
Разделы: назначение, локальный запуск, переменные окружения, команды тестов, формат ответа /api/revenue, частые ошибки.
Пиши кратко, без рекламного тона.
Не выдумывай переменные окружения. Используй только список: DATABASE_URL, API_TOKEN, LOG_LEVEL.
Последняя строка про запрет на выдумывание особенно полезна. Без неё модель может добавить REDIS_URL, SENTRY_DSN или другие привычные переменные, которых в проекте нет. Для API-документации я добавляю пример запроса и ответа, коды ошибок и ограничения по диапазону дат. Если документация публикуется наружу, я отдельно проверяю, не попали ли туда внутренние URL, токены из примеров или названия закрытых систем.
Подробнее о генерации структурированных текстов и проверке результата можно посмотреть в статье про нейросеть для генерации текста. Принцип тот же: черновик ускоряет старт, качество появляется после проверки по критериям.
Ограничения: где нейросеть ошибается чаще всего
В моей практике повторяются семь классов ошибок.
- Устаревшие API. Модель предлагает метод, который был в старой версии библиотеки или поменял сигнатуру.
- Выдуманные поля и параметры. Особенно в SQL, SDK и конфигурациях.
- Слабая обработка ошибок. Часто есть happy path, но нет таймаутов, повторов, отмены и понятного сообщения пользователю.
- Игнорирование безопасности. Встречаются SQL-инъекции в строковой сборке, лишнее логирование токенов, небезопасные настройки CORS.
- Неверные предположения о данных. Пустые списки,
null, дубли, часовые пояса и разные валюты ломают красивый пример. - Слишком крупный патч. Чем больше сгенерированный diff, тем сложнее понять, где изменилась логика.
- Несовпадение со стилем проекта. Код может быть корректным, но выбиваться из соглашений команды.
Мой чеклист перед принятием сгенерированного кода короткий: запустить тесты, добавить минимум один негативный кейс, проверить типы, посмотреть линтер, оценить сложность, прочитать diff целиком. Для SQL добавляется план запроса. Для фронтенда, ручная проверка состояния загрузки, ошибки и пустого ответа.
Как встроить генерацию кода в командный процесс
Для личной работы достаточно дисциплины промптов. Для команды нужен договор. Я бы начал с трёх правил: какие данные нельзя отправлять в запрос, какие типы задач разрешены для генерации, какой уровень ревью обязателен перед слиянием.
Практичная схема выглядит так: задача → промпт → черновик → локальный запуск → ревью → маленький коммит. Если какой-то шаг пропущен, риск растёт. Особенно опасен пропуск локального запуска, потому что синтаксически красивый код может падать на первом импорте.
Ещё один рабочий приём, хранить удачные промпты рядом с кодом или во внутренней базе знаний. Через месяц у команды появляется 10–20 проверенных шаблонов: тесты для обработчика API, SQL-агрегация, README для сервиса, ревью миграции, разбор ошибки из логов. Это уже не разовые эксперименты, а повторяемый процесс.
Заключение
Генерация кода нейросетью лучше всего работает как ускоритель понятных задач. Она быстро даёт черновик функции, теста, SQL-запроса, хука или документации. Но ответственность за контракт, безопасность, производительность и совместимость остаётся у разработчика.
Я получаю максимальную пользу, когда формулирую задачу как техническое задание на маленький фрагмент: входы, выходы, ограничения, версия стека, критерии проверки. После этого нейросеть экономит время на рутине, а я трачу внимание на архитектуру, крайние случаи и качество изменений. Такой баланс честнее и надёжнее, чем ожидание, что инструмент сам поймёт весь проект по одной фразе.