Ajouter un commentaire

Comment GitHub a réécrit le runtime Copilot en Rust en utilisant... Copilot ?

Par:
francoistonic

jeu, 24/09/2026 - 16:01

GitHub a publié un long post sur la migration du runtime GitHub Copilot en Rust, en utilisant Copilot. Cela représente plus de 800 000 lignes de code. Que ce soit la CLI, Copilot appl ou encore le SDK, ils reposent sur l'environnement d'exécution de l'agent Copilot. A l'origine, il était écrit en TypeScript sur Node.js et V8. Fondamentalement, l'architecture a peu évolué. Mais avec l'arrivée de l'app et de la CLI, les équipes ont décidé de migrer le code du runtime en Rust. 

"La majeure partie du code a été écrite par des agents, soit 128 demandes de fusion intégrées à la branche principale et déployées progressivement, évitant ainsi une migration finale unique. Les quelques régressions inévitables ont été rapidement détectées et corrigées, tandis que les performances de l'environnement d'exécution ont été considérablement améliorées. Un projet qui aurait nécessité une équipe complète de développeurs pendant un an ou deux avant l'intégration des agents a été réalisé en grande partie par un seul développeur, en quelques mois seulement, tandis que le reste de l'équipe continuait d'étendre significativement les capacités et la portée de l'environnement d'exécution." explique le post.

Pourquoi cette migration ? 

La multiplication des services et des outils Copilot pèse forcément sur le runtime. De nombreuses contraintes ont dicté le choix de Rust, par exemple : un langage ayant un minimum de dépendances, intégrer proprement des processus, avoir un niveau de performance, d'évolution et de fiabilité, un langage interopérable pour supporter facilement les différents SDK (C#, TypeScript, Python, Rust, Go et Java), une sécurité par défaut.

Rust fut donc choisi mais au départ, l'idée n'était pas de tout réécrire en Rust : "Nos exigences privilégiaient l'intégration via une ABI C, une faible surcharge au démarrage et en régime permanent, et une utilisation prévisible des ressources."

Deux tâches essentielles ont été menées :

1/ Séparer le code spécifique à l'interface TUI du runtime, afin que le premier repose entièrement sur le second, et plus précisément sur l’interface publique du SDK. Aujourd’hui, l’interface de ligne de commande (CLI) accède encore directement aux composants internes du moteur d’exécution à plusieurs endroits ; son intégration complète à l’interface publique du SDK est en cours.
2/ Porter cette couche d’exécution en Rust à 100 %, ce qui donne un binaire natif exposant une ABI C pour une utilisation en interne par tous les frontaux du langage, ainsi qu’un serveur basé sur l’entrée/sortie standard ou sur des sockets pour les cas où une exécution hors processus est souhaitée.
130 000 lignes à migrer... dans la 1ere estimation
Un tel chantier ne s'improvise pas. Le plan d'action a été établi en mai 2026. 130 000 lignes TypeScript étaient concernées. Mais la réalité n'était pas celle-ci :
1 / du code lié à la couche TUI n'était pas inclus et il fallait l'intégrer aux codes du runtime
2 / il fallait prendre en compte les évolutions liées aux pull requests, soit un volume de code non négligeable.
Au final, ce sont plus de 430 000 lignes de TypeScript qui devaient être migrées... "This is confused further because there was also incoming Rust code, separate from the port, over the timeframe; early in the porting effort, incoming code was more likely to be dominated by TypeScript, whereas later in the effort, it was more likely to be dominated by Rust.". Bref, on comprend que lancer une migration de cette ampleur ne s'improvise pas et qu'il fallait bien délimiter les codes concernés mais en les 1eres estimations et le code réellement migré a été multiplié par 6 !
Il est intéressant de voir le volume de code au fur et à mesure de la migration. En réalité, il fallait prendre en compte le code TypeScript de production mais aussi tous les tests unitaires TypeScript. On comprend immédiatement que l'estimation de 130 000 lignes n'était pas réaliste car il y avait dès le départ : env. 160/170 000 lignes en production et 250 000 lignes de tests unitaires !
Au final, le projet a généré 830 000 lignes de code en Rust et 469 000 pour les tests unitaires ! "Lors de la migration, l'environnement d'exécution a intégré environ 300 000 lignes de production TypeScript et en a supprimé environ 430 000, tandis qu'environ 1 200 000 lignes de production Rust ont été intégrées et environ 365 000 supprimées. Autrement dit, la stabilité apparente de la ligne TypeScript dans le graphique masquait en réalité une importante activité liée à TypeScript."
A partir de là, l'équipe s'est interrogée sur la stratégie à adopter :
- soit une migration radicale ou big bang : le runtime Rust comme une alternative complète et intégrée quand il est totalement prêt et terminé. Cette approche a deux conséquences :
a. Arrêt complet du développement. Toute activité sur la branche principale est interrompue pendant la réécriture, celle-ci étant effectuée dans la branche principale.
b. Développement parallèle. La réécriture a lieu dans une branche de fonctionnalité tandis que le travail se poursuit sur la branche principale. La réécriture s'efforce constamment de suivre et d'intégrer les modifications de la branche principale.
- soit une migration composant par composant avec réécriture progressive avec réduction de TypeScript. Une des options de cette approche est "plutôt que de supprimer les composants au fur et à mesure de leur portage, les composants TypeScript et Rust sont maintenus comme des options interchangeables à chaud, le composant TypeScript étant supprimé une fois que le niveau de confiance s'est stabilisé."

