Один из наших проектов нужно было перенести на Kotlin, чтобы включить его в общую экосистему клиента и упростить поддержку. Мы должны были курировать процесс и следить за итоговой реализацией, в которой заказчик решил использовать ИИ.
В этой статье рассказываем о реальных возможностях нейронки для миграции бэкенда и про то, с чем лучше к ней не обращаться — чтобы не тратить ни время, ни нервы.
Главный челлендж переноса бэкенда на совершенно другой стек — сложность архитектуры и объем кодовой базы. Ты можешь хорошо разбираться в решениях на целевом коде, но при этом не знать то, на чем базируется старая система.
Так было и на нашем проекте. Миграцией занимался Senior клиента, который впервые столкнулся с исходным стеком, но понимал, что должно получиться в итоге. Для переноса небольшого сервиса с Node.js на Java-стек он использовал Claude.
Нейросеть обработала модуль целиком: сгенерировала контроллеры, DTO и базовые тесты. Разработчик быстро проверял результат, правил мелкие неточности и адаптировал код под нужные паттерны.
На все ушло примерно месяц. Мы вывели проект в продакшн, постепенно переключали ручки API и тестировали функционал. Еще около двух — заняла доработка и стабилизация.
Для заказчика ИИ показал себя выгодным инструментом. С его помощью один Java-специалист пересоздал сервис в два раза быстрее, чем его делали 2 бэкенд-разработчика на Node.js. Большая часть генерации действительно сэкономила время.
В чем же нейросеть реально полезна
1. Конвертация типовых структур
Если нужно перевести десятки DTO, моделей или интерфейсов между языками — это идеальная зона для ИИ. Такие задачи однообразны, правила преобразования предсказуемы, а ошибки легко проверить.
Пример запроса:
Перепиши этот код на Kotlin с использованием Spring Boot. Сохрани сигнатуру метода и добавь типизацию.
export async function getUser(req, res) {
const user = await db.findUser(req.params.id);
res.json(user);
}
Пример результата:
@RestController
@RequestMapping("/user")
class UserController(private val userService: UserService) {
@GetMapping("/{id}")
fun getUser(@PathVariable id: Long): ResponseEntity<UserDto> {
val user = userService.findUser(id)
return ResponseEntity.ok(user)
}
}
Код не финальный, но снимает большую часть рутинной работы.
2. Генерация тестов и документации
ИИ неплохо справляется с шаблонными юнит-тестами, когда структура кода однотипна. Он может покрыть базовые кейсы, а разработчик затем дополнит сложные сценарии.
Точно так же можно поручить нейросети генерацию документации по публичным методам API. Она формирует описания и примеры, а человек уточняет детали и форматирует под требования команды.
Пример запроса:
Составь описание API для этого контроллера в формате OpenAPI 3.0 и предложи набор тестов для проверки корректности ответов.
Полученные тексты не стоит копировать в чистом виде, но они помогут с ревью и структурой.
3. Анализ и рефакторинг кода
Миграцию редко выполняют те же специалисты, которые писали исходный код. Если документации нет, то ИИ может быстро разобраться в чужом проекте. Нейросеть опишет структуру модулей, укажет потенциальные зависимости или дублирование логики.
Пример запроса:
Объясни, что делает этот модуль. Определи входные и выходные данные, внешние вызовы и зависимости.
// order-service.js
import { getUserById } from './user-service.js';
import { getProductsInCart } from './product-service.js';
import { OrderRepository } from './db/OrderRepository.js';
export async function createOrder(userId) {
const user = await getUserById(userId);
const products = await getProductsInCart(userId);
const total = products.reduce((sum, item) => sum + item.price, 0);
const order = await OrderRepository.save({
userId: user.id,
products,
total,
status: 'created'
});
return { id: order.id, total, status: order.status };
}
Пример результата:
Модуль order-service отвечает за создание заказа. Вызывает функции getUserById (модуль user-service) и getProductsInCart (модуль product-service). Использует OrderRepository для записи данных в базу. На вход получает userId, возвращает JSON с идентификатором заказа, суммой и статусом.
Такие описания можно брать за основу для будущей технической документации или маппинга между сервисами.
Пример запроса:
Проанализируй этот код. Найди неиспользуемые импорты, повторяющиеся функции и зависимости между файлами.
[Очень длинный код]
Результат — список модулей, от которых можно отказаться, и явное понимание, как устроен старый проект.
4. Работа с результатами SQL-запросов
ИИ может сгенерировать код для маппинга резалт сета в сущности при использовании jdbcTemplate.
Пример запроса:
Сгенерируй RowMapper для JdbcTemplate, который сопоставляет результат SQL-запроса с сущностью User (+ контекст: [запрос в БД] и [сущность]).
RowMapper<User> userRowMapper = (rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setEmail(rs.getString("email"));
user.setCreatedAt(
rs.getTimestamp("created_at").toLocalDateTime()
);
return user;
};
Это формализованная задача без бизнес-логики, где результат легко проверить и доработать вручную.
Для переноса бэкенда удобнее использовать диалоговые чат-боты, которые умеют разбирать большие фрагменты кода и сохранять логику при конверсии. Это Claude, ChatGPT с Codex и IDE-ассистенты вроде Copilot или Codeium.
Чтобы не рисковать данными, лучше работать в закрытых окружениях или маскировать доменные имена и идентификаторы перед отправкой в чат.
С чем нейросеть не справится
После успешной реализации сервиса на очереди был более крупный модуль. Это уже полноценное веб-приложение с множеством интеграций по HTTP и Kafka, сложной структурой базы данных и фронтовыми взаимодействиями. Разработчик применил тот же подход, но результат оказался противоположным.
В итоговом коде накопилось множество конфликтов, тесты не проходили, а большая часть логики требовала ручной переписки. Все называли его «кодом Шредингера»: формально он есть, но не работает.
Причина оказалась в разнице масштабов. Небольшой сервис, где логика прозрачна и нет сложных зависимостей, хорошо подходит под генерацию. Крупное приложение — с интеграциями, кешами и ролевой моделью — требует архитектурного мышления. В таких случаях нейросеть перестает быть помощником и превращается в источник дополнительной нагрузки.
1. Ошибки в ролевой модели
В проекте была сложная система ролей. Права доступа зависели не только от фактической роли, но и от связей конкретного пользователя с сущностями. Иногда — с ограничениями по атрибутам.
Нейросеть сгенерировала вариант, в котором часть логики была реализована через RBAC-библиотеку. Остальная логика, включая ABAC, была догенерирована отдельно и оказалась в сервисном слое.
В итоге получился спагетти-код в сервисном слое + отдельный файл под RBAC. Решение переделали.
2. Игнорирование контекста
Решая задачу с ABAC, ИИ сгенерировал Request Wrapper, выполняющий вычитку и кэширование request body. Он был установлен как фильтр на все POST/PUT запросы с наивысшим приоритетом, что сломало обработку multipart-запросов. Все пришлось переписать.
3. Проблемы при работе с конкретными фреймворком
В нашем случае — со Spring. Для авторизации сервис должен был один раз загрузить JWKS-ключ и кешировать его. Нейросеть поставила аннотацию @Cacheable на метод, но не учла, что он вызывается из того же класса — в таком случае кеш не работает.
В результате на каждый запрос происходил новый HTTP-вызов к серверу авторизации. Исправили просто — вызвали бин через контекст, чтобы кеширование заработало.
4. Ошибки в модели выполнения
При генерации кода нейросеть выдала полностью синхронную реализацию, где операции выполнялись строго последовательно, блокируя поток. Из-за этого сервис потерял в производительности.
В целом же код ИИ «грязный». Нейросеть не знает проектного контекста, а рассказывать ей о нем не следует. Она нужна только для того, чтобы сократить время на рутину и помочь разобраться в новом для себя стеке.
Типичные проблемы:
— неточности в типах данных — nullable, generics;
— потеря бизнес-логики при конвертации;
— дублирование кода;
— некорректные зависимости или циклы импортов.
Валидировать логику должны тимлид и QA. Обязательный этап — ручное ревью и рефакторинг.
При хорошо выстроенном пайплайне и подготовленных артефактах миграция становится проще, но остается зоной рисков. Многое идет не по плану: меняются требования, всплывают зависимости, ломаются связи между сервисами.
Так или иначе, все узкие места связаны с архитектурой. Чем она сложнее, тем больше уязвимых мест для переноса и меньше участия ИИ. В этом контексте код невозможно переписать построчно, самому не разобравшись с логикой системы.
А приходилось ли вам участвовать в миграции проекта на другой стек — с какого на какой? Использовали нейросеть?
Dhwtj
Может да, а может и нет. JS молчит про типы. Может, id это int, string, uuid...
Такому только дай волю он весь код сломает
И не убирает ничего из корзины когда заказ сделан. Глюк