Strona główna / Artykuły

Lekcje z przeniesienia projektu Bun na język Rust za pomocą sztucznej inteligencji: weryfikacja to prawdziwa praca

To, czego uczy nas ruch Bun’a przy pomocy agenta z Zig do Rust – dotyczący zestawów testowych jako umów, przewodników portowania, niebezpiecznego kodu oraz tego, dlaczego weryfikacja obecnie ogranicza programowanie przy użyciu AI.

Wydawca: REACTAPP.TOP12 min czytania2857 słów

Źródła: Bun Rewrote Its Runtime in Rust With AI. The Real Story Is Bigger Than the Code

Większość zespołów inżynieryjnych traktuje przepisanie języka w dużym systemie produkcyjnym jako coś, czego należy unikać tak długo, jak to możliwe. Bun, środowisko wykonawcze i zestaw narzędzi dla JavaScript i TypeScript, jak donoszą źródła, przeniósł swoją implementację z Zig na Rust, przy czym większość pracy nad tłumaczeniem wykonały agenty kodujące oparte na sztucznej inteligencji, a uzyskane wyniki są imponujące. Bardziej interesująca jest jednak nie ilość kodu stworzonego przez te agenty, lecz sposób, w jaki cały proces decydował o tym, który z tego kodu można jest zaufać. Ten tekst opisuje ten proces i przekształca go w praktyki, które możesz zastosować we własnych operacjach refaktoryzacji i migracji wspomaganych przez sztuczną inteligencję.

Dlaczego zwykle unika się przepisywań

Zmiana języka programowania wiąże się z typowym zestawem ryzyk. Może ona spowodować subtelne regresje, których nikt nie zauważy przez miesiące, zmienić wydajność w nieoczekiwanych kierunkach, uszkodzić platformę używaną jedynie przez nielicznych użytkowników oraz pochłonąć znaczną część zasobów zespołu. W najgorszym przypadku zespół musi utrzymywać zarówno stary, jak i nowy wariant implementacji równocześnie, płacąc dwa razy za każdą poprawkę błędu.

Dlatego migracja na Bun przyciąga uwagę. W chwili pisania tego tekstu repozytorium projektu opisuje Bun jako środowisko wykonawcze JavaScript oparte na Rust, zbudowane na bazie JavaScriptCore, a Anthropic poinformował o przejęciu Bun w grudniu 2025 roku. Oficjalne raporty podają, że przeniesienie kodu obejmowało około 535 000 linii języka Zig, przetłumaczonych w ciągu około 11 dni, przy czym agenty zajmowały się większością prac implementacyjnych w ramach ściśle określonego procesu.

Jest kuszące potraktowanie tego jako kolejnego tytułu w stylu „Sztuczna inteligencja napisała ogromną ilość kodu”. Lepiej postawić zupełnie inne pytanie: czy system sztucznej inteligencji może wprowadzić bardzo duże zmiany, gdy ludzie stworzą skuteczny mechanizm weryfikacji poprawności wyniku? To pytanie ma o wiele większe znaczenie dla sposobu tworzenia oprogramowania w najbliższych latach.

Port, a nie przebudowa

Bun został początkowo napisany głównie w języku Zig, języku systemowym cenionym za kontrolę na niskim poziomie i przewidywalną wydajność. Migracja nie była traktowana jako nowy początek. Jej cel był celowo wąski: zachować istniejącą architekturę i widoczne zachowanie, zmieniając jedynie język, w którym napisana jest implementacja.

To podejście do projektowania jest kluczową decyzją. Konwencjonalna przeróbka zachęca inżynierów do ciągłego przeprojektowywania – wymieniają one struktury danych, przestawiają moduły, dodają abstrakcje i dostosowują interfejsy API. Każda z tych decyzji stanowi nowe źródło ryzyka oraz coś, co trzeba przeanalizować. Wykluczając większość z nich, zespół Bun przekształcił otwartą, kreatywną zadanie w zadanie mechaniczne z jednoznacznym punktem odniesienia: obecną implementacją oraz testami, które ją opisują.

Liczba wierszy wydaje się trudnym elementem, ale tak nie jest. Tworzenie składniowo poprawnego kodu w języku Rust jest stosunkowo łatwe. Prawdziwy problem polega na zapewnieniu, że Rust będzie zachowywał się dokładnie tak samo jak Zig, który zastępuje.

Zestaw testów jako umowa behawioralna

Załóżmy, że agent otrzymuje jedynie polecenie „przepisz tę bazę kodu na Rust”. To polecenie pozostawia otwarte wszystkie ważne decyzje. Które części mogą ulec zmianie, a które muszą pozostać identyczne? Jakie skróty są do przyjęcia? Które zachowania pamięci mają kluczowe znaczenie? A przede wszystkim, skąd ktoś ma wiedzieć, że wynik jest poprawny?

