← Retour à l'accueil

Cours 03

Flows de développement — suite, conclusion et transition vers la CI

Ce cours conclut le module flows de développement ouvert au cours-02 et pose les premières bases de l'intégration continue.

QCM d'ouverture

La séance commence par 20 minutes de QCM sur papier. Les questions portent sur les notions du cours-02 : Docker, GitHub Flow, Git Flow, Pull Requests, release branches, hotfixes et logique globale des workflows étudiés.

Posez le stylo quand le temps est écoulé.

Release-Based Workflow

GitHub Flow fonctionne très bien tant qu'une seule ligne courante suffit. Mais certains produits doivent fournir des livrables distincts, figer une version pour un audit, ou maintenir plusieurs versions majeures en parallèle. Dans ce cas, la release devient une branche explicite de stabilisation, pas seulement un tag posé à la fin.

Une release est un sanctuaire de stabilité

Dans le dépôt du TP, main représente la ligne d'intégration de la prochaine version. La branche release/v1.0 représente une version déjà livrée, taguée v1.0.0, et release/v1.1 sera créée depuis main lorsque la prochaine version devra être stabilisée.

Une nouvelle fonctionnalité n'est jamais développée directement sur une branche release/*. Ces branches restent des sanctuaires destinés à la recette, à la stabilisation et aux correctifs de bugs. Une feature part de main ; une release ne reçoit que ce qui doit rendre un livrable sûr.

Même métier, mais plusieurs lignes à garder cohérentes

Une nouvelle fonctionnalité comme GET /tasks/stats part de main, puis la branche release/v1.1 est créée depuis main au moment du gel de version. Un correctif urgent, par exemple la validation du champ title, part de release/v1.0, puis doit être reporté sur main et sur release/v1.1 si le bug y existe aussi.

Le coût caché : aligner les versions parallèles

Le Release-Based Workflow protège les versions livrées, mais il crée une obligation de synchronisation. Un correctif appliqué sur release/v1.0 ne corrige pas automatiquement main ni release/v1.1. Le cherry-pick devient donc un geste important pour reporter un commit précis sans fusionner toute l'histoire d'une branche dans une autre.

Pourquoi ce flow est-il particulièrement adapté aux livrables audités ou maintenus en parallèle ?

Parce qu'une branche release/* matérialise un état de code précis, stabilisé et contrôlable. Elle peut être testée, certifiée, livrée à un client, puis corrigée sans absorber toutes les nouveautés de main.

En échange, l'équipe doit suivre explicitement où chaque bug existe. Si le même défaut touche release/v1.0, main et release/v1.1, le correctif doit être reporté sur chaque ligne concernée.

Ce gitGraph illustre le cycle à retenir : une release v1.0.0 déjà taguée, une fonctionnalité préparée sur main, une release/v1.1 créée pour stabiliser la suite, et un hotfix reporté par cherry-pick.

Chargement du gitGraph…

TP 3 : Release-Based Workflow (20 min)

Objectif : pratiquer un flow dans lequel une version déjà livrée peut recevoir un correctif, pendant que la prochaine version est préparée sur main puis stabilisée dans release/v1.1.

Dépôt support (à forker sur GitHub avant de cloner) : github.com/GVI2026/tp-release-based-workflow

À observer : main et release/v1.0 n'ont pas le même rôle. La branche release/v1.0 contient déjà le commit chore(release): bump project to version 1.0.0, tagué v1.0.0. Une feature prépare la suite sur main ; un hotfix corrige une version déjà livrée.

À réussir : appliquer un correctif critique sur la bonne release, puis le reporter sur les autres lignes où le bug existe encore.

Quel geste Git permet de reporter précisément un correctif de release/v1.0 vers main et release/v1.1 sans fusionner toute la branche ?

Le geste attendu est le cherry-pick. Il applique un commit précis sur une autre branche, ce qui permet de garder plusieurs versions parallèles alignées sur un correctif critique sans mélanger leurs cycles de release.

GitLab Flow

