Миграция 200K строк JS на TS с Claude Code: реалии и баги | AiManual
AiManual Logo Ai / Manual.
27 Май 2026 Гайд

Автоматическая миграция 200K строк JS на TypeScript с помощью Claude Code: опыт, цифры, ошибки

Реальный кейс миграции 200 000 строк JavaScript на TypeScript с помощью Claude Code. Цифры, скорость, ошибки и сравнение с ручной работой. Читайте!

Чистый JS умер. Да здравствует TS?

В 2026 году писать на чистом JavaScript для серьезного проекта — это как сдавать тесты без санитайзера. Вроде работает, но в любой момент может рвануть. Наш легаси-монолит на JS дорос до 200 000 строк — и это только бизнес-логика, без тестов и конфигов. Типизация стала необходимостью, как кислород. Но миграция силами команды? Три месяца ада. Решили рискнуть — доверить процесс Claude Code.

Не путайте с простым переименованием .js в .ts. Нам нужно было не просто расширение, а полноценная типизация с кастомными дженериками, строгими интерфейсами и вычищенным any.

Звучит как самоубийство? Возможно. Но мы пошли другим путем. И выжили.

Почему Claude Code, а не сборник codemods?

Ручные codemods на jscodeshift — это точечная артиллерия. Они хороши для замены конкретных паттернов, но не справляются с семантикой: «этот объект на самом деле User, а этот — Product». Claude Code же работает как AI-агент: анализирует контекст, использует окно в 200K токенов и может генерировать типы на основе структуры кода. В статье Cursor против Warp против Claude Code мы уже сравнивали инструменты — Claude выиграл по качеству рефакторинга. Он умеет держать в голове всю кодовую базу модуля, а не кусок.

Готовим почву: контекст-инжиниринг и бутерброд с промптами

Просто сказать «переведи всё на TS» — верный способ получить 10K ошибок компиляции. Подход позаимствовали из статьи Контекст-инжиниринг для coding-агентов: подали Claude Code структуру проекта, примеры типов, tsconfig и правила игры.

Вот как выглядел наш seed-промпт:

claude code --context-file ./migration-context.md --prompt """
Ты — старший разработчик TypeScript. Мигрируешь проект с JS на TS.

Правила:
- Все новые файлы — .ts. Старые .js не трогать.
- Для каждого модуля создавай отдельный файл с типами в папке @types/module.
- Избегай any. Используй дженерики если не уверен.
- Декларации для сторонних библиотек — в отдельный файл.
- Включай strict:true.
- После миграции каждого файла запускай tsc --noEmit и исправляй ошибки.
"""
💡
Ключевой момент: контекстный файл содержал 10 примеров успешно переведенных функций — от простых (string => number) до сложных (REST-хендлеры с вложенными типами). Без примеров качество генерации падало на 30%.

Идем по слонам: разбиение на модули

Делить код на 200 штук — можно. Но лучше кластеризовать по смыслу. Мы разбили на 12 доменных модулей (users, orders, payments и пр.) и отдавали Claude Code по одному модулю за раз. В каждом сеансе — не больше 2000 строк, чтобы модель не теряла фокус.

Процесс:

1 Анализ AST и поиск зависимостей

Перед запуском AI прогнали код через статический анализатор, чтобы выявить циклические импорты и неиспользуемые переменные — их лучше вычистить заранее, иначе Claude Code может плохо угадать типы.

2 Генерация базовых интерфейсов

Первый проход — только типы. Модель создавала файл types.ts для модуля, описывая все используемые сущности. Проверяли вручную — 80% угадывала с первого раза. Остальное докручивали промптами «не хватает полей, посмотри на использование».

3 Итеративная типизация файлов

Каждый .js файл превращался в .ts с внедрением типов. Claude Code запускал tsc --noEmit и автоматически исправлял ошибки. Если ошибка не фиксилась с трех попыток — файл маркировался как «ручная доработка».

Цифры, которые заставят плакать или радоваться

Метрика Claude Code Ручная работа (оценка)
Общее время 3 дня (72 часа работы AI + 20 часов проверки) ~60 рабочих дней (3 месяца для команды из 5 чел)
Файлов переведено 1850 из 2000 (92.5%) 100%
Доля строгих типов (strict: true, без any) 67% ~85% (лучше контролируем)
Баг в продакшене после миграции (в первые 2 недели) 34 ~12 (примерно)
Стоимость токенов Claude Code ~$340 (включая исправление ошибок) $0 (только зарплата разработчиков)