Bun już miał odpowiedź na ostatnie pytanie: dużą, dojrzałą bazę testów. Gdy testy stają się umową, agent nie musi już wymyślać implementacji – on jedynie odtwarza zachowanie, które zostało już zapisane w formie wykonywalnej, a każdy plik, który tworzy, albo spełnia tę umowę, albo nie.

Czego testy mogą, a czego nie mogą obiecywać

Zestaw testów nie jest dowodem poprawności. Sprawdza jedynie zachowania, które ktoś chciał zaimplementować, a plik przetłumaczony może przejść wszystkie testy, mimo że różni się pod względem ścieżki, której nikt nie wykorzystał. Testy zapewniają natomiast pętlę zwrotną znacznie silniejszą niż prośba o ocenę własnego wyniku przez model. Bez nich wygenerowana wersja może wyglądać całkowicie wiarygodnie, jednocześnie po cichu zmieniając semantykę. Dzięki nim każda wygenerowana zmiana musi uzasadnić swoje istnienie.

Raportowany proces Bun łączył ze sobą kilka etapów: kompilację, zestaw testów, weryfikację na obsługiwanych platformach, przegląd kodu oraz dalszą walidację. Model generował kandydatów; proces inżynieryjny decydował, które z nich przetrwają. To rozdzielenie zadań stanowi kluczową ideę do zapamiętania.

Dlaczego przewodnik portowania był równie ważny jak model

Zanim agenci dotknęli tysięcy plików, projekt rzekomo stworzył szczegółowy przewodnik portowania opisujący, w jaki sposób konstrukcje Zig powinny być wyrażone w Rust. Obejmował on mapowania typów, zasady nazewnictwa, powtarzające się wzorce tłumaczenia, sposób obsługi zależności oraz wyraźne ograniczenia dotyczące tego, co agenci mogli robić.

Cel polega na zastąpieniu niejasnego żądania („napisz ten plik w Rust”) takim, które ma określone ramy: konwertuj plik przy użyciu uzgodnionych mapowań, zachowaj jego widoczne zachowanie w niezmienionej formie i nie ingeruj w architekturę. Agenci radzą sobie znacznie lepiej w takim środowisku, ponieważ zbiór akceptowalnych odpowiedzi jest mały i dobrze określony.

Przewodnik ułatwia również przeglądanie. Zamiast niejasnego pytania „czy to wygląda dobrze?”, recenzent może zadać dwa konkretne pytania:

  1. Czy ten kod napisany w Rust zachowuje się tak samo jak Zig, który zastępuje?
  2. Czy agent postępował zgodnie z zasadami tłumaczenia ustalonymi w projekcie?

Oba pytania mają odpowiedzi, które można sprawdzić, co jest dokładnie tym, czego potrzebuje proces recenzji, gdy ilość zmian jest ogromna.

Co zawiera przydatny przewodnik portowania

Jeśli planujesz podobną migrację, nawet w znacznie mniejszym zakresie, taki przewodnik zazwyczaj określa:

  • sposób, w jaki każdy typ źródłowy, kontener i mechanizm obsługi błędów mapuje się na język docelowy
  • zasady nazewnictwa, aby kod wygenerowany był spójny we wszystkich plikach i przez różne agenty
  • kterze zależności mogą zostać wprowadzone, a które są zabronione
  • wzorce, których agent nie powinien używać, nawet jeśli umożliwiłyby one skompilowanie pliku
  • co stanowi „zakończenie” pracy nad pojedynczym plikiem lub modułem

Lekcja ta ma zastosowanie daleko poza przypadkiem Bun: system instrukcji dla agenta może wpływać na jakość wyniku niemal tak mocno jak wybór modelu. Zespoły, które już posiadają pliki z instrukcjami dla agentów, rozpoznają ten wzorzec; nasz przewodnik pisania skutecznego pliku CLAUDE.md omawia tę samą zasadę w codziennej pracy.

Fałszywie wierny Rust nie zawsze jest idiomatycznym Rustem

Każdy, kto oczekuje, że przetłumaczony kod będzie wyglądać jak projekt napisany od zera w Rust, będzie rozczarowany – i to jest zamierzone. Wierny port powinien przypominać oryginał, z którego został przeniesiony.