GitLab Flow répond à un autre besoin : organiser le passage du code entre des environnements. Il reste proche d'un flow à branche principale, mais ajoute des branches ou règles de déploiement qui représentent les étapes de validation avant la production. Le gitGraph ci-dessous montre une promotion de main vers staging, puis de staging vers production.

Principe : main reçoit les features intégrées, puis une Pull Request de promotion amène ce code vers staging. Les versions candidates y sont taguées en rc (release-candidate) avant la promotion finale vers production.

Usage typique : un service Cloud ou Web critique, par exemple la plateforme d'accès aux comptes d'une banque, où l'enjeu principal est de tester dans un environnement de pré-production proche du réel.

Point d'attention : GitLab Flow structure surtout la promotion entre environnements ; ce n'est pas le meilleur outil si le problème central est de maintenir plusieurs livrables clients figés en parallèle.

Chargement du gitGraph…

Trunk-Based Development

Le Trunk-Based Development est abordé ici comme une direction possible, accessible aux équipes qui intègrent très fréquemment et qui disposent déjà d'une pipeline CI/CD fiable et mature. Par rapport à GitHub Flow, la différence n'est pas l'existence de main, mais la durée très courte, voire l'absence, des branches de travail.

Principe : une branche principale unique, souvent main ou trunk, sur laquelle les intégrations sont très fréquentes.

Feature flags : ils permettent de masquer une fonctionnalité incomplète sans la garder éternellement sur une branche longue.

Release à chaque commit : dans un modèle respecté à 100 %, chaque commit accepté sur main peut déclencher une release. En pratique, une équipe peut nuancer ce principe : par exemple, déployer le code automatiquement mais garder une fonctionnalité désactivée derrière un feature flag jusqu'à validation métier.

Pas toujours de PR : dans un trunk-based strict, du code peut être ajouté directement sur main si la CI/CD, les tests et les garde-fous automatisés sont assez solides. Beaucoup d'équipes gardent quand même des Pull Requests très courtes pour conserver une revue légère sans recréer des branches longues.

Merge queues : elles sérialisent les intégrations pour réduire le risque de casser la branche principale au moment du merge.

Prérequis : une pipeline CI/CD fiable, rapide et lisible est indispensable pour que ce mode de travail soit viable.

Que se passe-t-il lorsqu'une feature buggée est ajoutée à la main sans revue de code ?

La CI/CD devient la première protection. Si la feature casse un test, le build, une règle de qualité ou une vérification de sécurité, la pipeline échoue, bloque le déploiement et remonte une alerte à l'équipe. Le commit est alors corrigé rapidement, annulé, ou la fonctionnalité est désactivée derrière un feature flag avant d'être visible par les utilisateurs.

Si le défaut passe malgré toutes les vérifications, le risque est plus élevé qu'avec une revue humaine classique. C'est pour cela que le Trunk-Based Development demande des tests rapides, fiables, une surveillance claire et une capacité de rollback ou de désactivation immédiate.

Chargement du gitGraph…

Tableau comparatif final des flows

Aucun flow n'est universel. Un projet peut très bien évoluer d'un mode très simple vers un flow plus structuré à mesure que la taille de l'équipe, le nombre de versions supportées et l'exigence qualité augmentent.

Sans flow réel : acceptable au tout début d'un prototype individuel, mais fragile.

GitHub Flow : bon point d'entrée dès qu'une intégration fréquente sur une ligne courante claire est visée ; l'équipe peut rester sur ce modèle en choisissant elle-même quand générer une release, par exemple avec release-please.

Git Flow : utile pour les projets à livraison manuelle ou communautaire, où des barrières humaines et un processus de validation rigide protègent la production.

GitLab Flow : pertinent pour un service Cloud ou Web critique qui doit passer par une pré-production avant de toucher les serveurs de production.

Release-Based Workflow : adapté aux livrables distincts, aux audits sur code figé et à la maintenance de plusieurs versions en parallèle.

Trunk-Based Development : logique quand la maturité CI/CD permet des intégrations très fréquentes sur un tronc unique.

