Anti-patrons CI/CD
Les erreurs récurrentes, et pourquoi elles se répètent.
Un anti-patron est une solution qui semble bonne sur le moment mais qui crée des problèmes à long terme. Le gain est immédiat et visible, le coût est différé et diffus : c'est cette asymétrie qui le rend piégeant. On l'adopte pour livrer plus vite aujourd'hui, on le paie en maintenance et en incidents pendant des mois. Appliqués aux pipelines, les anti-patrons ralentissent le retour aux développeurs, compliquent la maintenance, créent des bugs difficiles à reproduire et accumulent de la dette technique.
En bref
Les pipelines les plus problématiques ne sont pas celles qui échouent bruyamment, mais celles qui semblent fonctionner tout en accumulant une dette invisible : temps de construction qui s'allonge, tests instables qu'on ignore, exceptions qui s'empilent. L'objectif de ce cours n'est pas de faire honte, mais de reconnaître des situations vécues.
La pipeline monolithe
Une pipeline monolithe enchaîne toutes les étapes du cycle dans une seule définition, du style au déploiement en production. Chaque exécution paie le coût de l'ensemble, même quand la modification ne concerne qu'une étape, et une défaillance sur n'importe quel maillon bloque tout le reste.
Symptôme
Une seule pipeline fait tout : style, tests, construction, analyse de sécurité, déploiement sur tous les environnements. Elle dure 45 minutes. Personne n'ose y toucher.
| Problème | Impact concret |
|---|---|
| Retour lent | 47 minutes pour apprendre qu'une faute de frappe casse la construction |
| Fragilité | un changement dans une étape peut casser toutes les autres |
| Pas de parallélisation | les étapes s'exécutent une par une, même quand elles sont indépendantes |
| Débogage difficile | en cas d'échec, il faut chercher dans 500 lignes de configuration |
Quatre symptômes, une seule cause : l'absence de découpage.
La correction consiste à découper en pipelines indépendantes, chacune avec un objectif unique, capable d'échouer sans bloquer les autres.
Découpée, la chaîne signale un défaut de style en deux minutes au lieu de quarante-sept.
Pipeline CI (5 min) vérifie que le code est correct
├── lint style du code
├── tests unitaires fonctions prises isolément
└── build compilation de l'application
Pipeline CD-staging (3 min) déploie pour les tests
├── deploy staging
└── tests e2e exerce l'application via son interface
Pipeline CD-prod (2 min) déploie pour les vrais utilisateurs
├── deploy production
└── smoke tests vérifie les fonctions essentiellesLa pipeline non déterministe
Une pipeline déterministe donne toujours le même résultat avec les mêmes entrées. La phrase révélatrice de l'inverse : « relance, ça va passer ».
Exécution 1 : succès
Exécution 2 : échec (délai de test dépassé) pourquoi maintenant ?
Exécution 3 : succès
Exécution 4 : échec (dépendance introuvable)
Exécution 5 : succès rien n'a changé pourtant| Cause | Explication |
|---|---|
| Dépendances non épinglées | npm install récupère la dernière version, qui a pu changer depuis hier |
| Tests instables | tests dépendant de l'ordre d'exécution, du minutage ou de données externes |
| Ressources partagées | plusieurs tests utilisent la même base de données et se marchent dessus |
| Limitation de débit | les API externes bloquent les requêtes trop fréquentes |
Le message d'échec oriente : « module not found » pointe les dépendances, un délai dépassé intermittent les tests instables.
| Action | Pourquoi elle aide |
|---|---|
| Épingler toutes les dépendances | le fichier de verrouillage garantit les mêmes versions à chaque exécution |
| Isoler les tests | chaque test crée ses propres données, aucun conflit possible |
| Simuler les API externes | on n'appelle plus de vrais services |
| Corriger les tests instables | les ignorer ne fait que repousser le problème |
La première action rapporte le plus vite : elle tient en un changement de commande.
{Une commande de différence, tout le déterminisme en jeu.}
# À ÉVITER : recupere les dernieres versions disponibles
- run: npm install
# CORRECT : installe exactement les versions de package-lock.json
- run: npm ciRelancer un test instable n'est pas le réparer
Beaucoup d'équipes « résolvent » les tests instables en les relançant automatiquement jusqu'à ce qu'ils passent. Le test finit vert, mais le défaut qu'il signalait est toujours là : vous avez désactivé l'alerte, pas corrigé la cause. Le jour où le même aléa frappe en production, plus aucun signal ne prévient.
Le copier-coller sans gouvernance
Une pipeline qui fonctionne se duplique de projet en projet, puis diverge au gré des ajustements locaux. Chaque copie devient un exemplaire unique, sans source commune : une correction ne se propage plus, et un défaut recopié se répand avec le modèle.
projet-a/.github/workflows/ci.yml (version d'origine)
projet-b/.github/workflows/ci.yml (copiée, modifiée)
projet-c/.github/workflows/ci.yml (copiée de B, re-modifiée)
projet-d/.github/workflows/ci.yml (copiée de C, avec les défauts de C)| Problème | Conséquence concrète |
|---|---|
| Incohérence | un projet scanne les vulnérabilités, un autre non, sans raison valable |
| Maintenance impossible | une faille dans la pipeline : cinquante fichiers à corriger à la main |
| Dérive | les copies divergent, personne ne sait plus laquelle fait référence |
| Pas de capitalisation | une équipe améliore sa pipeline, les autres n'en profitent pas |
La dérive est le mécanisme silencieux qui rend la correction si risquée.
La correction : centraliser la logique dans un workflow réutilisable
(workflow_call sur GitHub Actions) ou un modèle partagé. Une seule
définition fait autorité, chaque projet la référence par version, et une
correction s'y propage sans copier-coller.
{Deux cents lignes copiées deviennent cinq lignes d'appel.}
jobs:
ci:
uses: org/shared-workflows/.github/workflows/ci.yml@v1
with:
language: python
python-version: "3.11"La reconstruction à chaque environnement
L'artefact est reconstruit pour chaque environnement. La phrase révélatrice : « ça marchait en staging ! ». Le principe et sa correction sont détaillés au chapitre ce qui compte ici, c'est de savoir reconnaître le symptôme.
| Problème | Exemple concret |
|---|---|
| Non-reproductibilité | l'artefact testé en staging n'est pas celui déployé en production |
| Bugs fantômes | un défaut apparaît en production alors que « ça marchait en staging » |
| Temps perdu | chaque déploiement recompile tout, même quand rien n'a changé |
Le « ça marchait en staging » est littéralement vrai : vous avez testé un artefact, vous en déployez un autre.
{Un artefact unique, identifié par son empreinte, promu tel quel.}
build:
npm run build
docker push app:abc123 # artefact unique
staging:
docker pull app:abc123 # exactement le meme artefact
ENV=staging deploy # seule la configuration change
production:
docker pull app:abc123 # toujours le meme artefact
ENV=production deploy # seule la configuration changeLes tests en fin de chaîne
Placer les tests les plus coûteux à la toute fin retarde le signal d'échec jusqu'après la construction et le déploiement. Le développeur apprend la régression longtemps après avoir changé de sujet, et la remise en contexte coûte souvent plus cher que la correction.
lint (1 s) -> build (3 min) -> deploy (2 min) -> tests e2e (15 min)
|
+-- échec détecté
après 20 minutes| Problème | Impact sur le développeur |
|---|---|
| Retour tardif | vingt minutes pour découvrir qu'un défaut simple casse tout |
| Gaspillage de ressources | construction et déploiement exécutés pour rien |
| Perte de contexte | il a commencé autre chose, il doit se replonger dans le code |
Placer les tests lents en dernier ne gêne pas quand ils passent — seulement quand ils échouent.
La correction est la pyramide des tests, vue au chapitre : les tests rapides d'abord, les lents ensuite, et seulement si les premiers passent.
lint (1 s) --+-- tests unitaires (30 s) --+-- build (3 min)
| |
+-- échec rapide +-- tests e2e (seulement si tout passe)La dépendance à l'environnement
La pipeline fonctionne sur un runner et échoue ailleurs : c'est le « ça marche sur ma machine », version intégration continue.
Runner A (ubuntu-20.04) : succès
Runner B (ubuntu-22.04) : échec même code, résultat différent
Runner C (macOS) : échec| Cause | Exemple concret |
|---|---|
| Outils non installés | la pipeline utilise jq, installé sur un runner et pas sur les autres |
| Versions implicites | python lance 3.8 sur un runner, 3.11 sur un autre |
| Chemins en dur | /home/runner/specific/path n'existe pas partout |
| Variables d'environnement | définies sur un runner, absentes ailleurs |
Quatre choses que la pipeline suppose au lieu de les déclarer — les versions implicites sont les plus sournoises.
| Solution | Pourquoi elle fonctionne |
|---|---|
| Conteneuriser | l'image embarque tous les outils, identiques partout |
| Expliciter les versions | python-version: "3.11" garantit la même version sur tous les runners |
| Éviter les chemins absolus | $GITHUB_WORKSPACE s'adapte au runner |
| Installer les prérequis | si un outil est nécessaire, l'installer explicitement |
Règle d'or : ce qui n'est pas explicitement défini dans la pipeline peut changer sans prévenir.
{Environnement contrôlé, permissions minimales, action épinglée par empreinte.}
permissions: {} # aucun droit par defaut
jobs:
test:
runs-on: ubuntu-24.04
permissions:
contents: read # le strict necessaire
container: python:3.11-slim # environnement controle
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- run: pip install -r requirements.txtLes exceptions permanentes
Une dérogation accordée pour débloquer une livraison n'a presque jamais de date de fin. Faute d'échéance et de réexamen, elle survit à l'urgence qui l'a justifiée, et le périmètre réellement contrôlé se réduit sans que personne ne l'ait décidé.
Exceptions au scan de sécurité :
- projet-legacy (temporaire, ajouté en 2022) toujours là
- projet-urgence (temporaire, ajouté en 2023) toujours là
- projet-client-X (temporaire, ajouté en 2023) toujours là| Problème | Risque concret |
|---|---|
| Fausse sécurité | les tableaux de bord sont verts, mais les projets exemptés passent sous le radar |
| Précédent dangereux | « eux ont une exception, pourquoi pas nous ? » |
| Dette invisible | personne ne sait combien de projets sont exemptés, ni pourquoi |
Vous croyez couvrir cent pour cent du parc vous en couvrez peut-être soixante.
| Mesure | Comment l'appliquer |
|---|---|
| Date d'expiration | chaque exception expire automatiquement après 90 jours |
| Justification documentée | pourquoi cette exception, et quel est le plan pour la supprimer ? |
| Revue régulière | audit trimestriel : les exceptions non renouvelées disparaissent |
| Alternative au contournement total | ignorer une vulnérabilité précise plutôt que le scan entier |
La date d'expiration inverse la charge de la preuve : il faut désormais justifier le renouvellement.
Plutôt que d'exempter un projet
Configurez le scan pour ignorer une vulnérabilité identifiée, avec sa justification : « CVE-2023-XXXX ignorée car non applicable à notre usage — aucune entrée utilisateur sur ce chemin ».
La pipeline à point unique de défaillance
Une seule pipeline, sur un seul runner, avec une seule personne qui la comprend. Si l'un de ces éléments tombe, tout s'arrête.
| Point unique | Parade |
|---|---|
| Runner unique | constituer un groupe de runners, deux ou trois au minimum |
| Mainteneur unique | documenter la pipeline, former au moins une autre personne |
| Absence d'alerte | notifier quand un runner devient indisponible |
Le point unique le plus souvent oublié n'est pas une machine : c'est une personne.
Audit et synthèse
Liste d'audit, un anti-patron par ligne
-
Retour rapide — moins de 15 min pour le premier signal.
-
Déterminisme — plus aucun « relance, ça va passer ».
-
Gouvernance — des modèles centralisés, pas du copier-coller.
-
Construction unique — un artefact construit une seule fois.
-
Pyramide des tests — les tests rapides s'exécutent en premier.
-
Exceptions traçables — aucune exception sans date d'expiration.
-
Pas de point unique — plus d'une personne connaît la pipeline.
Traitez d'abord le retour rapide et le déterminisme : ce sont les deux points qui libèrent le plus de temps pour l'équipe. Refaites cet audit chaque trimestre — les anti-patrons s'installent progressivement.
À retenir de cette séance
Un anti-patron crée plus de problèmes qu'il n'en résout. La pipeline monolithe ralentit le retour : découpez. La pipeline non déterministe érode la confiance : épinglez. Le copier-coller multiplie la maintenance : centralisez. Construisez une fois, déployez partout. Les tests rapides d'abord : échouer en trente secondes plutôt qu'en vingt minutes. Les exceptions temporaires deviennent permanentes : datez-les. Éliminez les points uniques, machines comme personnes.
Réponse unique
Une pipeline échoue une fois sur cinq sans qu'aucun code n'ait changé. De quel anti-patron s'agit-il ?
Indice · Même entrée, résultat différent : comment appelle-t-on cela ?
[ METTRE EN PRATIQUE ]
Diagnostiquer trois anti-patrons
3 étapes · 30 min · 120 XP
[ CETTE LEÇON DANS LES PARCOURS ]