To powoduje widoczną kompromisowość. Zig może zwięźle wyrażać określone operacje na niskim poziomie, podczas gdy Rust skłania się ku większej jasności co do typów, czasu trwania, własności oraz granic niebezpiecznego kodu. Dlatego zwięzła funkcja w Zigu może przekształcić się w znacznie dłuższą funkcję w Rust. Ten dodatkowy rozmiar jest skutkiem optymalizacji pod kątem równoważności zachowania, a nie elegancji, i nie jest dowodem na to, że wynik jest błędny.

Pomaga rozróżniać dwie kwestie:

  • ">To nie jest ten Rust, który napisałbym od zera."
  • ">Ten Rust jest błędny."

Pierwsze to ocena stylu; drugie to wada. Traktowanie ich jako jednego rzeczy prowadzi do nieuzasadnionej krytyki prostych adaptacji.

Najpierw adaptacja, potem optymalizacje

Opisany podejście traktowało tłumaczenie mechaniczne oraz późniejszą optymalizację jako oddzielne etapy. Najpierw sprawić, by Rust zachowywał się jak Bun; następnie udoskonalić sam Rust. Taki podział pracy oznacza, że zespół nie musi jednocześnie rozważać dwóch dużych, wzajemnie oddziałujących zmian. Jeśli podczas czyszczenia pojawi się regresja, można ją przypisać temu procesowi, ponieważ bazowy port został już zweryfikowany pod kątem tych samych testów.

Prawidłowe odczytywanie bloków unsafe

Obserwatorzy, którzy przeglądali ten port, zauważyli dużą liczbę bloków unsafe i potraktowali to jako oznakę niskiej jakości kodu napisanego w Rust. Ta reakcja jest zrozumiała, ale ignoruje to, jak kod w ogóle do tego doszedł.

Rust wymaga, aby operacje potencjalnie niebezpieczne, takie jak odwoływanie się do surowych wskaźników lub wywoływanie funkcji zewnętrznych, były wyraźnie oznaczone jako unsafe. Zig nie posiada odpowiedniego znacznika. Oryginalny kod Bun jest pełen manipulacji pamięcią na niskim poziomie, pracy z wskaźnikami oraz wywołań do komponentów w językach C i C++, więc wierny przekład naturalnie przedstawia dużą część z nich jako unsafe w Rust. Ponadto Bun zawiera JavaScriptCore i komunikuje się z innymi bibliotekami natywnymi, a granica pomiędzy kodem w Rust a C lub C++ z natury wymaga operacji niebezpiecznych.