Cette trajectoire synthétise la progression réaliste d'un projet.

Chargement du schéma…

Quel flow choisir pour un projet open source livré manuellement avec une forte revue humaine avant chaque version ?

Git Flow. Le besoin principal est de protéger une livraison manuelle avec des étapes explicites de préparation, validation, hotfix et tag.

Quel flow choisir pour un service bancaire en ligne qui doit passer par un environnement staging avant production ?

GitLab Flow. Le critère dominant est la promotion contrôlée du code entre environnements, avec une barrière de pré-production avant les serveurs réellement utilisés par les clients.

Quel flow choisir pour maintenir deux versions majeures certifiées chez des clients différents ?

Release-Based Workflow. Le point clé est de garder plusieurs livrables figés et maintenus en parallèle, tout en reportant les correctifs critiques sur chaque version concernée.

DevOps et CI/CD

Les flows présentés posent tous la même question en filigrane : comment garder confiance dans les changements quand plusieurs branches, plusieurs versions et plusieurs personnes sont actives en même temps ? DevOps et la CI/CD sont précisément la réponse industrielle à cette question.

DevOps : briser les silos

Pendant longtemps, développement et exploitation fonctionnaient en silos séparés avec des objectifs mesurés différemment. Les développeurs étaient évalués sur la quantité de fonctionnalités livrées ; les équipes d'exploitation sur la stabilité du système. Résultat : chaque mise en production devenait un moment de friction, de ralentissement et de méfiance mutuelle.

DevOps est d'abord une réponse culturelle et organisationnelle : aligner les équipes sur un objectif commun — livrer de la valeur rapidement et de façon fiable. L'outillage (CI/CD, monitoring, infrastructure as code) vient ensuite rendre cette collaboration concrète et répétable.

Le cycle DevOps est continu : il passe par la planification, le code, la vérification, le packaging, le déploiement, la configuration, la surveillance… puis revient à la planification à partir de ce qui est observé en production.

Chargement du cycle DevOps…

Que se passe-t-il quand un bug critique est découvert en production si Dev et Ops ont des objectifs mesurés séparément ?

L'équipe Dev peut considérer que le bug est "en prod donc c'est un problème Ops". L'équipe Ops peut refuser de déployer un correctif précipité qui risquerait de déstabiliser le système. Personne n'est incité à résoudre rapidement, car la responsabilité partagée n'est pas mesurée.

DevOps supprime cette friction en rendant les deux équipes co-responsables du même résultat : un service stable et évolutif.

CI, Continuous Delivery et Continuous Deployment (CD)

Ces trois pratiques forment un spectre progressif : chaque niveau automatise davantage, et chaque niveau suppose que le précédent fonctionne de façon fiable.

Intégration Continue (CI) : chaque commit déclenche automatiquement une vérification — formatage, lint, tests, build. L'objectif est de détecter immédiatement si le changement casse quelque chose dans le projet. Sans CI solide, les deux pratiques suivantes n'ont pas de fondation.

Continuous Delivery : la CI est complétée par une automatisation du packaging et de la préparation à la mise en production. L'artefact est toujours prêt à être déployé. Le déploiement lui-même reste déclenché manuellement — souvent pour des raisons métier ou réglementaires.

Continuous Deployment : le déploiement en production est lui aussi automatisé. Dès qu'un commit passe toutes les vérifications, il part en production sans intervention humaine. Cela suppose une confiance très élevée dans la pipeline et dans les tests.

Chargement du spectre CI/CD…

Dépôt CI pédagogique — temps 1 : prise en main guidée (20 min)

Dépôt support : GVI2026/tp-ci-api

Comme pour les autres TP du cours, ce dépôt doit d'abord être forké sur un compte GitHub avant d'être manipulé.

La théorie CI/CD étant posée, le dépôt pédagogique permet de lire un vrai workflow pour la première fois. L'objectif n'est pas encore de corriger quoi que ce soit, mais de repérer la structure avant d'en étudier l'anatomie en détail.

  1. Repérer et lire le fichier .github/workflows/ci.yml.

