Da 430.000 righe di TypeScript a Rust: GitHub ha usato agenti AI per riscrivere il runtime di Copilot
Riscrivere da zero il runtime che alimenta CLI, SDK e integrazioni di #GitHub #Copilot (in VS Code, Visual Studio, Excel, Outlook, PowerPoint, #Word) è il tipo di progetto che, in condizioni normali, nessun team ingegneristico si permetterebbe di avviare: centinaia di migliaia di righe di codice in produzione, usate da milioni di sviluppatori, da portare da un linguaggio a un altro senza fermare la nave. GitHub lo ha fatto lo stesso, usando agenti AI per eseguire gran parte del lavoro, e ha pubblicato un resoconto tecnico dettagliato dell’operazione: circa 430.000 righe di #TypeScript convertite in oltre 830.000 righe di #Rust, per un costo in token AI di circa 120.000 dollari e tre settimane di lavoro umano concentrato, distribuite su un progetto durato da metà maggio a fine agosto 2026.
Perché Rust, e perché oraIl runtime agentico di Copilot girava su Node.js/V8: ogni consumer dell’SDK (che fosse la CLI, un plugin IDE o un’integrazione Office) doveva avviare un sottoprocesso e comunicare via JSON-RPC su pipe o socket, pagando un overhead minimo di circa 100 MB di memoria solo per l’avvio della VM V8, oltre alla latenza di marshalling tra processi. Portare il runtime in Rust ed esporlo tramite una C ABI nativa elimina questo overhead: sei linguaggi SDK (C#, Go, #Java, Python, Rust, TypeScript) possono ora ospitare il runtime in-process, tramite bridge nativi diversi per ciascun linguaggio (P/Invoke per C#, purego per Go, JNA per Java, cffi per Python, libloading per Rust, koffi per TypeScript), mantenendo lo stesso protocollo JSON-RPC internamente per non dover duplicare l’intera superficie API (364 route di dispatch) per ciascun linguaggio.
Il risultato più citato è un miglioramento di 15,9 volte su un workload specifico, oltre a un consumo di memoria drasticamente ridotto grazie all’eliminazione dell’overhead V8. Ma il dato più interessante per chi valuta operazioni simili non è il benchmark finale, bensì il come ci sono arrivati.
Metodologia: sostituzione atomica, non big-bangInvece di congelare lo sviluppo per una riscrittura monolitica, il team ha adottato una strategia di sostituzione atomica componente per componente: ogni pezzo di TypeScript portato in Rust veniva sostituito da un thin shim che richiamava il nuovo codice nativo, con il vecchio codice cancellato nello stesso commit. Questo ha permesso di continuare a rilasciare regolarmente durante tutta la migrazione: 135 #release in 14,5 settimane, una media di 1,3 release al giorno, con il 10,5% dei download instradati su canali pre-release per validazione reale prima della promozione a stable.
Il lavoro è proceduto “dalle foglie verso il centro”: prima le utility pure e le funzioni di I/O, poi i sottosistemi stateful, infine l’orchestrazione. Il singolo PR più corposo ha toccato oltre 99.000 righe modificate, concentrato nella fase finale quando la migrazione ha raggiunto i sottosistemi più interconnessi.
Come sono stati orchestrati gli agentiUn solo sviluppatore ha supervisionato l’operazione, coordinando “decine di sviluppatori assistiti da agenti” che univano centinaia di pull request a settimana. La struttura tipica prevedeva sessioni primarie che coordinavano il lavoro, sessioni figlie per porzioni parallele (fino a 15 simultanee) e subagent per compiti di esplorazione mirata (explore, code-review, rubber-duck).
Un esempio citato nel post: la porting del file session.ts (30.000 righe) ha richiesto 25 ore, generando 15 sessioni figlie in sette ondate più cinque subagent, con la sessione padre che ha effettuato 60 poll di stato e inviato 89 messaggi di coordinamento prima di fare cherry-pick e risolvere i conflitti.
Un problema emerso presto: 15 agenti che tentano build simultanee sullo stesso laptop generano stalli da contesa di risorse. La soluzione adottata è stata una sessione di chat dedicata che agisce da “mutex agentico”, concedendo lease di build esclusivi tramite messaggistica cross-session — un pattern di ingegneria distribuita applicato alla gestione di agenti AI concorrenti, non troppo diverso da un lock manager in un sistema distribuito tradizionale.
Testing e code review a scalaLa superficie di test generata è imponente: 468.689 unit test in Rust e 174.675 test end-to-end in TypeScript, con ogni PR di porting validato contro l’intera suite E2E esistente. Un ruolo cruciale lo ha giocato una skill custom chiamata rust-rebase-review, che eseguiva un confronto comportamentale riga-per-riga tra TypeScript e Rust usando più subagent, uno per modello (Opus, GPT-5.6 Sol, Grok 4.6), oltre a normalizzare lo stile Rust (uso di memchr per le ricerche, composizione di trait, riduzione di allocazioni superflue).
Su ogni commit giravano cargo check, cargo test, cargo clippy, cargo fmt e controlli di compatibilità degli schemi. Un dato che ridimensiona un pregiudizio comune su Rust: degli 8.678 errori del compilatore osservati durante il porting, solo l’1,7% era legato al borrow checker — la parte di Rust che genera più discussioni si è rivelata “una presenza silenziosa sullo sfondo”, mentre il grosso degli errori (37%) riguardava semplice risoluzione di nomi e import.
Le regressioni: dove il codice generato dagli agenti ha fallitoIl resoconto è onesto nel documentare le decine di regressioni introdotte durante il porting, categorizzate per tipologia:
Semantica ambigua (~20%): il tipo number implicito di TypeScript, una volta reso esplicito come f64/i64 in Rust, ha causato mismatch di serializzazione (es. 42.0 contro 42)
Comportamenti ambientali (~25%): fuso orario, variabili d’ambiente, working directory e identità del repository che in TypeScript venivano catturati implicitamente prima del porting
Desincronizzazione di operazioni accoppiate (~15%): aggiornamenti di registro senza la relativa cancellazione, stato di task completato ma non propagato nella sessione
Blocco del thread principale (~10%): export napi sincroni che congelavano la UI, risolti rendendo gli export asincroni con offload su thread pool
Problemi lifecycle/ownership (~15%): handle opachi in TypeScript che sopravvivevano alle istanze Rust corrispondenti, hook di dispose che orfanizzavano blocchi di modello
Ogni regressione risolta è stata reincorporata nelle istruzioni standing e nei prompt di Copilot per prevenirne la ricorrenza — un ciclo di feedback che vale la pena replicare in qualsiasi pipeline agentica su larga scala.
Lezioni per chi valuta un’operazione simileAl netto dei numeri, il post di GitHub offre alcune indicazioni pratiche per team che considerano l’uso di agenti AI su refactoring o migrazioni massicce:
Serve intento esplicito: dare nomi diversi a sessioni concorrenti non impedisce tentativi di orchestrazione indesiderata; i confini vanno vietati esplicitamente nei prompt.
Le capacità disponibili vengono usate: aver reso disponibile una skill di orchestrazione ha portato gli agenti a scoprirla e applicarla anche fuori dall’ambito previsto.
Il coordinamento tra pari richiede un arbitro: due sessioni autonome su lavoro adiacente necessitano di un coordinatore designato o di arbitraggio umano.
Lo scope dell’esecuzione autonoma va delimitato con precisione: istruzioni generiche come “non svegliarmi per dettagli” sono state interpretate in modo estensivo, includendo l’annessione unilaterale di lavoro altrui.
Il giudizio umano resta indispensabile per definire i problemi, tracciare i confini, scegliere le strategie e arbitrare le eccezioni: gli agenti hanno gestito l’esecuzione, ma la responsabilità architetturale è rimasta umana.
Un altro dato che ridimensiona l’idea di “riscrittura automatica”: del corpus di 12,76 milioni di eventi raccolti durante il progetto, solo circa 2.600 messaggi erano effettivamente scritti da umani (il resto erano istruzioni di skill, merge automatici e traffico cross-session/subagent). Di questi messaggi umani, il 31% riguardava review, testing e CI, e il 17% consisteva nel mettere in discussione decisioni tecniche o architetturali proposte dagli agenti — a conferma che il ruolo umano si è spostato dalla scrittura di codice alla supervisione critica del lavoro agentico.
Per i team .NET, Java o Python che integrano Copilot SDK, il beneficio pratico è concreto: l’hosting in-process tramite C ABI riduce l’overhead di memoria e latenza rispetto al precedente modello a sottoprocesso, senza richiedere modifiche al codice applicativo che consuma l’SDK.
Fonte originale: The GitHub Blog