Главная / Статьи

Уроки портирования из Zig в Rust с использованием искусственного интеллекта Bun: верификация — вот что действительно важно.

Чему уроки действий Bun, совершенных при помощи агента из Zig в Rust, учат нас относительно тестовых наборов как контрактов, руководств по портированию, небезопасного кода и того, почему верификация сейчас ограничивает возможности искусственного интеллекта в программировании.

Издатель: REACTAPP.TOP12 мин чтения2857 слов

Источники: Bun Rewrote Its Runtime in Rust With AI. The Real Story Is Bigger Than the Code

Большинство инженерных команд стараются избегать переписывания языка в крупных производственных системах как можно дольше. Сообщается, что Bun — среда выполнения и набор инструментов для JavaScript и TypeScript — перевёл свою реализацию с Zig на Rust, причём большую часть работы по переводу выполнили ИИ-агенты, и цифры в этом отношении впечатляют. Однако более важным является не то, сколько кода сгенерировали агенты, а то, каким образом процесс принял решение о том, какой из этого кода можно считать надёжным. В данной статье рассматривается этот процесс и приводятся практики, которые можно применить при собственных рефакторингах и миграциях с использованием ИИ.

Почему обычно избегают переписывания

Переписывание языка сопряжено с хорошо известным набором рисков. Оно может привести к тонким откатам, которые никто не замечает в течение месяцев, изменить производительность в неожиданных направлениях, сломать платформу, используемую лишь небольшим числом пользователей, и поглотить значительную часть ресурсов команды. В худшем случае команда вынуждена поддерживать старую и новую реализации одновременно, тратя вдвое больше средств на устранение каждой ошибки.

Именно поэтому миграция на Bun привлекает внимание. На момент написания статьи репозиторий проекта описывает Bun как JavaScript-среду выполнения на основе Rust, построенную на JavaScriptCore, а компания Anthropic объявила о покупке Bun в декабре 2025 года. В публичных отчетах указывается, что портинг состоял примерно из 535 000 строк кода на Zig, переведенных за около 11 дней, причем агенты выполняли большую часть работ по реализации в строго определенном процессе.

Хочется воспринять это как ещё один заголовок в духе «Искусственный интеллект написал огромное количество кода». Однако более важный вопрос звучит иначе: может ли система ИИ осуществить кардинальные изменения, если люди создадут надёжный механизм для проверки корректности результата? Именно этот вопрос имеет гораздо большее значение для того, как будет разрабатываться программное обеспечение в ближайшие годы.

Миграция, а не полная переработка

Изначально Bun был написан в основном на Zig — языке системного программирования, ценящемся за возможности низкого уровня и предсказуемую производительность. Миграция не рассматривалась как полный старт с нуля. Её цель была специально ограничена: сохранить существующую архитектуру и наблюдаемое поведение, изменяя лишь язык, на котором написан код.

Именно это решение по форматированию является ключевым в проектировании. Традиционный подход к переписыванию кода побуждает инженеров постоянно вносить изменения: менять структуры данных, перестраивать модули, добавлять абстракции и корректировать API. Каждый из таких выборов создаёт новый источник риска и требует дополнительной проверки. Исключив большинство из этих вариантов, команда Bun превратила задачу с открытыми возможностями для творчества в механическую, имеющую чёткий ориентир — текущую реализацию и тесты, описывающие её.

Количество строк кажется сложной частью работы, но на самом деле это не так. Создание синтаксически корректного кода на Rust относительно просто. Настоящая сложность заключается в обеспечении того, чтобы Rust вёл себя точно так же, как Zig, который он заменяет.

Набор тестов как контракт поведения

Представьте агента, которому дают лишь указание «переписать этот кодовый базис на Rust». Такая инструкция оставляет открытыми все важные решения: какие части могут измениться, а какие должны остаться неизменными? Какие ускорения допустимы? Какое поведение памяти имеет решающее значение? И, что самое главное, откуда кто-то узнает, что результат верен?

Bun уже имел ответ на последний вопрос: большой, зрелый набор тестов. Как только тесты становятся своего рода контрактом, агент больше не придумывает реализацию — он воспроизводит поведение, уже зафиксированное в исполняемом виде, причем каждый создаваемый им файл либо соответствует этому контракту, либо нет.

Что могут и чего не могут обещать тесты

Набор тестов не является доказательством корректности. Он проверяет только те поведения, которые человек планировал зафиксировать в коде, и переведенный файл может пройти все тесты, при этом отличаясь в каком-то аспекте, с которым никто не работал. Тесты же обеспечивают механизм обратной связи, гораздо более надежный, чем просьба к модели самой оценить свой результат. Без них генерированный вариант может казаться совершенно правдоподобным, при этом тихо изменяя семантику. С их помощью каждое генерированное изменение должно заслужить свое место.