Workflow : orchestration complète décrite dans un fichier YAML sous .github/workflows/.

Event / Trigger : événement qui déclenche la pipeline, par exemple push, pull_request ou schedule.

Job : unité d'exécution logique du workflow, exécutée sur son propre runner.

Step : action unitaire à l'intérieur d'un job, sous forme de script shell ou d'action réutilisable.

Runner : machine fraîche qui exécute un job.

Action : brique réutilisable appelée avec uses:, par exemple actions/checkout@v4.

Combien de jobs sont définis dans .github/workflows/ci.yml, et quel est le rôle du job qui s'exécute en premier ?

Le workflow contient cinq jobs : install, format-lint, tests, build et security. Le premier à s'exécuter est install, dont le rôle est d'installer les dépendances npm et de les stocker en cache pour tous les jobs suivants. Si le cache est déjà présent, l'étape npm ci est ignorée.

Ce mécanisme est documenté en détail dans docs/fonctionnement-cache.md du dépôt.

Pour terminer cette prise en main, lancer la pipeline complète pour la première fois. Le premier lancement est plus long car les images Docker doivent être téléchargées :

act

Ce premier run peut prendre plusieurs minutes. La structure de la pipeline sera analysée en détail dans la section suivante.

Anatomie d'une pipeline GitHub Actions

Lorsqu'un commit est poussé, voici ce qui se passe concrètement côté GitHub Actions.

Un événement (push, pull_request, ou encore un planning schedule) déclenche l'exécution d'un workflow — un fichier YAML dans .github/workflows/. Ce workflow décrit un ensemble de jobs.

Chaque job s'exécute sur sa propre machine virtuelle fraîche, appelée runner. À l'intérieur d'un job, les steps s'enchaînent séquentiellement. Un step est soit un script shell (run: npm test), soit une action réutilisable (uses: actions/checkout@v4).

Les jobs peuvent avoir des dépendances entre eux via needs:. L'ensemble des jobs et de leurs dépendances forme un DAG (Directed Acyclic Graph) — un graphe orienté sans cycle. Ce graphe rend visibles à la fois les dépendances obligatoires et les opportunités de parallélisation.

Chargement de l'anatomie pipeline…

Le principe fail-fast

Placer les vérifications les plus rapides et les moins coûteuses en premier dans la chaîne n'est pas arbitraire : c'est le principe fail-fast. Un job qui échoue arrête immédiatement tous les jobs qui en dépendent. Inutile de compiler si le formatage du code est déjà incorrect. Inutile de scanner les dépendances si les tests unitaires échouent.

Voici la pipeline du dépôt pédagogique. Elle se lit de gauche à droite : chaque flèche en pointillés indique qu'un échec ici stoppera tous les jobs à droite. L'ordre sert à obtenir le diagnostic utile le plus tôt possible.

Le feedback loop : la valeur d'une pipeline ne se mesure pas seulement à ce qu'elle détecte, mais à la vitesse à laquelle elle renvoie l'information au développeur. Un retour en 2 minutes permet de corriger immédiatement. Un retour en 45 minutes implique un changement de contexte mental qui multiplie le temps de correction réel.

Chargement du graphe CI…

Descriptions des jobs

format-lint : vérifie le formatage et lance l'analyse statique. C'est le premier filtre de qualité : il détecte les erreurs mécaniques et les incohérences faciles à corriger.

tests : exécute les tests automatisés pour vérifier que le comportement attendu de l'application est toujours respecté.

build : compile l'application et confirme que le projet peut produire un artefact exploitable.

security : analyse les dépendances pour repérer les vulnérabilités connues avant qu'elles n'arrivent dans une chaîne de livraison.

Shift-left : détecter tôt, corriger moins cher

Dans un modèle traditionnel, les vérifications arrivent tard : les tests manuels et les audits de sécurité interviennent juste avant la mise en production. À ce stade, corriger un bug est entre 10 et 100 fois plus coûteux qu'en phase de développement, parce que l'auteur du code a changé de contexte, que d'autres fonctionnalités ont été empilées dessus, et que l'impact d'un correctif est plus difficile à isoler.