Innymi słowy, przeniesienie nie dodało zagrożenia; sprawiło jedynie, że istniejące zagrożenia stały się widoczne. Prosta liczba bloków unsafe niewiele mówi o jakości. Bardziej informacyjne pytania to:

  • Dlaczego ta konkretna operacja musi być niebezpieczna?
  • Czy obszar niebezpieczny jest na tyle mały, by można było o nim rozważać strategie bezpieczeństwa?
  • Czy bezpieczny kod otaczający to rozwiązanie zapobiega nadużyciom ze strony użytkowników?
  • Czy jego zachowanie jest sprawdzane poprzez testy?
  • Czy można je zawęzić lub usunąć w późniejszym etapie?
  • To pytania dostarczają również naturalnego planu działania na etapie oczyszczania: należy zmniejszyć obszary niebezpieczne, otoczyć je bezpiecznymi abstrakcjami oraz udokumentować niezmienniki, od których każda z nich zależy.

    Gdy tłumaczenie ujawnia możliwości ulepszeń

    Bieżące przeniesienie kodu może nadal odkryć możliwości, które zostały ukryte w oryginalnym projekcie. Jeden z przytoczonych przykładów dotyczył ścieżki kodowej, w której implementacja w Zig wykorzystywała C++ do pobierania informacji. W Rust dane lokalne wątku można było uzyskać bezpośrednio, pomijając tę pośrednią wywołanie.

    Wniosek nie jest taki, że Rust przewyższa Zig pod względem szybkości; takie uogólnienie nie wytrzymałoby dokładnej analizy. Kwestia jest węższa i bardziej przydatna: przenoszenie kodu między różnymi językami zmusza kogoś lub coś do dokładnego przyjrzenia się każdej ścieżce wywołania, a ta analiza czasami ujawnia prace, które w ogóle nie były konieczne. Takie ustalenia wymagają oceny przypadku po przypadku, a nie włączania ich do debaty na temat języków.

    Gdzie skupiły się wysiłki inżynierskie

    Migracja nie usunęła pracy inżynierskiej; jedynie ją przeniosła. W tradycyjnym rozwoju duży udział czasu ludzi poświęca się pisaniu kodu implementacyjnego. W rozwoju z wykorzystaniem agentów większa część tego czasu jest przeznaczana na:

    • definiowanie ograniczeń i reguł tłumaczenia
    • projektowanie ścieżek pracy, w których działają agenci
    • pisanie i udoskonalanie testów
    • przeglądanie generowanych zmian
    • diagnozowanie awarii
  • decydowanie o tym, jakie pliki i systemy mogą być modyfikowane przez agenty
  • wzmacnianie pętli zwrotnej po każdej rundzie
  • Inżynier staje się odpowiedzialny za system, który tworzy i sprawdza kod, a nie za każdą linię samego kodu. W przypadku dużego projektu pętla wygląda mniej więcej tak: specyfikacja, następnie generowanie, kompilacja, testowanie, przegląd, korekta i znowu od początku. Model bierze udział w kilku z tych kroków, ale to projekt pętli decyduje o tym, czy wynik ma jakąkolwiek wartość. To właśnie ten proces pracy, a nie liczba linii kodu, stanowi prawdziwą innowację.

    Dlaczego Bun był dobrym kandydatem

    Nie każda baza kodu nadaje się do zastosowania tej metody, a Bun miał kilka zalet:

    • działająca implementacja służąca jako punkt odniesienia
    • znana, stabilna architektura
    • zachowanie, które można było zaobserwować za pomocą istniejącego zestawu testów
    • język docelowy wyraźnie wybrany
    • baza kodu, którą można podzielić na dość niezależne pliki i komponenty
    • jedyny, mierzalny cel: utrzymanie programu przy zmianie języka

    Porównaj to z prośbą o stworzenie nowego produktu, gdy nie ma referencyjnej implementacji, a to, co jest „poprawne”, zależy od subiektywnej oceny. Ogólną zasadą jest to, że im dokładniej można sformułować pożądane zachowanie, tym większą część implementacji można zautomatyzować. To nie oznacza, że agenci mogą bezpiecznie podejmować się dowolnych dużych projektów. Oznacza to, że automatyzacja działa najlepiej, gdy cel można zmierzyć.

    Czego migracja nie dowodzi

    Latwo jest przesadzać z takim wynikiem, dlatego warto jasno określić jego ograniczenia. Przykład z Bunem nie pokazuje, że sztuczna inteligencja może zastąpić inżynierów oprogramowania. Nie dowodzi też, że każda baza kodu może zostać w ten sposób przetłumaczona, że generowany kod jest automatycznie łatwy w utrzymaniu lub że Rust zawsze jest szybszy od Zig. Z pewnością nie usprawiedliwia również łączenia ogromnych prośb o pull requesty wygenerowanych przez sztuczną inteligencję z zwykłym kodem produktu.

    Wręcz przeciwnie – pokazuje to coś zupełnie innego. Praca polegała na solidnych testach, jasno określonych zasadach, weryfikacji, przeglądzie, debugowaniu oraz ciągłej nadzorze przez ludzi. Analiza opublikowanego procesu pracy wskazuje również, że agenci nie byli pozostawieni sami sobie; ludzie monitorowali proces i dostosowywali go w miarę pojawiania się problemów. Osąd inżynierski nie stracił na znaczeniu – stał się podstawą wszystkiego innego.

    Weryfikacja staje się wąskim gardłem

    Od dziesięcioleci pisanie kodu uważane jest za powolny proces. Załóżmy, że agent może stworzyć 10 000 linii w czasie, jaki kiedyś potrzebował programista na napisanie 500. Oczywistą reakcją jest celebrowanie tej szybkości, dopóki nie pojawia się kolejne pytanie: kto ma sprawdzić te 10 000 linii?

    Tworzenie kodu jest obecnie tanie. Potwierdzenie jego poprawności nadal jest kosztowne, a koszty gwałtownie rosną w przypadku środowisk wykonywania, kompilatorów, baz danych, systemów operacyjnych oraz kodu wymagającego wysokich standardów bezpieczeństwa, gdzie mały błąd może doprowadzić do poważnego awarii. To wskazuje na rosnącą wartość wszystkiego, co służy weryfikacji, a nie tylko generowaniu: zestawów testowych, możliwości obserwacji, analizy statycznej, technik fuzzing, metod formalnych, środowisk izolowanych oraz recenzji przeciwników. Zespoły, które najbardziej skorzystają na agentach do pisania kodu, to prawdopodobnie te z najskuteczniejszymi systemami weryfikacji, a niekoniecznie te z dostępem do najlepszych modeli.

    Praktyki do wykorzystania przy własnych zmianach wspomaganych przez AI

    Nie potrzebujesz projektu o rozmiarze odpowiadającym czasowi działania, aby zastosować te zasady. Są one równie skuteczne przy przenoszeniu usługi między frameworkami lub konwertowaniu bazy kodu JavaScript na TypeScript.

    Zainstaluj testy przed dużą zmianą

    Dokładny zestaw testów stanowi wykonalną specyfikację. Im więcej zachowań możesz sprawdzić automatycznie, tym większą część pracy implementacyjnej możesz bezpiecznie przekazać innym. Jeśli stopień pokrycia jest niski, jego wzmocnienie to pierwszy etap migracji, a nie kwestia drugorzędna.

    Zapisz zasady

    Rozdziel proces generowania od przeglądania

    Agent nigdy nie powinien być jedynym osądzającym swojego wyniku. Niezależna procedura sprawdzenia, czy to przez człowieka, drugiego agenta o innych zleceniach, czy obu, dostarcza nowej perspektywy i ujawnia problemy, których nie dostrzega etap generowania.

    Pilotaż na małej, reprezentatywnej grupie

    Najpierw przeprowadź proces na kilku typowych plikach. Błędne założenia łatwo naprawić, gdy dotyczą dziesięciu plików, ale są bardzo kosztowne, gdy powtórzyły się w dziesięciu tysiącach.

    Traktuj generowany kod jako niewiarygodny, dopóki nie zostanie udowodnione jego poprawność

    Kod, który się kompiluje, to niekoniecznie kod, który działa. Kompilacja to tylko jedna z wielu kontrol, a nie dowód poprawności.

    Zmuszaj ludzi do odpowiedzialności za kluczowe decyzje

    Agenci mogą bardzo szybko badać ogromną liczbę możliwych implementacji. Decyzja o tym, co system musi robić i jakie ryzyka są do przyjęcia, pozostaje obowiązkiem człowieka.

    Poza debatą o Zig kontra Rust

    Kuszące jest przedstawienie tej sytuacji jako konfrontacji Zig z Rust, kodu pisanego ręcznie z AI albo ludzi z agentami. Żadne z tych ujęć nie wyjaśnia zbyt wiele. Port Bun należy lepiej postrzegać jako eksperyment pokazujący, w jaki sposób buduje się oprogramowanie, gdy tworzenie kodu kosztuje prawie nic. Gdy system potrafi przetłumaczyć setki tysięcy linijek kodu, kluczowe pytanie przestaje brzmieć „czy możemy to zaimplementować?” i staje się „czy możemy stworzyć niezawodny proces, który pokaże nam, czy implementacja jest poprawna?”. To zupełnie różne problemy inżynieryjne.

    Jak należy ocenić wynik

    Pierwszy przekład to jedynie pierwsza faza. Bazy kodu przeniesione mechanicznie mogą z czasem stać się bardziej idiomatyczne: obszary niebezpieczne są sprawdzane i ograniczane, abstrakcje ulepszane, wydajność optymalizowana, a warstwy kompatybilności usuwane. Podobnie jak w przypadku każdego systemu produkcyjnego, o jego skuteczności ostatecznie będzie decydować to, czy służy on prawdziwym użytkownikom. Materiały źródłowe wskazują, że Bun kontynuował wydawanie nowych wersji po migracji; aktualny stan projektu można sprawdzić w jego repozytorium oraz dzienniku zmian.

    Prawdziwa ocena będzie wynikać z długoterminowych efektów, a nie z wielkości przepisania kodu:

    • czy środowisko wykonawcze pozostaje stabilne w różnych wersjach
    • czy kod staje się łatwiejszy do utrzymania
    • czy wydajność pozostaje konkurencyjna
    • czy błędy związane z pamięcią stają się rzadsze
    • czy współpracownicy potrafią zrozumieć i ulepszyć nowy kod

    Główne wnioski

    • Wartość migracji Bun polega na cyklu reguł, testów, przeglądania i korekty, a nie na ilości generowanego kodu.
    • Przedstawienie przepisania jako portowania zachowującego funkcjonalność daje agentom mierzalny cel, a recenzentom konkretny wykaz sprawdzania.
    • Przewodnik portowania zmniejsza zakres akceptowalnego wyniku i umożliwia przeglądanie na dużą skalę.
    • Rust o dużej objętości lub silnie zależny od elementów niebezpiecznych w wiernym portowaniu jest często naturalną ceną ekwiwalencji; oceniaj kod niebezpieczny pod kątem jego granic, a nie liczby.
    • W miarę jak generowanie staje się tańsze, inwestycje w weryfikację będą tym, co odróżni zespoły, a projektowanie tego cyklu zwrotnego informacji staje się coraz ważniejszym elementem pracy inżynierskiej.

    Pozycje pokrewne