AI-миграция кода между языками программирования
Миграция кодовой базы с одного языка на другой — задача, которую раньше решали одним из двух способов: переписывали вручную (дорого и долго) или искали транспиляторы (узкая применимость, некачественный вывод). AI-миграция занимает промежуточную позицию: она не просто переводит синтаксис, но адаптирует идиомы, заменяет библиотеки на идиоматические аналоги и учитывает особенности целевого языка.
Архитектура системы миграции
Наивный подход — скормить LLM весь файл и попросить перевести. Он работает для файлов до 200–300 строк. Для реальных кодовых баз нужна другая архитектура:
Dependency Analyzer — строит граф зависимостей между файлами/модулями. Определяет порядок миграции.
Chunk Splitter — разбивает файлы на независимые чанки (класс, функция, модуль), которые можно мигрировать и тестировать отдельно.
Context Manager — передаёт в LLM уже мигрированные зависимости, чтобы новые файлы использовали правильные импорты.
Validator — компилирует и тестирует мигрированный код.
Glossary — словарь соответствий: библиотека оригинала → библиотека в целевом языке.
Миграция Python → TypeScript
from anthropic import Anthropic
from pathlib import Path
import ast
import json
from typing import Optional
client = Anthropic()
# Словарь соответствия библиотек Python → TypeScript/Node.js
PYTHON_TO_TS_GLOSSARY = {
"fastapi": "express + zod (or hono)",
"pydantic": "zod",
"sqlalchemy": "prisma (or drizzle-orm)",
"pytest": "jest (or vitest)",
"requests": "fetch (native) or axios",
"asyncio": "native async/await + Promise",
"datetime": "Date + date-fns",
"pathlib": "path (node built-in)",
"dataclass": "interface or class with constructor",
"TypedDict": "interface",
"Optional[X]": "X | undefined",
"List[X]": "X[]",
"Dict[K, V]": "Record<K, V>",
}
MIGRATION_SYSTEM = """Ты — senior engineer, мигрирующий Python код на TypeScript.
Принципы:
- Используй идиомы TypeScript, не Python с другим синтаксисом
- Pydantic модели → Zod схемы + TypeScript интерфейсы
- SQLAlchemy → Prisma (если ORM) или raw SQL с типами
- FastAPI декораторы → Express/Hono роуты
- Python async/await → TypeScript async/await (они идентичны в семантике)
- Exception handling: Python exceptions → TypeScript Error классы + Result types
- Type hints → строгие TypeScript типы (no any)
Структура TypeScript файла:
1. imports (используй ESM)
2. типы/интерфейсы
3. константы
4. основной код
Соглашения:
- snake_case → camelCase для переменных/функций
- snake_case → PascalCase для классов
- Добавляй JSDoc для публичных функций"""
class PythonToTypeScriptMigrator:
def __init__(self):
self.migrated_modules: dict[str, str] = {} # original_path -> ts_content
self.type_glossary: dict[str, str] = {} # python_type -> ts_type
def migrate_file(
self,
py_file: str,
related_migrations: Optional[dict[str, str]] = None,
) -> str:
"""Мигрирует один Python файл в TypeScript"""
source = Path(py_file).read_text()
# Анализируем импорты через AST
imports = self._extract_imports(source)
library_mapping = self._map_libraries(imports)
# Собираем контекст из уже мигрированных зависимостей
context_parts = []
if related_migrations:
for dep_file, ts_content in related_migrations.items():
context_parts.append(
f"// Already migrated: {dep_file}\n{ts_content[:1000]}"
)
context = "\n\n".join(context_parts) if context_parts else ""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=8096,
system=MIGRATION_SYSTEM,
messages=[{
"role": "user",
"content": f"""Мигрируй Python файл в TypeScript.
Файл: {py_file}
```python
{source}
Библиотеки (Python → TypeScript): {json.dumps(library_mapping, ensure_ascii=False, indent=2)}
{f"Контекст уже мигрированных зависимостей:{chr(10)}{context}" if context else ""}
Верни только TypeScript код файла.""" }] )
ts_code = response.content[0].text
# Убираем markdown обёртку если есть
if "```typescript" in ts_code:
ts_code = ts_code.split("```typescript")[1].split("```")[0].strip()
elif "```" in ts_code:
ts_code = ts_code.split("```")[1].split("```")[0].strip()
return ts_code
def _extract_imports(self, source: str) -> list[str]:
"""Извлекает импорты через AST"""
try:
tree = ast.parse(source)
imports = []
for node in ast.walk(tree):
if isinstance(node, ast.Import):
imports.extend(alias.name for alias in node.names)
elif isinstance(node, ast.ImportFrom):
if node.module:
imports.append(node.module.split(".")[0])
return list(set(imports))
except SyntaxError:
return []
def _map_libraries(self, python_imports: list[str]) -> dict:
"""Сопоставляет Python библиотеки с TypeScript аналогами"""
mapping = {}
for lib in python_imports:
if lib in PYTHON_TO_TS_GLOSSARY:
mapping[lib] = PYTHON_TO_TS_GLOSSARY[lib]
return mapping
def migrate_project(self, src_dir: str, output_dir: str) -> dict:
"""Мигрирует весь Python проект в TypeScript"""
src_path = Path(src_dir)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
# Строим граф зависимостей
files = list(src_path.rglob("*.py"))
migration_order = self._topological_sort(files)
results = {}
for py_file in migration_order:
if py_file.name.startswith("test_"):
continue # Тесты мигрируем отдельно
# Получаем уже мигрированные зависимости
related = {
str(dep): self.migrated_modules[str(dep)]
for dep in migration_order
if str(dep) in self.migrated_modules
}
ts_content = self.migrate_file(str(py_file), related[-3:] if len(related) > 3 else related)
# Сохраняем
relative = py_file.relative_to(src_path)
ts_file = output_path / relative.with_suffix(".ts")
ts_file.parent.mkdir(parents=True, exist_ok=True)
ts_file.write_text(ts_content)
self.migrated_modules[str(py_file)] = ts_content
results[str(py_file)] = str(ts_file)
return results
def _topological_sort(self, files: list[Path]) -> list[Path]:
"""Сортирует файлы в порядке зависимостей (упрощённая версия)"""
# Простая эвристика: сначала модели, потом утилиты, потом сервисы, потом роуты
priority = {"model": 0, "schema": 0, "type": 0, "util": 1, "helper": 1,
"service": 2, "repo": 2, "route": 3, "handler": 3, "view": 3, "app": 4}
def get_priority(path: Path) -> int:
name = path.stem.lower()
for key, p in priority.items():
if key in name:
return p
return 2 # Default
return sorted(files, key=get_priority)
### Миграция с валидацией
```python
def migrate_and_validate(py_file: str, ts_output: str) -> dict:
"""Мигрирует файл и запускает TypeScript компилятор"""
import subprocess
migrator = PythonToTypeScriptMigrator()
ts_code = migrator.migrate_file(py_file)
Path(ts_output).write_text(ts_code)
# Компилируем для проверки типов
result = subprocess.run(
["npx", "tsc", "--noEmit", "--strict", ts_output],
capture_output=True, text=True
)
if result.returncode != 0:
# Пробуем исправить ошибки компиляции
fixed_code = fix_typescript_errors(ts_code, result.stdout + result.stderr)
Path(ts_output).write_text(fixed_code)
result = subprocess.run(
["npx", "tsc", "--noEmit", "--strict", ts_output],
capture_output=True, text=True
)
return {
"success": result.returncode == 0,
"errors": result.stdout + result.stderr if result.returncode != 0 else "",
"output_file": ts_output,
}
def fix_typescript_errors(ts_code: str, errors: str) -> str:
"""Исправляет ошибки TypeScript компилятора через LLM"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=8096,
messages=[{
"role": "user",
"content": f"""Исправь ошибки TypeScript компилятора.
Код:
```typescript
{ts_code}
Ошибки компилятора:
{errors}
Верни исправленный код."""
}]
)
text = response.content[0].text
if "typescript" in text: return text.split("typescript")[1].split("```")[0].strip()
return text
### Другие направления миграции
Система аналогично работает для других пар языков. Ключевые отличия — в глоссарии библиотек:
**Java → Kotlin**: jackson → kotlinx.serialization, Spring annotations → Ktor/Spring аналоги, checked exceptions → sealed classes с Result.
**PHP → Python**: Laravel Eloquent → SQLAlchemy, Blade templates → Jinja2, Composer → pip/uv.
**JavaScript → TypeScript**: основная работа — добавление типов, замена `any` на конкретные типы, добавление Zod валидации на boundaries.
### Практический кейс: Python microservice → TypeScript
**Контекст**: стартап мигрировал notification-сервис (Python FastAPI, 3200 строк) в TypeScript для унификации стека (фронтенд команда знала только JS/TS).
**Объём**: 28 файлов, 12 Pydantic моделей, 34 API endpoints, 180 юнит-тестов.
**Процесс (2 недели)**:
- Неделя 1: настройка glossary, миграция моделей и утилит (автоматически), ручная доработка 3 сложных файлов с бизнес-логикой
- Неделя 2: миграция роутов, адаптация тестов (Jest), интеграционное тестирование
**Результаты**:
- 85% кода мигрировано автоматически без ручных правок
- 15% потребовало доработки (сложная логика с Python-специфичными идиомами)
- TypeScript compilation errors: 47 → 0 (после 2 итераций LLM fix)
- Test coverage мигрированного сервиса: 71% (было 74% в Python — минимальная потеря)
**Неожиданный плюс**: в процессе миграции AI выявил 3 места с потенциальными race conditions в Python коде, которые были исправлены в TypeScript версии.
### Сроки
- Прототип миграции одного файла: 1–2 дня
- Система с dependency graph и batch-миграцией: 1 неделя
- Валидация + auto-fix loop: 1 неделя
- Полная миграция проекта 5000–15000 строк: 3–6 недель (включая QA)