Le shift-left consiste à avancer toutes les vérifications possibles vers la gauche du cycle de vie du code. Concrètement, la CI exécute automatiquement formatage, lint, tests, build et contrôles de sécurité dès qu'un changement est proposé, au lieu d'attendre une phase de validation tardive.

Le shift-left explique donc quand les vérifications doivent arriver : pendant le développement et l'intégration. Le fail-fast explique ensuite dans quel ordre les organiser dans la pipeline pour renvoyer le diagnostic le plus utile le plus tôt possible.

À quelle étape de la pipeline est-il le moins coûteux de détecter une erreur ?

Le plus tôt possible — idéalement dans le job format-lint. Une erreur attrapée là prend quelques secondes à corriger : le fichier est édité et la vérification relancée. Aucun contexte n'est perdu.

À l'inverse, une erreur qui n'est détectée qu'en production oblige à analyser des logs en direct, à réverter un déploiement, parfois à réveiller des personnes. Le coût humain et technique est sans commune mesure.

Dépôt CI pédagogique — temps 2 : exercices et correction (60 min)

Le but est maintenant de diagnostiquer puis corriger une CI cassée. Le dépôt a été conçu pour que les pannes soient réalistes, lisibles et rapides à interpréter.

Depuis un fork de tp-ci-api, GitHub Actions peut être activé pour observer les mêmes exécutions directement sur GitHub, en complément de act en local.

Pipeline de référence à garder en tête

install → format-lint → tests → build → security

L'ordre n'est pas arbitraire. Il sert à détecter vite les erreurs les moins coûteuses à diagnostiquer avant de mobiliser des étapes plus longues.

Commandes utiles pendant le TP

npm run format:check
npm run lint
npm run test:ci
npm run build
act
act -j format-lint
act -j tests

Exercice 1 — corriger une étape qualité simple

Niveau : Débutant

Branche : exercise/01-format-lint. L'objectif est de lire le bon log, puis de corriger un problème de formatage ou de lint sans modifier inutilement le reste du projet.

Exercice 2 — réparer un test unitaire

Niveau : Débutant

Branche : exercise/02-test-failure. L'échec est volontairement simple à comprendre : le test doit être repéré, son intention relue, puis le test ou le code testé doit être corrigé.

Exercice 3 — remettre la pipeline dans un ordre fail-fast

Niveau : Intermédiaire

Branche : exercise/03-pipeline-order. Le travail consiste à réorganiser les dépendances de jobs dans .github/workflows/ci.yml pour retrouver une chaîne logique et lisible.

Exercice 4 — lire les logs et trouver la bonne cause

Niveau : Intermédiaire

Branche : exercise/04-log-reading. Ici, l'enjeu n'est pas seulement de corriger : il faut surtout savoir lire le bon job, la bonne step et le bon message d'erreur.

Exercice 5 — stabiliser la chaîne complète

Niveau : Avancé

Branche : exercise/05-stabilize. Cette branche sert à vérifier qu'un ordre de diagnostic cohérent peut être appliqué jusqu'au retour complet au vert.

Bonus — paralléliser une partie simple du DAG

Niveau : Avancé

Branche : bonus/parallelization. Ce bonus est proposé à ceux qui ont terminé les exercices précédents. Le but est de séparer proprement des vérifications indépendantes sans rendre la lecture du graphe inutilement compliquée.

Conclusion et ouverture

En trois séances, le module est passé d'un usage local de Git à des workflows d'équipe structurés, puis à la question que tous ces flows soulèvent : comment garder confiance dans les changements quand les branches, les versions et les intégrations se multiplient ?

La CI répond à la première partie de cette question : détecter vite, corriger tôt, intégrer en confiance. L'autre partie — livrer en production de façon fiable et automatisée — sera l'objet du prochain cours, consacré au Continuous Delivery et au Continuous Deployment.