В описанной схеме работы Bun было реализовано несколько этапов, расположенных один над другим: компиляция, набор тестов, проверка на поддерживаемых платформах, ревью кода и дополнительная верификация. Модель генерировала кандидатские варианты; инженерный процесс определял, какие из них останутся. Именно такое разделение труда является ключевой идеей.

Почему руководство по портированию было важно настолько же, как и сама модель

До того как агенты начали обрабатывать тысячи файлов, по сообщениям, в рамках проекта был составлен подробный руководство по портированию, описывающий, как структуры Zig следует реализовать в Rust. В нем рассматривались сопоставления типов, правила нумерации, часто встречающиеся шаблоны преобразования, способы обработки зависимостей и четкие ограничения на действия агентов.

Это позволяет заменить расплывчатую просьбу («написать этот файл на Rust») более конкретной: преобразовать файл с использованием установленных правил, сохранить его внешнее поведение без изменений и не трогать архитектуру. В такой среде агенты работают гораздо эффективнее, поскольку набор допустимых решений ограничен и четко определен.

Руководство также облегчает процесс проверки. Вместо неопределенного вопроса «все ли хорошо?» рецензент может задать два конкретных вопроса:

  1. Ведет ли этот код на Rust себя так же, как Zig, который он заменяет?
  2. Соблюдал ли агент правила перевода, согласованные в проекте?

На оба вопроса есть ответы, которые можно проверить, и это именно то, что необходимо в процессе ревью при огромном объеме изменений.

Что включает полезное руководство по портированию

Если вы планируете подобную миграцию, даже в гораздо меньшем масштабе, такое руководство обычно содержит:

  • информацию о том, как каждый тип исходного кода, контейнер и механизм обработки ошибок соответствуют целевому языку
  • правила нумерации, чтобы генерируемый код был единым во всех файлах и агентах
  • список зависимостей, которые можно внедрить, и тех, которые запрещены
  • шаблоны, которых агент не должен использовать, даже если они позволяют скомпилировать файл
  • критерии того, что считается «завершенным» для отдельного файла или модуля

Этот урок актуален не только в случае с Bun: система инструкций для агента может повлиять на качество результата практически так же сильно, как и выбор модели. Команды, уже ведущие файлы с инструкциями для агентов, распознают эту закономерность; наша инструкция по созданию эффективного файла CLAUDE.md охватывает тот же принцип для повседневной работы.

Верный вариант на Rust не всегда является идиоматичным вариантом на Rust

Те, кто ожидает, что портированный код будет выглядеть как разработка с нуля на Rust, будут разочарованы, и это сделано намеренно. Верный порт должен выглядеть так же, как исходный код, с которого он был портирован.

Это приводит к очевидному компромиссу. Zig позволяет кратко описывать некоторые операции низкого уровня, в то время как Rust требует четкого указания типов, времен жизни, прав собственности и границ небезопасного кода. Поэтому компактная функция на Zig может превратиться в значительно более длинную функцию на Rust. Дополнительная длина — следствие оптимизации под поведенческую эквивалентность, а не элегантность, и не является доказательством того, что результат некорректен.

Полезно разделять два утверждения:

  • «Это не тот Rust, который я бы написал с нуля».
  • «Этот код на Rust некорректен».

Первое — это суждение о стиле; второе — дефект. Считать их одним и тем же приводит к необоснованной критике механических переносов кода.

Сначала порт, затем улучшения

В описанном подходе механический перевод и последующая доработка рассматривались как отдельные этапы. Сначала делается так, чтобы Rust вел себя как Bun, а затем улучшается сам Rust. Такое разделение работы означает, что команде никогда не приходится одновременно учитывать два крупных, взаимодействующих изменения. Если во время очистки появляется регрессия, её можно приписать самой очистке, поскольку исходная версия уже проходила проверку на тех же тестах.

Правильное чтение блоков unsafe

Наблюдатели, изучавшие порт, заметили большое количество блоков unsafe и восприняли это как признак низкого качества Rust. Такая реакция понятна, но она игнорирует тот факт, как код вообще оказался в таком состоянии.

В Rust операции, потенциально представляющие опасность, такие как обращение к необработанным указателям или вызов функций из других языков, должны быть явно помечены как unsafe. В Zig отсутствует аналогичный маркер. Исходный код Bun полон низкоуровневой обработки памяти, работы с указателями и вызовов компонентов на C и C++, поэтому в верной трансляции большая часть таких операций автоматически становится unsafe в Rust. Кроме того, Bun включает JavaScriptCore и взаимодействует с другими нативными библиотеками, а граница между кодом на Rust и C или C++ по своей природе требует использования небезопасных операций.

