Раньше я формулировал эту мысль провокационно: TypeScript — лучший язык для Codex. Сейчас я бы сказал точнее. Для той продакшен-разработки, которой я занимаюсь — frontend и full-stack приложения — TypeScript дает среду, в которой Codex проще контролировать, проверять и использовать без лишней веры в «магический AI».
Ключевое слово здесь не TypeScript, а управляемость. AI-агенты отлично умеют писать код, который выглядит правдоподобно. Продакшен требует большего: код должен вписываться в существующие контракты, сохранять поведение, переживать рефакторинги и быстро падать, когда какое-то предположение неверно. TypeScript делает намного больше таких ограничений машиночитаемыми, чем обычный JavaScript.
Что Codex на самом деле нужно от кодовой базы
Хороший промпт полезен, но промпт — лишь один источник контекста. Сама кодовая база либо помогает понять задачу, либо заставляет модель гадать. На практике лучше всего работает среда, где важные предположения видны в коде и автоматически проверяются.
- Сигнатуры функций задают допустимые входы и выходы.
- Типы и интерфейсы явно описывают доменные объекты.
- Union-типы и enum сужают набор допустимых состояний.
- Компилятор превращает многие неверные предположения в конкретные ошибки сразу после изменения.
AI-агенту не обязательно идеально «понимать все приложение», если приложение само возражает, когда агент нарушает контракт. Чем короче этот цикл обратной связи, тем меньше места остается для уверенных догадок.
Типы — это исполняемый контекст, а не просто документация
Хорошие типы объясняют намерение человеку, но для AI-workflow у них есть еще одно преимущество: эту документацию можно проверить машиной. Например:
type PaymentResult =
| { ok: true; receiptId: string }
| { ok: false; code: "declined" | "timeout" };
function getReceiptId(result: PaymentResult) {
if (result.ok) return result.receiptId;
return result.receiptId;
// TypeScript: Property 'receiptId' does not exist
// on the failure branch.
}В JavaScript такая ошибка может выглядеть вполне нормально, пока ветка не выполнится в runtime. TypeScript отклонит неверное предположение до merge. Для Codex ошибка компилятора — это не просто красная строка в редакторе, а довольно точный сигнал о том, что именно модель поняла неправильно.
Поэтому я предпочитаю точные доменные типы широким конструкциям вроде Record<string, any>. Чем лучше типы описывают реальность, тем полезнее сама кодовая база как контекст и для разработчика, и для AI.
Компилятор превращает ошибки в feedback loop
Я не доверяю процессу «попросить Codex написать код и принять первый ответ». Нормальный процесс итеративный:
task → inspect → edit → typecheck → lint → test → review diffЭто скучная последовательность — и именно поэтому она сильная. После каждого осмысленного изменения среда отвечает. Type errors ловят сломанные контракты. Lint — другой класс проблем. Тесты проверяют поведение, которое типы выразить не могут. Финальный diff все равно проходит человеческий review.
Хитрый промпт может улучшить первую попытку. Сильный цикл проверки улучшает каждую попытку. Для продакшена это намного важнее.
Особенно сильно TypeScript помогает в больших рефакторингах
Маленькие локальные правки просты почти для любого coding assistant. Сложнее, когда изменение расползается на десятки файлов: переименование доменного поля, новая форма API-ответа, ужесточение props компонента, разделение union-типа, замена старой абстракции.
В TypeScript breaking change строит карту затронутых предположений. Компилятор фактически показывает места, где старый контракт все еще используется. Codex может пройти по этому списку, а после каждого прохода код снова проверяется. Это не делает большой рефакторинг автоматически правильным, но делает blast radius видимым.
В слабо типизированной кодовой базе пропущенная зависимость нередко впервые проявляется как баг на экране, который никто не открыл во время разработки.
От чего TypeScript не спасает
TypeScript — guardrail, а не доказательство корректности. Он не предотвращает важные классы ошибок:
- Неверная бизнес-логика: идеально типизированный код все равно может считать не то.
- Runtime-данные: API, БД, форма или внешний сервис могут вернуть данные, не соответствующие compile-time предположениям.
- Недостающий продуктовый контекст: компилятор не знает, зачем существует конкретный flow и какой edge case важен пользователю.
- Ошибки безопасности и архитектуры: валидные типы не гарантируют правильную авторизацию, кеширование, границы БД или инфраструктуру.
Поэтому runtime validation, тесты, логирование, observability и review остаются обязательными. Одна из самых опасных ошибок в AI-assisted development — увидеть чистый typecheck и решить, что фича уже корректна.
Как я провожу AI-assisted изменения
- Даю задаче узкую цель. Что меняем, что не трогаем и по каким критериям считаем работу готовой.
- Перед правкой заставляю Codex изучить проект. Существующие типы, call sites, тесты и соседние модули часто полезнее длинного промпта.
- Предпочитаю минимальное связное изменение. Сфокусированный diff проще проверить, чем «полезное» переписывание соседнего кода.
- Сразу запускаю typecheck и lint. Структурные ошибки лучше увидеть, пока изменение еще маленькое.
- Запускаю релевантные тесты и добавляю их, если меняется поведение. Типы проверяют контракты, тесты — поведение.
- Финальный diff смотрю сам. Имена, архитектура, дублирование, edge cases и главное — решает ли реализация исходную проблему.
Когда JavaScript все еще достаточно
Я не считаю JavaScript плохим. Для небольшого скрипта, одноразового прототипа, простой автоматизации или кода с минимальным shared state дополнительная структура TypeScript может мало что дать. AI также может отлично работать в JavaScript-проекте с сильными тестами и понятными соглашениями.
Мой тезис уже: чем больше и долговечнее кодовая база и чем больше людей ее меняют, тем выше ценность ограничений, которые можно проверить машиной. И ровно в этот момент ошибки AI становятся дороже.
Почему я на самом деле предпочитаю TypeScript вместе с Codex
TypeScript не делает Codex умнее. Он делает среду менее терпимой к плохим догадкам.
В этом и смысл. В продакшене мне нужен не просто «креативный агент», а агент внутри системы, которая постоянно сообщает ему, когда он ошибается. Типы, ошибки компилятора, lint, тесты и ручной review вместе создают такую систему.
Поэтому мой выбор TypeScript + Codex — не фанатизм к языку. Это выбор в пользу быстрой, машиночитаемой обратной связи: меньше неоднозначности, меньше скрытый blast radius, безопаснее рефакторинги и, если реально соблюдать цикл проверки, заметно более надежная скорость разработки.