120 projets à migrer. Seul. La dernière fois qu’on avait dû faire ça, on s’était promis de ne plus jamais le refaire.
La dernière fois
Il y a deux ans, on avait déjà dû faire une migration de pipelines CI à l’échelle du département. Un template maison à mettre à jour, une évolution structurelle. On avait fait la seule chose qui semblait possible : communiquer. Demander à chaque responsable de projet de faire la mise à jour. Certains l’ont fait dans la semaine. D’autres jamais. Résultat : un drift entre les projets à jour et les projets à la traîne, du support pendant des mois, et des « Ah, il fallait migrer ? » à chaque incident.
Après ça, on s’était promis de ne plus jamais reproduire ce scénario.
Deux ans plus tard, nouveau workflow de CI standardisé, nouveaux templates Python, nouvel outil de création de projets. Plusieurs milliers de projets dans le département. 120 à migrer.
Un humain, 120 projets
Quand le sujet est arrivé, la question n’était pas « qui va s’en occuper ». L’équipe avait d’autres priorités. C’est tombé sur moi. Seul.
Déléguer aux mainteneurs de chaque projet n’était pas une option. On savait exactement comment ça finirait. La seule alternative, c’était de ne pas faire la migration du tout. Laisser les projets sur l’ancien template, accepter le drift, et gérer les problèmes un par un quand ils surgiraient.
Sauf que cette fois, j’avais quelque chose que je n’avais pas deux ans plus tôt : un agent IA, des skills sur mesure, et une idée de comment orchestrer le tout.
Construire les briques
L’idée : pour chaque étape de la migration, construire un outil spécialisé, le tester, l’intégrer dans une chaîne. Recommencer pour l’étape suivante.
Le pattern a été à peu près le même pour chaque phase :
1. Brainstormer l’approche avec l’IA. Je décrivais le problème, elle proposait des pistes. On discutait, on ajustait. (J’ai un skill que j’utilise régulièrement pour ca)
2. Déterminer l’input et l’output. Pour la phase d’identification des projets, l’input, c’est les IDs GitLab (ou le path du projet). L’output, un fichier jsonl avec les projets trouvés et leur statut. Pour la phase de classification, l’input, c’est ce même jsonl. L’output, le même fichier enrichi du type de migration nécessaire.
3. Créer un script et un skill. Une fois l’approche validée, j’ai fait écrire le code par l’IA. Pas un script jetable, un skill réutilisable, avec des commandes dédiées.
4. Tester à petite échelle. Un pour chaque cas identifié. Je m’assurais d’avoir les comportements attendu pour les différents cas ou paramètres avant de valider le skill.
5. Intégrer à l’orchestrateur. Le skill était intégré ensuite dans la chaîne principale, et on passait à la phase suivante.
Au total, j’ai identifié quatre cas d’upgrade possibles, quatre patterns de modification différents selon ce que le projet utilisait. L’IA a analysé chaque cas et m’a aidé à déterminer la meilleure façon de le traiter.
Cette phase de construction a pris un peu de temps. Le plus long, ce n’est pas d’écrire le code. C’est de comprendre exactement ce qu’il faut faire, et dans quel ordre. L’IA ne peut pas le deviner. C’est moi qui ai dû modéliser le processus.
Pour les curieux : j’ai utilisé OpenCode avec Claude Sonnet 4.6 pour le gros du travail de conception et d’analyse. Pour l’exécution pure de certains skills, une fois le pattern bien rodé, un modèle plus léger suffisait. Au début, je passais par le MCP GitLab pour prototyper rapidement. Ça m’a permis d’itérer vite sans écrire de code. Une fois l’approche validée, j’ai basculé sur du scripting direct via l’API GitLab, avec quelques précautions pour ne pas surcharger une API un peu sensible : des temps de pause entre les appels, une limite de parallélisation.
L’orchestrateur
Une fois les briques prêtes, il fallait les assembler. J’ai construit progressivement un skill orchestrateur qui les appelait dans l’ordre.
Le fonctionnement repose sur le fichier jsonl qui sert de colonne vertébrale. Chaque ligne représente un projet, avec son statut, l’étape en cours, et les informations accumulées au fil du processus.
{"id": "projet-142", "statut": "identifié", "cas": "type-3"}
{"id": "projet-142", "statut": "classifié", "cas": "type-3", "mainteneur": "jean.dupont"}
{"id": "projet-142", "statut": "migré", "cas": "type-3", "mr": "https://gitlab..../merge_requests/42"}
Ce jsonl, c’est mon filet de sécurité. Si quelque chose plante, je sais exactement où j’en suis. Je peux reprendre le processus sans recommencer de zéro. Et en cas d’erreur, je peux tracer ce qui s’est passé projet par projet.
Au départ j’ai privilégié un fichier distinct pour chaque étape afin de contrôler encore plus l’évolution, mais à l’usage ca s’est avéré inutile et plus complexe.
L’orchestrateur a plusieurs modes. Un mode DRY-RUN qui simule tout sans rien modifier. Un mode reprise qui repart du jsonl existant, avec éventuellement un path de projet à partir duquel reprendre. Et un mode reset pour tout recommencer. C’est accessible via des commandes slash que j’ai définies dans le répertoire .opencode du projet.
Le process complet tient en sept étapes :
- Recherche et sélection des projets à migrer
- Identification du cas d’upgrade parmi les quatre possibles
- Identification du mainteneur basée sur le nombre de commits récents
- Commit et push de la mise à jour
- Création de la merge request
- Sélection du reviewer selon le mainteneur identifié
- Vérification du statut de la pipeline
Chaque étape met à jour le jsonl. À tout moment, je sais combien de projets sont traités, combien restent, et où ils en sont.
Le tableau de bord
Quand l’orchestrateur a été fonctionnel, j’ai ressenti le besoin de visualiser l’ensemble. Un fichier jsonl de 120 lignes, c’est bien pour un programme. Pour un humain qui veut voir d’un coup d’œil où en est la migration, c’est moins parlant.
J’ai demandé à l’IA de me générer un dashboard web. Cette fois, je suis passé en mode « vibe coding », un one-shot sans réutilisation prévue. J’ai décrit ce que je voulais voir, l’IA a pondu une première version, et on a itéré.
Au final, le dashboard affiche des KPIs :
- Le nombre total de projets identifiés
- Le pourcentage de réalisation
- La répartition par cas d’upgrade
Et le détail par projet :
- Les liens utiles
- Le cas identifié
- Le statut de chaque merge request (créée, validée, mergée)
- Le reviewer
- Le statut de la pipeline
Ce dashboard, je l’avais en permanence sous les yeux le temps de la migration. Il m’a évité de perdre le fil. À 120 projets, sans visualisation, le risque de perdre de vue un projet est réel.
J’ai également poussé le vice en rajoutant un skill à l’orchestrateur permettant de générer un récapitulatif succinct en commentaire de l’issue Gitlab afin que toute l’équipe ait de la visibilité sur l’évolution de la migration.
Ce qui a foiré
Parce que oui, tout ne s’est pas passé comme prévu.
Les tokens cramés en exploratoire. Au début, je ne savais pas exactement comment structurer le processus. J’ai tâtonné, j’ai testé des approches qui n’ont pas marché, et j’ai consommé pas mal de tokens avant de trouver la bonne méthode. Ce n’est pas un échec en soi, c’était de l’exploratoire pur, impossible à éviter, mais un peu coûteux.
Les merge requests dupliquées. À un moment, j’ai mis à jour certains critères de migration. Sauf que certains projets déjà traités se sont retrouvés avec une deuxième merge request, identique à la première. L’erreur était bête : j’aurais dû préciser explicitement de mettre à jour les MR existantes et l’IA en a créé de nouvelles. Le jsonl m’a permis de repérer les doublons rapidement et de fixer la petite dizaine de projets impactés.
Les branches non-default. Ma première version de l’orchestrateur ne regardait que la branche principale. Problème : certains projets avaient des branches actives avec l’ancien template CI. J’ai dû ajouter une étape d’identification des branches à migrer. (On a un public varié avec des gens qui ne connaissent pas les rebases et autres subtilités techniques)
Le tiers mergé sans validation. En observant le flux, j’ai remarqué qu’environ un tiers des projets ne justifiaient pas une revue par le mainteneur. Les changements étaient purement mécaniques, identiques d’un projet à l’autre et sans impact direct. J’ai pris la décision de les merger directement. Pas de foirage ici, c’était juste un affinage du processus basé sur l’observation.
Et oui, un agent IA ne comprend pas ce qu’il fait. Il exécute. C’est à l’humain de détecter les angles morts et de corriger le tir.
Ce que l’IA n’a pas fait
L’IA a fait le gros œuvre. Mais il y a des choses qu’elle n’a pas touchées, et ce n’est pas une limite. C’est le vrai boulot.
Les vérifications à l’élaboration des skills. Avant d’intégrer un skill à l’orchestrateur, je le testais manuellement sur un petit échantillon. Je vérifiais que le code généré faisait exactement ce que j’attendais. L’IA produit du code plausible. Parfois, il est correct. Parfois, il est plausible et faux. La différence, c’est la vérification humaine.
La contre-analyse de la liste des projets. Une fois la phase d’identification terminée, j’ai repris la liste à la main. Je voulais être sûr de ne pas avoir oublié un projet. L’IA avait bien travaillé, mais l’humain qui vérifie, c’est la dernière ligne de défense.
La relecture et le merge manuel de chaque MR. Ca m’a pris deux bonnes heures. Chaque merge request, je l’ai ouverte, checkée, et mergée moi-même. Pas par manque de confiance, par responsabilité. L’IA est un outil. Ce que je lui fais faire, c’est moi qui en réponds.
Ces deux heures n’étaient pas une contrainte. C’est le cœur du travail. L’IA a exécuté des centaines d’opérations mécaniques que je n’aurais jamais eu le temps de faire. Moi, j’ai fait ce qu’une IA ne peut pas faire : vérifier, juger, décider.
C’est aussi là que j’ai repéré des choses que l’orchestrateur ne voyait pas. Quelques pipelines qui échouaient pour des raisons différentes. Quelques conflits mineurs. Pas assez nombreux pour justifier d’automatiser leur résolution, mais suffisants pour mériter un coup d’œil humain.
Ce que ça change
La migration est terminée. Les 120 projets sont à jour, tracés, vérifiés.
Si je n’avais pas eu l’IA, qu’est-ce qui se serait passé ? Rien. La migration n’aurait pas eu lieu. Pas parce qu’elle était techniquement infaisable. Parce qu’elle n’était pas réaliste par rapport à nos autres priorités.
L’IA agentique n’a pas seulement rendu cette migration plus rapide. Elle l’a rendue réaliste. Combien d’autres taches similaires traînent dans un fond de backlog parce que ca n’a pas assez de valeur en comparaison de l’effort demandé pour des humains sans IA ?
Et puis ce découpage en briques indépendantes, ce n’est pas juste un one-shot. Avec un peu d’adaptation, certaines skills devraient resservir pour une prochaine migration. À moins que d’ici là, l’IA ait encore tellement progressé qu’il faille à nouveau changer de paradigme. Ce qui est certain, c’est que la question ne se posera plus de la même manière.
La dernière fois qu’on avait dû faire une migration comme celle-ci, on s’était promis de ne plus jamais recommencer. Cette fois, je n’ai pas eu à choisir entre ne pas la faire et y laisser mon énergie.