Другими словами, порт не добавил новой опасности; он сделал существующую опасность видимой. Простое подсчёт количества блоков unsafe мало что говорит о качестве кода. Более информативными являются следующие вопросы:

  • Почему именно эта операция должна быть небезопасной?
  • Является ли область с небезопасными операциями достаточно маленькой, чтобы её можно было анализировать?
  • Обеспечивает ли безопасный код вокруг него защиту от неправильного использования со стороны вызывающих функций?
  • Покрываются ли его поведение тестами?
  • Можно ли сузить его функционал или устранить в последующих этапах обработки?
  • Эти вопросы также указывают на путь для этапа оптимизации: сокращать небезопасные участки кода, оборачивать их в безопасные абстракции и документировать инварианты, от которых зависит каждая из них.

    Когда процесс перевода помогает найти улучшения

    Буквальный перенос кода может всё ещё выявить возможности, скрытые в первоначальной реализации. Один из приведённых примеров касался сценария, при котором реализация на Zig обращалась к C++ для получения информации. В Rust соответствующие данные, локальные по потоку, можно было получить более прямым способом, избегнув этого промежуточного вызова.

    Вывод не в том, что Rust превосходит Zig по скорости; такое обобщение не выдержит тщательной проверки. Суть более узкая и полезная: перенос кода между языками заставляет кого-то внимательно рассмотреть каждый путь вызова, и такой анализ иногда выявляет работы, которые вообще не были необходимы. Такие находки заслуживают оценки по отдельным случаям, а не включения в дискуссию о языках.

    Куда сместилась инженерная работа

    Миграция не устранила инженерную работу; она лишь переместила её. В традиционном разработчестве большая часть времени программистов уходит на написание кода реализации. В разработке с помощью агентов больше времени тратится на:

    • определение ограничений и правил перевода
    • проектирование рабочих процессов, в которых работают агенты
    • написание и усиление тестов
    • проверку генерированных изменений
    • диагностику сбоев
  • определение того, какие файлы и системы могут изменяться агентами
  • усиление цикла обратной связи после каждого этапа
  • Инженер отвечает за систему, которая генерирует и проверяет код, а не за каждую отдельную строку кода. В крупном проекте цикл выглядит примерно так: спецификация, затем генерация, затем компиляция, затем тестирование, затем анализ, затем корректировка, и снова с начала. Модель участвует в нескольких из этих этапов, но именно структура цикла определяет, имеет ли результат какую-то ценность. Именно такой рабочий процесс, а не количество строк кода, является настоящим инновационным подходом.

    Почему Bun был хорошим кандидатом

    Не каждая база кода подходит для такого подхода, и у Bun было несколько преимуществ:

    • рабочая реализация, служащая ориентиром
    • известная, стабильная архитектура
    • поведение, которое можно было наблюдать с помощью существующего набора тестов
    • четко определенный целевой язык
    • база кода, которую можно разделить на довольно независимые файлы и компоненты
    • одна измеримая цель: сохранить программу, изменив язык

    Сравните это с задачей по разработке нового продукта, когда отсутствует эталонная реализация, и понятие «правильного» является вопросом мнения. Общее правило заключается в том, что чем точнее можно определить желаемое поведение, тем большую часть реализации можно автоматизировать. Это не означает, что агенты могут безопасно браться за любой крупный проект. Это означает, что автоматизация работает наилучшим образом, когда цель можно измерить.

    Что миграция не доказывает

    Легко злоупотреблять таким результатом, поэтому важно четко указать на его ограничения. Пример с Bun не доказывает, что ИИ может заменить инженеров по программному обеспечению. Он не доказывает, что любая база кода можно преобразовать таким образом, что генерируемый код автоматически будет поддерживаемым, или что Rust всегда быстрее Zig. И он определенно не оправдывает слияние огромных пул-запросов, созданных с помощью ИИ, в обычный код продукта.

    Наоборот, этот пример показывает именно обратное. Работа зависела от строгих тестов, четких правил, верификации, ревью, отладки и постоянного надзора со стороны людей. Анализ опубликованной рабочей процедуры также показывает, что агенты не работали без присмотра; люди контролировали процесс и корректировали его по мере возникновения проблем. Роль инженерного суждения не уменьшилась — она стала основой для всего остального.

    Проверка превращается в узкое место

    На протяжении десятилетий написание кода считалось медленным этапом. Предположим, что агент может сгенерировать 10 000 строк кода за тот же промежуток времени, который раньше требовался разработчику для написания 500 строк. Естественной реакцией становится радость от ускорения, пока не возникает следующий вопрос: кто будет проверять эти 10 000 строк?

    Сегодня генерация кода стала дешевой. Однако подтверждение его корректности по-прежнему требует значительных затрат, причем эти затраты резко возрастают в случае работы с временем выполнения, компиляторами, базами данных, операционными системами и кодом, критичным с точки зрения безопасности, где небольшая ошибка может привести к серьезным сбоям. Это указывает на растущую ценность всех инструментов, предназначенных для проверки, а не только генерации кода: наборов тестов, систем наблюдаемости, статического анализа, методов генерации случайных данных, формальных методов, сред симуляции и анализа под угрозами. Команды, которые получат наибольшую выгоду от агентов для написания кода, скорее всего, будут теми, у кого самые совершенные системы проверки, а не обязательно теми, у кого есть доступ к лучшим моделям.

    Практики, которые можно использовать для собственных изменений с помощью ИИ

    Для применения этих рекомендаций не требуется проект размером с рабочую среду. Они так же эффективны при миграции сервиса между фреймворками или при преобразовании кода на JavaScript в TypeScript.

    Создайте тесты до крупных изменений

    Тщательный набор тестов представляет собой исполняемую спецификацию. Чем больше функций можно проверить автоматически, тем больше работы по реализации можно безопасно передать другим. Если уровень покрытия низкий, его улучшение является первым этапом миграции, а не последующим шагом.

    Запишите правила

    Избегайте запросов вроде «перепишите это». Укажите архитектуру, правила номенклатуры, допустимые и запрещенные зависимости, запретные сокращения, ожидаемые способы обработки ошибок и критерии приемки для каждого элемента работы.

    Разделяйте процессы генерации и проверки

    Агент никогда не должен быть единственным тем, кто оценивает свой собственный результат. Независимый этап проверки, будь то человек, другой агент с иной задачей или оба вместе, позволяет получить новую перспективу и выявить проблемы, которые остаются незамеченными на этапе генерации.

    Пилотирование на небольшом репрезентативном наборе

    Сначала протестируйте процесс на нескольких типичных файлах. Неправильные предположения легко исправить, если они влияют на десять файлов, но становятся крайне сложными для исправления, если они распространяются на десять тысяч файлов.

    Считайте сгенерированный код ненадежным до доказательства обратного

    Код, который компилируется, еще не означает, что он работает. Компиляция — это лишь один из этапов среди многих, а не доказательство корректности.

    Обеспечьте ответственность людей за важные решения

    Агенты могут очень быстро исследовать огромное количество возможных вариантов реализации. Решение о том, что должен делать система и какие риски являются приемлемыми, по-прежнему остается ответственностью человека.

    За пределами дискуссии Zig против Rust

    Хочется представить ситуацию как противостояние Zig и Rust, ручно написанного кода и ИИ или людей и агентов. Ни один из таких подходов не даёт полного объяснения. Порт Bun лучше понимать как эксперимент по тому, как создаётся программное обеспечение, когда стоимость написания кода практически равна нулю. Как только система может переводить сотни тысяч строк кода, центральный вопрос меняется с «можем ли мы это реализовать?» на «можем ли мы создать надёжный процесс, который покажет нам, верна ли реализация?» Это совершенно разные инженерные задачи.

    Как следует оценивать результат

    Первоначальный перевод представляет собой лишь первый этап. Кодовая база, перенесённая механически, со временем может стать более типичной для данного языка: будут проводиться аудиты и сокращение небезопасных участков кода, улучшаться абстракции, настраиваться производительность и удаляться слои совместимости. Как и любая производственная система, в конечном итоге её будут оценивать по тому, подходит ли она реальным пользователям. В исходных материалах указано, что Bun продолжал выпускать новые версии после миграции; для узнания текущего статуса проекта проверьте его репозиторий и чанлог.

    Полноценная оценка будет основана на долгосрочных показателях, а не на объёме переписывания кода:

    • остаётся ли среда выполнения стабильной между версиями
    • становится ли код проще в обслуживании
    • сохраняется ли конкурентоспособность по производительности
    • становятся ли баги, связанные с памятью, реже встречаться
    • могут ли участники проекта понимать и улучшать новый код

    Основные выводы

    • Ценность миграции с помощью Bun заключается в цикле правил, тестов, проверки и корректировок, а не в объеме генерируемого кода.
    • Формулировка переписывания как портирования с сохранением поведения предоставляет агентам измеримую цель, а рецензентам — конкретный чек-лист.
    • Руководство по портированию сужает диапазон приемлемого результата и делает возможной масштабную проверку.
    • Обширный или сильно зависящий от unsafe-кода Rust в верной реализации часто является неизбежной ценой эквивалентности; оценивайте unsafe-код по его границам, а не по количеству.
    • По мере снижения стоимости генерации инвестиции в верификацию становятся тем фактором, который позволяет командам отличаться друг от друга, и проектирование этого цикла обратной связи все чаще становится основой инженерной работы.