Finalement, l'option 2 avec suppession progressive des composants TypeScript fut choisie pour plusieurs raisons dont :

- pas d'arrêt de production

- la branche principale est toujours prête à être déployée

- réécriture incrémentale

- des migrations par petits morceaux et relativement autonomes

- tests de bout en bout

Avant de lancer le chantier, un PoC limité a été réalisé : "Avant de nous lancer pleinement, nous avons gagné en confiance et validé nos hypothèses. Nous avons commencé par deux pull requests qui ont établi l'espace de travail Rust, la chaîne d'outils, les règles de lint, l'intégration continue, le pipeline de compilation et les instructions de codage. Nous avons ensuite introduit la bibliothèque d'exécution, la génération de code et les modèles d'interopérabilité, tout en portant une collection de primitives de logique pure, choisies précisément pour leur absence d'E/S et d'état partagé, et parce qu'elles bénéficiaient déjà de tests robustes. Ce n'est qu'après l'intégration de ces primitives que la première pull request de portage majeure a permis de porter trois utilitaires sans effets de bord tout au long du processus."

Interopérabilité : un point crucial

Cette migration devait assurer l'interopérabilité entre TypeScript, Rust et l'ensemble des SDK disponibles :

1 / s'assurer que durant la migration vers Rust, le module concerné TypeScript était toujours accessible et que les fonctions Rust puissent appeler du TypeScript en callback

2 / toutes les librairies des SDK doivent pouvoir s'appuyer sur le runtime et exposer les fonctionnalités. 

Une migration d'un langage à un autre n'était pas uniquement une réécriture. Il a fallu remplacer les bibliothèques utilisées par le runtime. Elles ne dépendent pas de GitHub. Certaines étaient quasi identiques, d'autres nécessitaient plusieurs crates. La migration a permis de supprimer 60 dépendances npm.

Cette migration n'a pas échappé aux régressions. Il est quasi impossible d'avoir un comportement strictement identique sur une telle quantité de code. Les équipes rappellent un axiome simple : porter du code c'est facile, le corriger l'est beaucoup moins : "Au 14 septembre 2026, nous avions recensé des dizaines de régressions connues, toutes corrigées. La plupart étaient des bugs, avec un plus petit nombre de régressions de performance." Il n'y a pas 0 régression et toutes n'ont pas été détectées. 

Le passage à Rust a permis des gains de performance et une consommation RAM moindre.

Au final...

Cette migration représente :

136 milliards de tokens

120 000 $ : c'est le coût estimé pour la partie agentique uniquement

Bien entendu, il a fallu toute une équipe : une dizaine de développeurs pour le cœur du projet, et un nombre non précisé d'autres intervenants pour les pull requests et les approbations. Les leçons tirées de cette expérience

Les leçons tirées de cette expience

1 / il faut un objectif clair et précis, ne pas avoir d'estimations approximatives 

2 / des tests de bout en bout et tout le temps. C'EST INDISPENSABLE. 

3 / prévenir et protéger de toute action de l'agent non prévue ou demandée 

4 / traduire d'abord, redesigner après : il faut préserver le comportement, par exemple sur le code concurrent ou la gestion des droits. Chaque écart peut engrendrer des régressions et des comportements anormaux.

5 / transformer un échec en futur succès : un agent peut dévier, il faut en tirer des leçons. 

Post complet : https://github.blog/ai-and-ml/generative-ai/migrating-the-github-copilot-runtime-to-rust-using-copilot/

Filtered HTML

Plain text

CAPTCHA
Cette question permet de vérifier que vous n'êtes pas un robot spammeur :-)
 Y   Y  BBBB   III  TTTTTT  W     W 
Y Y B B I TT W W
Y BBBB I TT W W W
Y B B I TT W W W
Y BBBB III TT W W