Bun на Rust: 11 дней, 6 778 коммитов и что на самом деле изменилось
Команда Bun в мае 2026 года сделала необычное: заменила весь нативный core рантайма на Rust — и уложилась в 11 дней.
Bun был куплен Anthropic в декабре 2025 года. Jarred Sumner, создатель Bun, использовал пре-релизную версию Claude — «модель класса Mythos», которую они называют Fable 5 — для большей части работы. Это не мысленный эксперимент. Bun v1.4.0, первая Rust-версия, уже в canary.
Цифры:
- 535 496 строк Zig (без комментариев) превратились в кодовую базу на Rust
- 6 778 коммитов с 3 по 14 мая
- 1 448 файлов
.zigмеханически перенесены в.rs - около $165 000 на API (5.9B незакэшированных входных токенов, 690M выходных)
- пик: 64 Claude одновременно на 4 worktree

Bun всегда был ставкой на Zig
Bun начинался как построчный перенос JS/TS транспайлера esbuild с Go на Zig. Sumner написал свою первую строку на Zig 16 апреля 2021 года, увидев одностраничную справку Zig на Hacker News. Начальная версия — транспайлер, минификатор, бандлер, npm-совместимый пакетный менеджер, Jest-подобный тест-раннер, Node.js API — была написана одним человеком за год, в тесной квартире в Окленде, еще до того как LLM стали пригодны для такой работы.
Ставка окупилась. CLI Bun теперь скачивают больше 22 миллионов раз в месяц. Claude Code и OpenCode используют его как рантайм. Vercel, Railway и DigitalOcean дают первоклассную поддержку.
Но масштаб стал и проблемой.
Долг: GC-память рядом с ручным управлением
JavaScript работает со сборщиком мусора. Bun встраивает JavaScriptCore (движок Safari) плюс кучу C/C++ библиотек: uWebSockets, BoringSSL, SQLite, lsquic. Zig, как и C, не управляет памятью за вас. Bun находится между языком со сборщиком мусора и нативным кодом с ручным управлением — и именно на этой границе жили самые страшные баги.
В анонсе приведен список того, что починили в одном только v1.3.14: heap-use-after-free в node:zlib, use-after-free в node:http2 из-за реентерабельных колбэков, выход за границы массива в UDPSocket.sendMany, утечка памяти на каждый tls.connect, двойное освобождение в CSS-парсере. И это далеко не все.
Sumner явно говорит, что это не вина Zig: «Мы бы не зашли так далеко без Zig, и я всегда буду благодарен». Проблема структурная: работа с lifetime на границе GC сложна на любом языке, который не спроектирован для этого, а подход Zig — явный defer в каждом месте вызова — легко проскочить в редко достигаемых ошибочных путях.
Он рассматривал C++. Около 20% Bun уже на C++, и расширение дало бы конструкторы и деструкторы. Но это все равно опиралось бы на гайдлайны, проверяемые код-ревью — и повреждения памяти все равно случались бы.
Фишка Rust в другом: в safe Rust use-after-free, double-free и «забыл освободить» — это ошибки компиляции. Borrow checker и Drop превращают задачу из «соблюдай гайдлайны» в «проверь типы».
Стратегия: транспилировать, не перепроектировать
Переписывания имеют плохую репутацию, и не зря. Переписывание 535K строк с нуля заморозило бы фичи и исправления на год. Команда выбрала механический порт: та же архитектура, те же цели по производительности, тот же набор тестов — просто на Rust.
Два решения определили весь процесс:
-
Все сразу, не инкрементально. Опыт Sumner по переносу esbuild на Zig показал, что инкрементальные переписывания оставляют временные леса, которые вредят больше, чем помогают.
-
Пусть выглядит как транспилированный. Цель — Rust, который читается как Zig, откуда пришел. Рефакторинг к идиоматичному Rust — уже после выхода v1.4.
Важно: тест-сьют Bun написан на TypeScript, так что ему все равно, на каком языке реализован рантайм. Это позволило считать «все тесты зеленые» определением готовности.
Подготовка была небольшой, но продуманной. Перед тем как писать код, Sumner провел ~3 часа с Claude, сопоставляя паттерны Zig с Rust — этот разговор стал PORTING.md (попал на Hacker News). Второй проход проанализировал правильные Rust-lifetime для каждого поля структур по всей кодовой базе и сериализовал их в LIFETIMES.tsv. Оба документа прошли adversarial review до того, как была написана хотя бы одна строка.
Что осталось нетронутым: JavaScriptCore и встроенные C/C++ библиотеки. Переписыванию подвергся нативный клей и Bun-специфичный код, а не JS-движок.
Исполнение: 64 Claude, 11 дней
Порт выполнялся как около 50 «динамических воркфлоу» в Claude Code непрерывно в течение 11 дней. Каждый воркфлоу был циклом: взять задачу, произвести код, отправить на ревью, применить фидбек.
Интереснее всего модель ревью. На каждого исполнителя приходилось два или более adversarial reviewer — отдельные сессии Claude с задачей «считай что код неправильный и найди каждую причину, по которой он не работает». Исполнитель никогда не ревьюит; ревьюер никогда не пишет код. В анонсе это сравнивается с человеческим ревью: автор хочет смержить, значит проверяет кто-то другой.
Несколько деталей механики:
- Сначала пробный запуск. Перенесли 3 файла, прежде чем браться за все 1,448.
- Worktree-разбиение. Ранние запуски: Claude наступали друг другу на пятки с
git stashиgit reset. Решение: 4 worktree, по 16 Claude в каждом, с правилом «не запускать git или cargo посреди задачи». - Ошибки компиляции как очередь работы.
cargo checkвывалил ~16,000 ошибок в файл, сгруппированных по крейтам — 64 Claude долбили: 16 циклов fix/review/apply на 4 worktree. - Циклические зависимости. Zig был эффективно одной единицей компиляции; Rust потребовал ~100 крейтов. Распутывание циклов породило большинство из тех 16,000 ошибок.
- Изоляция. Стресс-тесты, которые исчерпывают TCP-сокеты или порождают ~10k процессов, запускались под
systemd-runcgroups. Машина все равно пару раз оставалась без диска.
timeline
title Bun: 11-дневный Zig → Rust (Май 2026)
2026-05-03 : Открыта ветка порта (PR #30412)
2026-05-04 : Первая пачка из 100 файлов
2026-05-06 : ~16,000 ошибок компиляции, 64 Claude
2026-05-08 : Первый CI-прогон (972 падающих файла)
2026-05-09 : Linux x64 зеленый
2026-05-11 : Windows зеленый (последняя платформа)
2026-05-14 : Все 6 платформ зеленые, мерж
Цифры: пик 1,300 строк кода в минуту, 695 коммитов за самый активный час (6 мая), 58 коммитов за самую активную минуту. Каждый коммит проверен двумя adversarial reviewer перед мержем. Финальный дифф: +1,009,272 строки. Ни один тест не был пропущен или удален.
CI — вот где стало реально. Через два дня после первого прогона число падающих тестовых файлов упало с 972 до 23. Еще через полтора дня Linux стал полностью зеленым. Windows финишировала последней 11 мая; все шесть платформ были зелеными на билде #54202, 14 мая.
Что реально дал Rust
Заявленная цель — стабильность, и пост подкрепляет ее цифрами.
Баги. Bun v1.4.0 исправляет 128 багов, воспроизводимых в v1.3.14.
Память. Drop заменил defer на каждом месте вызова. Пример из поста: бандлинг одного 60-модульного проекта 2,000 раз в одном процессе. В v1.3.14 каждый билд утекает ~3 MB навсегда; в v1.4.0 память стабилизируется.
| Билдов | v1.3.14 | v1.4.0 |
|---|---|---|
| 500 | 1,914 MB | 526 MB |
| 1,000 | 3,506 MB | 586 MB |
| 1,500 | 5,097 MB | 608 MB |
| 2,000 | 6,745 MB | 609 MB |
Sumner пишет, что предыдущая попытка исправить это в Zig никогда не была смержена — из-за отсутствия аналога Drop было трудно чувствовать уверенность.
Размер бинарника. Одно только переписывание на Rust сократило размер на 3.8 MB (Windows), 5.5 MB (macOS), 6.8 MB (Linux) — в основном за счет избавления от чрезмерного Zig comptime. Дальнейшая работа линкера (схлопывание идентичного кода, обрезка ICU данных) довела общее сжатие до ~20% на Linux и Windows.
| Версия | Платформа | Размер |
|---|---|---|
| v1.4.0 | Windows | 76 MB |
| v1.3.14 | Windows | 94 MB |
| v1.4.0 | Linux | 70 MB |
| v1.3.14 | Linux | 88 MB |
Скорость. Cross-language LTO между C/C++ и Rust позволяет компилятору делать встраивание через границы языков. Bun измерил 2–5% прироста:
Bun.serve: 169.6k → 177.7k req/s (+4.8%)express: 64.5k → 66.6k (+3.2%)next build: 13.62s → 13.03s (+4.5%)tsc -b --force: 0.94s → 0.89s (+4.7%)
Prisma перевел свой Compute public beta на Rust-версию. Alexey Orlenko: «Мы натыкались на утечки памяти и пул соединений, который не восстанавливался после приостановки и возобновления VM. Когда появилась Rust-версия, мы протестировали ее на тех же сценариях — она справилась идеально.»
Claude Code v2.1.181 (17 июня) и новее используют Rust-порт. Запуск на 10% быстрее на Linux. Sumner о видимых пользователю изменениях: «Скучно — это хорошо.»
Где порт споткнулся
Честная транспиляция 535K строк не бесплатна. В посте задокументированы 19 известных регрессий, все исправлены. Показательны мелкие семантические расхождения между языками:
-
Побочные эффекты
debug_assert!. В Zigassert— функция, аргумент выполняется в любой сборке. Rustdebug_assert!— макрос, который стирается в release — вызовinsert_stale, мутировавший HMR-состояние, просто перестал работать. (#30678) -
Срезы нечетной длины. Хелпер Zig игнорировал хвостовой нечетный байт;
bytemuck::cast_sliceпадает на нем.Blob.text()на UTF-16 BOM плюс нечетная длина начал падать. (#31188) -
Проверки границ. Zig
ReleaseFastих убирает; Rust оставляет. Константа-заглушка снизила потолок интернирования имен файлов с 8.4M до 270K — реальные проекты в него уперлись. (#31503) -
Форматные строки
comptime. Zig вычисляет форматные строки во время компиляции, так что цветовые маркеры убираются до подстановки аргументов. В Rust нетcomptime, поэтому парсер маркеров «съел» буквальный обратный слеш в гиперссылках OSC 8. (#30693)
Это баги, которые проявляются только потому, что два языка выглядят одинаково — но не являются таковыми.
Что все это значит
Отбросьте LLM-угол — и останется реальная инженерная история: проект уперся в потолок ручного управления памятью в масштабе и выбрал borrow checker Rust как инструмент, делающий целый класс багов невозможным, а не просто нерекомендуемым.
LLM-угол — вот о чем будут спорить. Sumner честно говорит, что это заняло бы трех инженеров с полным контекстом кодовой базы около года — и они бы никогда этого не сделали. Реалистичная альтернатива — «ничего не делать и продолжать чинить баги». Вместо этого один инженер, наблюдающий за 64 Claude, сделал это за 11 дней за ~$165K.
Около 4% Rust-кода Bun находится в unsafe блоках (~13,000 ключевых слов unsafe на ~27,000 строк из ~780,000 всего), и 78% этих блоков — одна строка. После мержа провели 11 раундов security review и добавили 24/7 coverage-guided фаззинг всех парсеров — 100 миллиардов выполнений на данный момент, ~15 PR.
Вопрос поддерживаемости — за ним я бы следил. Sumner говорит, что Rust читается как Zig, и показывает side-by-side сравнение canMergeSymbols. Идея в том, что честный порт можно проверять порт за портом — именно это позволило одному человеку подписать миллионный дифф.
Его заключительная фраза будет стареть интересно: «Один инженер сегодня может сделать гораздо больше, чем год назад.»