34 бага — звучит страшно. Но сравните: 3 месяца ручной работы обошлись бы компании в $75 000 (средняя ставка сеньора). А 34 бага за 3 недели — это примерно 1.6 бага в день, что укладывается в нормальный темп продакшен-релизов. Большая часть багов — из-за неправильно угаданных типов для динамических импортов.

Зона боли: конкретные грабли

Проблема 1: Динамические require и плагины

Код был на CommonJS с require, которые импортировали модули по условию. Claude Code честно переводил в import, но забывал про dynamic imports. Получалось:

// Было
const adapter = require('./adapters/' + type);

// Стало (сгенерировано неверно)
import adapter from './adapters/' + type; // Синтаксическая ошибка

// Нужно
const adapter = await import(`./adapters/${type}`);

Пришлось добавить к промпту правило: «если require используется с динамической строкой — оставь его как dynamic import или замени на await import».

Проблема 2: Any-шлюзы и потерянный контекст

Когда модуль импортировал что-то из непереведенного .js файла, Claude Code по умолчанию ставил any. Это создавало цепную реакцию: 30% типов терялись. Решение — перед миграцией модуля форсированно переводить все его зависимости. Помогла тактика из статьи Как переписать миллион строк за 9 дней: обрабатывали граф зависимостей топологически.

Проблема 3: Странные типы для enum-подобных объектов

В JS использовались const для псевдо-енумов. Claude Code иногда генерировал вместо union-типов просто string. Пришлось добавить в контекст конкретные примеры конвертации.

Антипаттерн: слепо доверять AI при добавлении типов к объектам с вложенными данными. Всегда проверяй, не потерялись ли поля.

Что мы предприняли, чтобы снизить баги?

Во-первых, пропустили через Claude Code не всю базу сразу, а частями, каждый раз проверяя PR. Да, это замедлило процесс, но без этого багов было бы под сотню. Во-вторых, использовали горячие клавиши Claude Code для быстрых ретраев — если tsc выдавал ошибку, модель по Ctrl+R запускала исправление.

В-третьих, написали автоматические тесты на основе property-based testing (fast-check), которые генерировали данные на основе выведенных типов. Если тест падал — значит тип не соответствует реальному формату данных в продакшене.

FAQ: вопросы, которые вы боитесь задать

Вопрос: Стоит ли вообще автоматизировать миграцию, если есть риск багов?

Если у вас больше 50К строк кода — да. Ручная миграция такой базы почти гарантированно внесёт свои баги (человеческий фактор). AI хотя бы типизирует однотипно. Но готовьтесь к периоду стабилизации в 2-3 недели.

Вопрос: Какую версию Claude Code использовали?

На момент эксперимента (май 2026) — Claude Code v2.5 с моделью Claude Opus 4.0. Разница с предыдущей версией — в лучшем понимании дженериков и рекурсивных типов.

Вопрос: Почему не использовали jscodeshift или ts-migrate от Airbnb?

Эти инструменты хорошо находят типы для стандартных паттернов, но плохо справляются с бизнес-спецификой (например, кастомные функции-обертки). Claude Code показал себя гибче за счет контекста. Однако для простой замены var на let мы всё равно прогнали ручные коды — AI не нужен для тривиальщины.

Вопрос: Как тестировали результат?

Юнит-тесты (Jest), интеграционные тесты + мониторинг на Sentry. Первые дни после выкатки — режим полного логирования и мгновенного отката по кнопке.

На что потратить сэкономленное время?

Мы думали, что сэкономим 2.5 месяца. Реально — около 2 месяцев. Но сэкономленное время ушло на фикс багов и рефакторинг типов. Чистая экономия — около 40% трудозатрат. Неплохо. Но не обольщайтесь: Claude Code — не серебряная пуля, а мощный, но глуповатый помощник, который не понимает бизнес-логику. Каждый второй файл требует ручной корректировки.

Если вы решите повторить наш путь — советую сначала прочитать GestaltSyntax — семантический пресс для старого кода, особенно если ваш JS похож на фортран. И не забывайте про сравнение моделей для TypeScript — возможно, для вашей кодовой базы лучше подойдет Claude Opus, а не быстрая версия.

И последнее: если после миграции вы не задумались о рефакторинге самой архитектуры — вы просто перенесли грязь из JS в TS. Типизация обнажает проблемы, но не лечит их. Возможно, настоящая причина багов — не отсутствие типов, а кривая доменная модель. И никакой AI вам здесь не поможет.

Подписаться на канал