Les formes modernes de pipelines CI/CD
Mise à jour :
Imaginez deux restaurants. Le premier est un petit bistrot familial : une cuisine, un chef, une carte simple. Le second est une chaîne avec 50 établissements : cuisines centrales, logistique complexe, menus standardisés. Les deux font de la restauration, mais leur organisation n’a rien à voir.
C’est pareil pour les pipelines CI/CD. Une startup avec trois développeurs et un seul service n’a pas besoin de la même architecture qu’une banque avec 200 équipes et des centaines de microservices. Il n’existe pas de « meilleure » pipeline — seulement celle qui correspond à votre contexte.
Ce guide présente les grandes familles d’architectures de pipelines et leurs compromis. L’objectif : vous donner les clés pour choisir (ou faire évoluer) la vôtre.
Avant de commencer : de quoi parle-t-on ?
Quand on parle d’« architecture de pipeline », on parle de comment organiser l’ensemble de vos pipelines — pas du contenu d’une seule pipeline. Les questions clés sont :
- Combien de pipelines avez-vous ? Une seule pour tout ? Une par service ? Une par branche ?
- Comment se déclenchent-elles ? À chaque commit ? Seulement sur certaines branches ?
- Comment les artefacts circulent-ils ? Reconstruits à chaque étape ? Promus d’un environnement à l’autre ?
Ces choix dépendent de trois facteurs principaux :
| Facteur | Questions à se poser |
|---|---|
| Structure d’équipe | Combien de développeurs ? Combien d’équipes ? Travaillent-elles sur le même code ? |
| Stratégie de branches | Utilisez-vous GitFlow avec plusieurs branches longues ? Ou tout le monde travaille sur main ? |
| Modèle de déploiement | Déployez-vous une fois par mois ou 50 fois par jour ? Avez-vous plusieurs versions en production ? |
Stratégies de branches : comment organiser votre code
La façon dont vous organisez vos branches Git influence directement la structure de vos pipelines. Deux grandes philosophies s’opposent.
Pipeline par branche (approche GitFlow)
Le principe : vous maintenez plusieurs branches « longues » qui vivent en parallèle pendant des semaines ou des mois. Chaque branche a un rôle précis et sa propre pipeline.
Analogie : c’est comme une usine avec plusieurs chaînes de production
parallèles. Une chaîne fabrique les prototypes (branche develop), une autre
prépare la prochaine version (branche release/1.2), une dernière produit ce
qui part en magasin (branche main).
Comment ça fonctionne concrètement :
- Les développeurs travaillent sur des branches
feature/*(nouvelles fonctionnalités) - Ces branches sont mergées dans
develop— une branche d’intégration - Quand on prépare une release, on crée une branche
release/X.Y - Après validation, la release est mergée dans
main(production) - Chaque branche longue a sa propre pipeline avec des comportements différents
Exemple de configuration :
| Branche | Ce que fait la pipeline |
|---|---|
feature/* | Tests unitaires, lint, analyse statique |
develop | Tests + déploiement en environnement de développement |
release/* | Tests complets + déploiement en staging + tests d’acceptance |
main | Déploiement en production (souvent avec approbation manuelle) |
Avantages :
- Séparation claire : chaque environnement correspond à une branche, c’est facile à comprendre
- Contrôle fin : vous décidez exactement ce qui va en production et quand
- Versions parallèles : vous pouvez maintenir plusieurs versions en production (1.0, 1.1, 2.0)
Inconvénients :
- Complexité : plusieurs pipelines à maintenir, qui peuvent diverger
- Merges douloureux : plus les branches vivent longtemps, plus les conflits sont nombreux
- Délai : entre l’écriture du code et sa mise en production, il peut se passer des semaines
Contexte idéal : équipes avec des cycles de release planifiés (tous les mois, tous les trimestres), produits avec plusieurs versions supportées en parallèle (logiciels installés chez les clients, applications mobiles).
Trunk-based development : tout sur une seule branche
Le principe : tout le monde travaille directement sur la branche principale
(main ou trunk). Les branches de fonctionnalité existent, mais elles sont
très courtes — quelques heures, pas quelques jours.
Analogie : c’est comme un journal quotidien. Chaque journaliste écrit son article, le fait relire rapidement, et il part à l’impression le jour même. Pas de « numéro spécial » préparé pendant des semaines.
Comment ça fonctionne concrètement :
- Un développeur crée une branche
feature/xxxdepuismain - Il travaille quelques heures (rarement plus d’une journée)
- Il ouvre une Pull Request, qui est revue rapidement
- Dès que c’est mergé dans
main, la pipeline se déclenche - Si tous les tests passent, le code peut partir en production automatiquement
La clé : les feature flags
Comment déployer du code en production s’il n’est pas terminé ? Grâce aux feature flags (ou « feature toggles ») — des interrupteurs dans le code qui activent ou désactivent une fonctionnalité.
# Exemple simplifié de feature flagif feature_flags.is_enabled("nouvelle_interface_paiement"): afficher_nouvelle_interface()else: afficher_ancienne_interface()Le code est en production, mais la fonctionnalité est « éteinte ». Quand elle est prête, on l’active pour quelques utilisateurs (canary), puis pour tout le monde.
Avantages :
- Intégration continue vraie : le code est intégré plusieurs fois par jour, pas une fois par semaine
- Pas de « merge hell » : les branches courtes = peu de conflits
- Feedback rapide : vous savez en quelques heures si votre code fonctionne avec celui des autres
Inconvénients :
- Discipline stricte : chaque commit sur
maindoit être de qualité production - Feature flags obligatoires : infrastructure supplémentaire à gérer
- Couverture de tests excellente requise : sans tests fiables, c’est le chaos
Contexte idéal : équipes matures pratiquant le continuous deployment, produits SaaS où une seule version est en production.
Comparaison : quelle approche choisir ?
| Aspect | Pipeline par branche (GitFlow) | Trunk-based |
|---|---|---|
| Fréquence de merge | Jours ou semaines | Heures |
| Nombre de pipelines | Multiple (une par type de branche) | Une seule |
| Feature flags | Optionnels | Obligatoires |
| Risque de conflit Git | Élevé (branches longues) | Faible (branches courtes) |
| Temps jusqu’à la production | Jours ou semaines | Minutes ou heures |
| Complexité de la pipeline | Moyenne à élevée | Simple |
| Besoin en tests | Important | Critique |
Comment choisir ?
- Vous livrez une fois par mois ou moins → GitFlow est acceptable
- Vous livrez plusieurs fois par semaine → considérez trunk-based
- Vous avez plusieurs versions en production → GitFlow est souvent nécessaire
- Vous faites du SaaS avec une seule version → trunk-based est plus adapté
Monorepo vs polyrepo : où mettre votre code
Au-delà des branches, une autre question structure vos pipelines : combien de dépôts Git avez-vous ?
Monorepo : tout dans un seul dépôt
Le principe : tous vos services, bibliothèques et outils partagent le même dépôt Git. Google, Meta et Microsoft utilisent cette approche pour une grande partie de leur code.
Analogie : c’est comme un grand entrepôt unique où tous les rayons (API, frontend, outils) sont sous le même toit. Facile de voir l’ensemble, mais il faut une bonne organisation.
monorepo/├── services/│ ├── api/ # Service backend│ ├── frontend/ # Application web│ └── worker/ # Traitement asynchrone├── libs/│ ├── auth/ # Bibliothèque d'authentification│ └── utils/ # Utilitaires partagés└── infra/ ├── terraform/ # Infrastructure as Code └── k8s/ # Manifests KubernetesLe défi : ne pas tout reconstruire à chaque commit
Si quelqu’un modifie le README du frontend, inutile de reconstruire l’API. La pipeline doit être « intelligente » et détecter ce qui a changé.
Commit modifie : services/api/src/users.py
Pipeline analyse les dépendances : ✓ services/api → rebuild (fichier modifié) ✓ libs/auth → vérifier (l'API l'utilise peut-être) ✗ services/frontend → skip (pas impacté) ✗ services/worker → skip (pas impacté)Avantages :
- Refactoring atomique : une seule PR peut modifier l’API, le frontend et les libs — tout reste cohérent
- Partage de code naturel : les bibliothèques communes sont dans le même repo, pas besoin de les publier
- Cohérence des versions : tous les services utilisent la même version des libs
Inconvénients :
- Pipeline complexe : détecter ce qui a changé demande de l’outillage (Nx, Turborepo, Bazel)
- Temps de clone élevé : le repo peut devenir très gros
- Permissions compliquées : difficile de restreindre l’accès à certaines parties
Outils courants pour monorepo :
| Outil | Langage/Écosystème | Point fort |
|---|---|---|
| Nx | JavaScript/TypeScript | Excellent pour les projets frontend |
| Turborepo | JavaScript/TypeScript | Simple, rapide, cache distribué |
| Bazel | Polyglotte | Très puissant, mais courbe d’apprentissage raide |
| Pants | Python, Go, Java | Bonne alternative à Bazel |
Polyrepo : un dépôt par service
Le principe : chaque service, chaque bibliothèque a son propre dépôt Git et sa propre pipeline.
Analogie : c’est comme des boutiques indépendantes dans une rue commerçante. Chacune gère ses stocks, ses horaires, sa décoration. Autonome, mais la coordination demande des efforts.
github.com/acme/api # Son propre repo, sa propre pipelinegithub.com/acme/frontend # Idemgithub.com/acme/worker # Idemgithub.com/acme/libs-auth # Publié comme package (npm, PyPI...)github.com/acme/infra # Terraform séparéAvantages :
- Pipeline simple : chaque repo a une pipeline qui fait toujours la même chose
- Équipes autonomes : l’équipe API peut déployer sans attendre l’équipe frontend
- Permissions claires : facile de donner accès à un repo sans exposer le reste
Inconvénients :
- Coordination des versions : l’API v2.3 est-elle compatible avec le frontend v1.8 ?
- Duplication : la configuration de pipeline est souvent copiée-collée entre repos
- Refactoring pénible : modifier une interface partagée = plusieurs PRs dans plusieurs repos
Le problème du « dependency hell » :
En polyrepo, les bibliothèques partagées sont publiées comme des packages (npm, PyPI, Maven…). Cela crée des questions :
- Quand
libs-authpublie une nouvelle version, qui la met à jour dans chaque service ? - Comment s’assurer que tous les services sont compatibles ?
- Que faire si un service est bloqué sur une vieille version ?
Stratégie hybride : le meilleur des deux mondes ?
Beaucoup d’organisations adoptent un compromis pragmatique :
| Type de code | Stratégie | Pourquoi |
|---|---|---|
| Services métier | Monorepo | Refactoring atomique, cohérence |
| Infrastructure | Repo séparé | Cycle de vie différent, permissions distinctes |
| Libs très partagées | Repos séparés + versioning | Réutilisation hors de l’organisation |
Exemple concret :
github.com/acme/platform # Monorepo : API + frontend + workersgithub.com/acme/infra # Séparé : Terraform, secretsgithub.com/acme/design-system # Séparé : composants UI réutilisables, versionnésPromotion vs rebuild : que déployer exactement ?
Une question cruciale que beaucoup d’équipes négligent : l’artefact que vous déployez en production est-il le même que celui que vous avez testé ?
Le problème du rebuild
Situation courante (et risquée) :
# Pipeline stagingstaging: script: - npm install - npm run build - docker build -t app:staging . - docker push registry/app:staging - kubectl apply -f k8s/staging/
# Pipeline production (quelques jours plus tard)production: script: - npm install # ⚠️ Versions des dépendances peuvent avoir changé - npm run build # ⚠️ Résultat potentiellement différent - docker build -t app:production . - docker push registry/app:production - kubectl apply -f k8s/production/Pourquoi c’est un problème ?
Entre le test en staging et le déploiement en production :
- Une dépendance npm a peut-être été mise à jour (même en patch)
- L’image de base Docker a peut-être changé
- Un outil de build a peut-être évolué
Résultat : ce que vous déployez en production n’est pas exactement ce que vous avez testé. Et quand ça casse, impossible de savoir pourquoi.
La solution : promotion d’artefact (build once, deploy many)
Le principe : construire l’artefact une seule fois, puis le promouvoir d’environnement en environnement sans le reconstruire.
Comment ça fonctionne :
- Build : l’artefact est construit avec un tag unique (ex:
app:abc123) - Test en dev : on déploie
app:abc123en développement - Test en staging : on déploie le même
app:abc123en staging - Production : on déploie toujours le même
app:abc123en production
La seule chose qui change entre environnements : la configuration (variables d’environnement, secrets).
Exemple concret :
# Étape 1 : Build uniquebuild: script: - docker build -t app:$CI_COMMIT_SHA . - docker push registry/app:$CI_COMMIT_SHA
# Étape 2 : Déploiement en staging (même image)staging: script: - helm upgrade app ./chart \ --set image.tag=$CI_COMMIT_SHA \ --set env=staging \ -f values-staging.yaml
# Étape 3 : Déploiement en production (toujours la même image)production: script: - helm upgrade app ./chart \ --set image.tag=$CI_COMMIT_SHA \ --set env=production \ -f values-production.yaml when: manual # Approbation requiseAvantages de la promotion :
| Aspect | Rebuild | Promotion |
|---|---|---|
| Reproductibilité | ❌ Incertaine | ✅ Garantie |
| Temps de déploiement | Long (rebuild) | Rapide (juste déployer) |
| Rollback | Rebuild l’ancienne version | Redéployer l’ancien tag |
| Confiance | « Ça marchait en staging… » | « C’est le même artefact » |
Prérequis pour la promotion :
Pour que ça fonctionne, votre application doit :
- Séparer le build de la config : pas de valeurs en dur (URLs, credentials)
- Accepter la configuration au runtime : variables d’environnement, fichiers de config montés
- Avoir un registre d’artefacts fiable : où stocker les images entre les étapes
Pipeline applicative vs pipeline infrastructure
Jusqu’ici, on a surtout parlé des pipelines qui déploient du code applicatif (API, frontend). Mais l’infrastructure aussi peut (et devrait) être gérée par des pipelines.
Pipeline applicative
Ce qu’elle déploie : le code de votre application — API, frontend, workers, jobs.
Caractéristiques :
- Déclenchée par des commits sur le code applicatif
- Fréquence élevée (plusieurs fois par jour possible)
- Impact limité (un service à la fois, généralement)
Pipeline infrastructure
Ce qu’elle déploie : les ressources sur lesquelles tourne votre application — réseaux, bases de données, clusters, DNS.
Caractéristiques :
- Déclenchée par des modifications d’Infrastructure as Code (Terraform, Pulumi)
- Fréquence plus faible (quelques fois par semaine)
- Impact potentiellement large (peut affecter plusieurs services)
Faut-il séparer ou intégrer ?
| Approche | Avantages | Inconvénients |
|---|---|---|
| Pipelines séparées | Cycles indépendants, permissions distinctes, blast radius limité | Coordination manuelle parfois nécessaire |
| Pipeline unique | Déploiement atomique (infra + app ensemble), cohérence garantie | Complexité, risque élevé si échec |
Recommandation : séparez les pipelines, mais coordonnez-les.
L’infrastructure change moins souvent que le code. Mélanger les deux crée des pipelines lourdes où un changement de couleur de bouton déclenche une vérification Terraform.
Comment coordonner ?
- Triggers : la pipeline infra déclenche la pipeline app après succès
- GitOps : des outils comme ArgoCD ou Flux détectent les changements et déploient
- Dépendances explicites : la pipeline app vérifie que l’infra est à jour avant de déployer
Architectures avancées
Une fois les bases maîtrisées, certaines équipes adoptent des patterns plus sophistiqués.
Pipeline en diamant : parallélisation maximale
Le principe : exécuter un maximum de jobs en parallèle, puis reconverger avant l’étape suivante.
Pourquoi « diamant » ? La forme du graphe ressemble à un diamant : un point de départ, une divergence (parallélisation), une convergence, puis à nouveau divergence/convergence.
Avantages :
- Temps total réduit : lint, tests et scan s’exécutent en même temps
- Feedback rapide : si le lint échoue, on le sait sans attendre la fin des tests
- Ressources optimisées : les runners travaillent en parallèle
Attention : tous les jobs parallèles doivent réussir pour passer à l’étape suivante. Un échec = pipeline bloquée.
Pipeline avec matrix : tester toutes les combinaisons
Le problème : votre bibliothèque doit fonctionner avec Python 3.9, 3.10, 3.11 et 3.12, sur Ubuntu, macOS et Windows. Ça fait 12 combinaisons à tester.
La solution : la matrix génère automatiquement tous les jobs.
test: strategy: matrix: python: [3.9, 3.10, 3.11, 3.12] os: [ubuntu-latest, macos-latest, windows-latest]
# Résultat : 4 versions × 3 OS = 12 jobs parallèlesCas d’usage :
- Bibliothèques open source (compatibilité multi-versions)
- Applications desktop (multi-OS)
- Validation de compatibilité avant une mise à jour de dépendance
Pipeline conditionnelle : adapter selon le contexte
Le principe : certains jobs ne s’exécutent que si certaines conditions sont remplies.
Exemples courants :
| Condition | Comportement |
|---|---|
Branche = main | Déployer en staging, puis production (avec approbation) |
Branche = feature/* | Déployer en environnement éphémère, détruire après 24h |
Fichiers modifiés dans docs/ | Skipper les tests, rebuilder uniquement la documentation |
Tag créé (v*) | Créer une release, publier les packages |
Pourquoi c’est utile ?
- Économie de ressources : pas besoin de tout tester si seule la doc a changé
- Feedback adapté : une feature branch n’a pas besoin de déployer en production
- Flexibilité : comportements différents selon le contexte sans multiplier les pipelines
Choisir son architecture : guide de décision
Face à toutes ces options, comment choisir ? Voici un guide basé sur des questions concrètes.
Question 1 : Combien d’équipes travaillent sur le code ?
| Situation | Recommandation |
|---|---|
| 1 équipe (< 10 personnes) | Monorepo simple, trunk-based possible |
| 2-5 équipes | Monorepo avec outillage (Nx, Turborepo) ou polyrepo léger |
| 10+ équipes | Polyrepo probable, ou monorepo avec infrastructure dédiée |
Question 2 : Quelle fréquence de déploiement ?
| Fréquence | Recommandation |
|---|---|
| Mensuelle ou moins | GitFlow acceptable, promotion d’artefact recommandée |
| Hebdomadaire | Trunk-based à considérer, promotion obligatoire |
| Quotidienne ou plus | Trunk-based + feature flags + continuous deployment |
Question 3 : Avez-vous plusieurs versions en production ?
| Situation | Recommandation |
|---|---|
| Non (SaaS, une seule version) | Trunk-based, pipeline unique |
| Oui (logiciel installé, app mobile) | Branches de release, pipelines par branche |
Question 4 : Quelle est votre maturité en tests ?
| Niveau | Recommandation |
|---|---|
| Peu ou pas de tests automatisés | Restez sur des pipelines simples, investissez dans les tests d’abord |
| Couverture correcte (> 60%) | Trunk-based envisageable avec prudence |
| Excellente couverture + tests d’intégration | Continuous deployment possible |
L’anti-pattern : copier sans comprendre
« Netflix fait du trunk-based avec continuous deployment, on devrait faire pareil ! »
Non. Netflix a :
- Des centaines d’ingénieurs dédiés à l’outillage
- Des années d’itération sur leurs pratiques
- Une infrastructure de tests et de feature flags mature
- Un contexte SaaS avec une seule version en production
Copier leur architecture sans leur contexte, c’est comme copier l’entraînement d’un athlète olympique sans avoir sa condition physique.
La bonne approche :
- Commencez simple : une pipeline linéaire qui build, teste, déploie
- Identifiez les douleurs : qu’est-ce qui vous ralentit vraiment ?
- Évoluez progressivement : ajoutez de la complexité quand vous en avez besoin
- Mesurez : temps de pipeline, fréquence de déploiement, taux d’échec
À retenir
-
L’architecture de pipeline reflète votre contexte — structure d’équipe, stratégie de branches, modèle de déploiement. Il n’y a pas de solution universelle.
-
Trunk-based vs GitFlow — branches courtes = moins de conflits et feedback rapide, mais demande discipline et feature flags. Branches longues = plus de contrôle, mais merges douloureux.
-
Monorepo vs polyrepo — monorepo facilite le refactoring et la cohérence, mais demande de l’outillage. Polyrepo donne de l’autonomie, mais complique la coordination.
-
Promotion > rebuild — construisez l’artefact une fois, déployez-le partout. Ce que vous testez doit être exactement ce que vous déployez.
-
Séparez app et infra — des cycles différents, des permissions différentes, des risques différents. Coordonnez, mais ne mélangez pas.
-
Commencez simple, complexifiez quand nécessaire — la pipeline parfaite n’existe pas au jour 1. Itérez en fonction des problèmes réels.
Pour aller plus loin
- Qu’est-ce qu’une pipeline CI/CD ? — Les fondamentaux avant d’aller plus loin
- CI vs CD vs CD — Comprendre les différences entre intégration, livraison et déploiement continus
- Bonnes pratiques CI/CD — Les invariants qui marchent partout
- Anti-patterns CI/CD — Les erreurs classiques à éviter