Rappels indispensablesDans le dialogue d’impression, choisissez « Enregistrer au format PDF » comme destination.
Retour

DevOps · C1 Culture et fondations · Chapitre 2 · 4 h

Rappels indispensables

Linux en ligne de commande, processus et permissions ; ports, DNS et HTTP ; Git avancé — branches, fusion, rebase, GitFlow contre trunk-based — et revue de code.

Trois choses bloquent un TP de DevOps dès la première séance, et aucune n'est du DevOps.

Le serveur n'a pas d'interface graphique : tout se fait en ligne de commande, et un étudiant qui hésite sur chmod ou sur un pipe perd son temps sur autre chose que le sujet. L'application ne répond pas, et il faut savoir où regarder : le processus écoute-t-il ? le port est-il ouvert ? le nom se résout-il ? Et le dépôt Git compte huit branches divergentes, parce que personne n'a décidé d'une stratégie.

Ce chapitre remet ces trois outils à niveau. Il ne remplace pas les cours de systèmes et de réseaux — il en extrait ce dont on se sert tous les jours, et il ajoute la partie de Git que l'usage individuel ne demande jamais.

Linux, ce qui sert vraiment

Le cours de systèmes a tout posé ; voici ce qu'on emploie sans y penser.

CommandeUsage quotidien en exploitation
ps aux, top, htopqu'est-ce qui tourne, et qui consomme
kill -TERM, kill -9arrêter proprement, puis brutalement
df -h, du -sh *le disque est plein — où ?
tail -f fichier.logsuivre un journal en direct
grep, awk, sort | uniq -cfouiller ces journaux
chmod, chowndroits et propriétaire
systemctl status, journalctl -ul'état d'un service et ses journaux
ssh, scpatteindre la machine, y déposer un fichier

Deux rappels du cours de systèmes qui reviendront constamment.

Les droits. rwx pour le propriétaire, le groupe et les autres, notés en octal — 755, 644. Un conteneur qui refuse de démarrer parce qu'un script n'est pas exécutable est le grand classique du chapitre 3.

Les processus et les signaux. SIGTERM demande de s'arrêter proprement, SIGKILL tue sans préavis. Cette distinction n'est pas théorique : Docker et Kubernetes envoient un SIGTERM, attendent quelques secondes, puis un SIGKILL. Une application qui n'écoute pas SIGTERM perd ses requêtes en cours à chaque déploiement — et personne ne comprend pourquoi.

Réseau, le minimum pour diagnostiquer

Quand « ça ne répond pas », la question se décompose en quatre, et il faut les poser dans cet ordre.

Le nom se résout-il ? dig monsite.fr ou nslookup. Un DNS mal propagé se manifeste comme une panne d'application.

Le port est-il ouvert et écouté ? ss -tlnp liste ce qui écoute localement ; curl -v ou nc -zv hôte port teste depuis l'extérieur. La distinction compte : un service qui écoute sur 127.0.0.1 fonctionne en local et est injoignable de partout ailleurs — c'est l'erreur la plus fréquente du chapitre 3, où un conteneur doit écouter sur 0.0.0.0 pour être atteignable.

La requête arrive-t-elle ? Les journaux du serveur le disent. Si rien n'apparaît, le problème est en amont : pare-feu, routage, groupe de sécurité.

Que répond-elle ? Les codes HTTP suffisent à orienter : 2xx succès, 3xx redirection, 4xx la faute vient du client — 404 introuvable, 401 non authentifié, 403 interdit — et 5xx la faute vient du serveur, 500 erreur interne, 502 la passerelle n'a pas obtenu de réponse, 503 service indisponible.

Cette dernière ligne est celle qu'on emploie le plus : un 502 accuse le service en amont, un 500 accuse l'application. Savoir lequel des deux on regarde fait gagner une heure.

Git au-delà de l'usage individuel

Un étudiant arrive avec commit, push, pull. Le travail en équipe demande trois choses de plus.

Les branches. Une branche est un simple pointeur mobile sur un commit. La créer ne copie rien — c'est ce qui rend l'opération instantanée, et ce qui explique qu'on en crée sans compter.

La fusion contre le rebasage. Les deux intègrent le travail d'une branche dans une autre, et produisent des historiques de formes différentes.

merge                              rebase                                   A───B───C───────M                  A───B───C───D'──E'     \         /                         D───────E                    les commits sont REJOUÉS                                   au-dessus de C

merge préserve l'histoire telle qu'elle s'est produite, au prix d'un graphe qui se ramifie. rebase réécrit les commits pour donner une ligne droite, plus lisible — mais il crée de nouveaux commits, ce qui donne la seule règle absolue du chapitre : on ne rebase jamais une branche déjà partagée avec d'autres. Les commits d'origine disparaissent, et les collègues qui les avaient récupérés se retrouvent avec un historique incompatible.

L'usage courant : rebaser sa branche locale sur la principale avant de proposer sa contribution, puis fusionner. On obtient un historique lisible sans réécrire ce que d'autres ont déjà.

Les conflits. Ils surviennent quand deux branches modifient les mêmes lignes. Git ne peut pas trancher et vous laisse le fichier avec les deux versions marquées. Le point important est statistique : la probabilité de conflit croît avec la durée de vie de la branche. Une branche fusionnée le jour même entre rarement en conflit ; une branche de trois semaines touche des fichiers qui ont bougé entre-temps.

Deux stratégies de branchement

C'est le lien direct avec le chapitre 1, et le choix a des conséquences mesurables.

GitFlow organise le travail autour de branches durables : main, develop, une branche par fonctionnalité, des branches de version et de correctif. C'est structuré, adapté à des versions livrées — un logiciel installé chez des clients, avec plusieurs versions maintenues en parallèle.

Trunk-based ne garde qu'une branche principale, dans laquelle chacun intègre au moins une fois par jour, à l'aide de branches de très courte durée. Les fonctionnalités inachevées sont masquées par des drapeaux de fonctionnalité plutôt que par des branches.

GitFlowTrunk-based
Durée de vie d'une branchesemainesheures à un jour
Taille des lotsgrossepetite
Conflitsfréquents et largesrares et étroits
Compatible livraison continuedifficilementoui
Adapté àversions installées chez des clientsservice en ligne

Le rapprochement avec le chapitre 1 est immédiat : une branche de trois semaines est un lot de trois semaines. Toute la logique DORA — petits lots, rétroaction courte, retour arrière facile — pousse vers le trunk-based dès qu'on livre en continu. Ce n'est pas une question de mode mais de cohérence : un pipeline qui déploie plusieurs fois par jour ne peut pas cohabiter avec des branches qui vivent un mois.

La revue de code

Une demande de fusion (pull request) n'est pas une formalité administrative : c'est le point où trois choses se produisent — un second regard sur la correction, une diffusion de la connaissance dans l'équipe, et le déclenchement automatique du pipeline du chapitre 5.

Trois règles d'usage, et la première explique les deux autres.

Petit. Une demande de deux cents lignes est relue sérieusement ; une de deux mille est approuvée sans être lue. Le taux de détection s'effondre au-delà de quelques centaines de lignes, et c'est encore un argument pour les petits lots.

Rapide. Une demande qui attend deux jours bloque son auteur et se désynchronise de la branche principale — donc conflits.

Sur le code, pas sur la personne. « Cette boucle relit la base à chaque tour » plutôt que « tu n'as pas réfléchi ». C'est le post-mortem sans blâme du chapitre 1, appliqué à la relecture.

Quiz · 1 question

Pourquoi ne rebase-t-on jamais une branche déjà poussée et partagée ?

  • Parce que le rebasage est plus lent que la fusion sur un dépôt distantlenteur
  • Parce que rebase RÉÉCRIT les commits : les originaux disparaissent, et les collègues qui les avaient récupérés se retrouvent avec un historique incompatibleréécriture de l'historique
  • Parce que Git refuse techniquement de rebaser une branche distanterefus technique

Réponse : Rebase ne déplace pas les commits : il les REJOUE au-dessus d'une autre base, ce qui produit de NOUVEAUX commits, avec de nouveaux identifiants. Les originaux deviennent inatteignables. Tant que la branche est locale, cela n'a aucune conséquence — c'est même l'usage recommandé, rebaser sa branche sur la principale avant de proposer sa contribution, pour obtenir un historique lisible. Mais si un collègue avait déjà récupéré les commits d'origine, son dépôt contient une histoire que le vôtre ne connaît plus : au prochain échange, Git voit deux lignées divergentes, et la résolution est pénible. Git ne l'interdit pas techniquement — il l'autorise, et c'est bien le problème, la protection étant une règle d'équipe et non un garde-fou de l'outil. La formule à retenir : on réécrit ce qu'on n'a pas encore partagé, jamais ce que d'autres ont déjà.

Quiz · 1 question

Une équipe qui déploie plusieurs fois par jour maintient des branches de fonctionnalité vivant trois semaines. Où est l'incohérence ?

  • Nulle part : les branches longues et le déploiement fréquent sont deux sujets indépendantsindépendants
  • Une branche de trois semaines EST un lot de trois semaines : conflits larges, intégration risquée, retour arrière coûteux — exactement ce que la fréquence de déploiement était censée supprimerla branche est le lot
  • L'incohérence est technique : Git ne sait pas fusionner une branche de plus de deux semaineslimite de Git

Réponse : La fréquence de DÉPLOIEMENT ne dit rien à elle seule : ce qui compte est la fréquence d'INTÉGRATION. Une équipe qui déploie dix fois par jour la branche principale, mais dont chaque contribution y arrive après trois semaines d'isolement, subit exactement les maux du chapitre 1 : gros lots, conflits larges, incapacité à savoir quelle modification a cassé quoi. Le déploiement fréquent ne fait que rendre visible plus tôt un problème constitué depuis longtemps. C'est le raisonnement qui pousse au trunk-based : des branches de quelques heures, une intégration au moins quotidienne, et des drapeaux de fonctionnalité pour masquer l'inachevé plutôt que des branches pour l'isoler. Git n'a par ailleurs aucune limite de durée — la difficulté est statistique, la probabilité de conflit croissant avec le temps pendant lequel les fichiers bougent des deux côtés.

À vous

L'exercice simule un dépôt et rend visible ce que la ligne de commande cache.

D'abord la forme de l'historique : les mêmes contributions intégrées par fusion puis par rebasage, avec le graphe affiché dans les deux cas. Vous constaterez que le contenu final est identique et que les identifiants de commits, eux, ont changé — d'où la règle.

Ensuite un simulateur de stratégie de branchement : vous faites varier la durée de vie des branches et le nombre de contributeurs, et le programme compte les conflits, la taille des lots intégrés et le délai moyen d'intégration. Les chiffres de GitFlow et du trunk-based se séparent nettement, et ils se relient directement aux indicateurs DORA du chapitre 1.

Exercice de code

Comparez fusion et rebasage sur le même historique, puis chiffrez l'effet de la durée de vie des branches.

Point de départ

// ── 1. Fusion contre rebasage ─────────────────────────────────────────────
// Un commit : identifiant, message, parents.
let compteur = 0;
const commit = (msg, parents) => ({ id: "c" + (++compteur), msg, parents });

function creerDepot() {
  const a = commit("A : socle", []);
  const b = commit("B : lecture config", [a]);
  const c = commit("C : correctif principal", [b]);      // sur main
  const d = commit("D : début fonctionnalité", [b]);     // sur la branche
  const e = commit("E : fin fonctionnalité", [d]);
  return { a, b, c, d, e };
}

function fusionner(depot) {
  // La fusion CONSERVE d et e tels quels, et crée un commit à deux parents.
  const m = commit("M : fusion de la branche", [depot.c, depot.e]);
  return [depot.a, depot.b, depot.c, depot.d, depot.e, m];
}

function rebaser(depot) {
  // ← à écrire : rejouer d et e AU-DESSUS de c. Ce sont de NOUVEAUX commits.
  return [];
}

function afficher(nom, commits) {
  console.log("   " + nom);
  for (const c of commits) {
    const p = c.parents.map((x) => x.id).join(", ") || "—";
    console.log("      " + c.id.padEnd(4) + c.msg.padEnd(28) + "parents : " + p);
  }
}

// ── 2. Effet de la durée de vie des branches ──────────────────────────────
// Modèle simple : chaque jour, chaque contributeur produit une modification.
// Une branche accumule ses modifications jusqu'à son intégration. Le risque
// de conflit croît avec le nombre de modifications faites AILLEURS pendant
// qu'elle vivait.
function simuler({ contributeurs, jours, dureeBranche }) {
  const modifsParJour = contributeurs;
  const integrations = Math.floor(jours / dureeBranche) * contributeurs;
  const tailleLot = dureeBranche;                    // modifications par lot
  const modifsAilleurs = 0;                          // ← à écrire
  const probaConflit = 0;                            // ← à écrire
  return { integrations, tailleLot, modifsAilleurs, probaConflit,
           delaiIntegration: dureeBranche / 2 };
}

// ── À VOUS ────────────────────────────────────────────────────────────────
// 1. Écrivez rebaser(). Comparez les identifiants : que constate-t-on, et
//    pourquoi ne rebase-t-on pas une branche partagée ?
// 2. Complétez simuler() : pendant qu'une branche vit d jours, les autres
//    contributeurs produisent (contributeurs − 1) × d modifications.
//    La probabilité de conflit croît avec ce nombre.
// 3. Comparez GitFlow (branches de 15 jours) et trunk-based (0,5 jour) sur
//    six contributeurs et trois mois. Reliez aux indicateurs DORA.

const d = creerDepot();
afficher("après fusion", fusionner(d));

Solution

let compteur = 0;
const commit = (msg, parents) => ({ id: "c" + (++compteur), msg, parents });

function creerDepot() {
  const a = commit("A : socle", []);
  const b = commit("B : lecture config", [a]);
  const c = commit("C : correctif principal", [b]);
  const d = commit("D : début fonctionnalité", [b]);
  const e = commit("E : fin fonctionnalité", [d]);
  return { a, b, c, d, e };
}

function fusionner(depot) {
  const m = commit("M : fusion de la branche", [depot.c, depot.e]);
  return [depot.a, depot.b, depot.c, depot.d, depot.e, m];
}

function rebaser(depot) {
  // On ne DÉPLACE pas d et e : on les REJOUE au-dessus de c, ce qui produit
  // deux commits neufs. Les originaux deviennent inatteignables.
  const dPrime = commit("D' : début fonctionnalité (rejoué)", [depot.c]);
  const ePrime = commit("E' : fin fonctionnalité (rejoué)", [dPrime]);
  return [depot.a, depot.b, depot.c, dPrime, ePrime];
}

function afficher(nom, commits) {
  console.log("   " + nom);
  for (const c of commits) {
    const p = c.parents.map((x) => x.id).join(", ") || "—";
    console.log("      " + c.id.padEnd(5) + c.msg.padEnd(36) + "parents : " + p);
  }
}

console.log("— 1. deux façons d'intégrer le même travail —");
const d1 = creerDepot();
afficher("FUSION : l'histoire est conservée, le graphe se ramifie", fusionner(d1));
console.log("");
const d2 = creerDepot();
const apres = rebaser(d2);
afficher("REBASAGE : une ligne droite, mais des commits NEUFS", apres);
console.log("");
console.log("   Le contenu final est le même. Les identifiants, non : D et E");
console.log("   n'existent plus, remplacés par D' et E'. Tant que la branche est");
console.log("   locale, personne ne s'en aperçoit — c'est l'usage recommandé. Si");
console.log("   un collègue avait déjà récupéré D et E, son dépôt contient une");
console.log("   histoire que le vôtre ne connaît plus, et Git voit deux lignées");
console.log("   divergentes. On réécrit ce qu'on n'a pas partagé, jamais l'inverse.");

console.log("");
console.log("— 2 et 3. l'effet de la durée de vie des branches —");
function simuler({ nom, contributeurs, jours, dureeBranche }) {
  const integrations = Math.round((jours / dureeBranche) * contributeurs);
  const tailleLot = dureeBranche;
  // Pendant qu'une branche vit d jours, les AUTRES produisent leurs propres
  // modifications sur la même base : c'est là que naissent les conflits.
  const modifsAilleurs = (contributeurs - 1) * dureeBranche;
  const probaConflit = Math.min(0.95, 1 - Math.pow(0.97, modifsAilleurs));
  return { nom, integrations, tailleLot, modifsAilleurs, probaConflit,
           delaiIntegration: dureeBranche / 2 };
}

const scenarios = [
  { nom: "GitFlow (branches de 15 j)", contributeurs: 6, jours: 90, dureeBranche: 15 },
  { nom: "intermédiaire (3 j)",        contributeurs: 6, jours: 90, dureeBranche: 3 },
  { nom: "trunk-based (0,5 j)",        contributeurs: 6, jours: 90, dureeBranche: 0.5 },
];
console.log("   stratégie                  | intégrations | modifs/lot | conflit | délai");
for (const s of scenarios) {
  const r = simuler(s);
  console.log("   " + r.nom.padEnd(26) + " | " + String(r.integrations).padStart(12) +
    " | " + String(r.tailleLot).padStart(10) +
    " | " + (r.probaConflit * 100).toFixed(0).padStart(6) + " %" +
    " | " + r.delaiIntegration.toFixed(1).padStart(4) + " j");
}
console.log("   Six contributeurs, trois mois, même volume de travail. Seule change");
console.log("   la durée de vie des branches — et l'on retrouve exactement les");
console.log("   quatre indicateurs du chapitre 1 : la taille des lots gouverne le");
console.log("   délai d'intégration, la fréquence, et le risque.");
console.log("   Une branche de trois semaines EST un lot de trois semaines : un");
console.log("   pipeline qui déploie dix fois par jour n'y change rien, il rend");
console.log("   seulement visible plus tôt un problème constitué depuis longtemps.");

En travaux pratiques

Travaux pratiques 2 · 2 h

Mise en place du dépôt et diagnostic réseau

Lancer l'application du fil rouge à la main, diagnostiquer une erreur de réseau, puis installer la stratégie de branchement qui servira tout le semestre.

Avant de commencer

  • Une machine Linux ou un WSL, avec Git, Node et curl
  • L'archive de l'application tickets fournie par l'enseignant

Énoncé

  1. Lancer l'application, et échouerDémarrez l'application fournie, puis tentez de l'atteindre depuis un autre terminal avec curl. Elle ne répond pas. Ne modifiez rien tant que vous n'avez pas suivi les quatre questions du diagnostic. Indice : Le nom se résout-il ? Le port est-il écouté, et sur quelle interface ? La requête arrive-t-elle jusqu'au service ? Que répond-elle ?
  2. Identifier l'interface d'écouteListez ce qui écoute sur la machine et comparez l'adresse d'écoute à celle que vous interrogez. Corrigez la configuration, sans toucher au code métier. Indice : ss -tlnp montre l'adresse ET le port. Une application liée à 127.0.0.1 est injoignable de l'extérieur.
  3. Vérifier les droitsLe script de démarrage fourni n'est pas exécutable. Rendez-le exécutable pour son seul propriétaire, sans donner de droit d'écriture au groupe ni aux autres. Écrivez la valeur octale que vous employez et justifiez-la.
  4. Créer le dépôt et sa stratégieInitialisez un dépôt, poussez-le, protégez la branche principale, et écrivez un fichier CONTRIBUTING.md qui fixe la stratégie de branchement : durée de vie maximale d'une branche, taille maximale d'une demande de fusion, et délai maximal de relecture.
  5. Fusion contre rebasage, sur un vrai conflitÀ deux, modifiez la même ligne du même fichier sur deux branches. Intégrez la première par fusion, la seconde par rebasage. Comparez les deux historiques obtenus, puis dites laquelle des deux opérations vous vous interdiriez si la branche avait déjà été poussée. Indice : git log --graph --oneline --all rend la différence de forme immédiatement visible.

C'est réussi quand

  • curl obtient une réponse depuis un autre terminal, et vous savez dire quelle question du diagnostic a révélé la cause
  • Le script démarre et ses droits sont justifiés
  • La branche principale refuse un push direct
  • Vous savez énoncer la règle sur le rebasage d'une branche partagée

Correction

Le diagnostic, dans l'ordreterminal
dig +short localhost           # 1. le nom se résout
ss -tlnp | grep 3000           # 2. LISTEN 127.0.0.1:3000  <-- la cause
curl -v http://localhost:3000  # 3. la requête n'arrive pas
# rien dans les journaux du service : le problème est en amont

L'écoute sur 127.0.0.1 est le premier bogue de tout le monde, et il ressemble à une panne de pare-feu. La correction est de lier le service à 0.0.0.0, c'est-à-dire toutes les interfaces — ce qui sera indispensable dès le conteneur du chapitre 3.

Droits du scriptterminal
chmod 744 demarrer.sh
# 7 = rwx pour le propriétaire
# 4 = r-- pour le groupe et les autres : lecture seule

700 conviendrait aussi si personne d'autre n'a besoin de lire le script. Ce qu'il faut savoir justifier, c'est pourquoi on n'écrit pas 777 : donner le droit d'écriture à tout le monde sur un fichier exécuté permet à n'importe qui d'y placer ce qu'il veut.

Stratégie de branchementCONTRIBUTING.md
## Branches
- Une branche par tâche, DURÉE DE VIE MAXIMALE : 1 jour ouvré.
- Intégration dans main au moins une fois par jour.
- Le travail inachevé est masqué par un drapeau de fonctionnalité,
jamais isolé dans une branche longue.

## Demandes de fusion
- 400 lignes modifiées au maximum ; au-delà, découper.
- Relecture sous 4 h ouvrées.
- Rebasage sur main AVANT la demande, jamais après partage.

Les trois nombres sont des décisions d'équipe, pas des vérités : ce qui compte est qu'ils soient écrits et tenus. Une branche d'un jour rend les conflits rares et étroits, et c'est le seul moyen de tirer parti du pipeline du chapitre 5.

Ce que montrent les deux historiquesterminal
git log --graph --oneline --all

*   9f2a1c4 (main) Merge branch 'ajout-filtre'     <-- fusion : le graphe fourche
|\
| * 3d8e7b1 ajout du filtre
* | 7c4b9a2 correctif du tri
|/
* 1a2b3c4 socle

* 5e6f7a8 (main) ajout du filtre (rejoué)          <-- rebasage : ligne droite
* 7c4b9a2 correctif du tri
* 1a2b3c4 socle

Le contenu final est identique ; seule la forme diffère. Le commit rejoué porte un nouvel identifiant : les anciens sont devenus inatteignables, et c'est pourquoi on ne rebase jamais une branche que d'autres ont déjà récupérée. La pratique courante est de rebaser sa branche locale sur main avant de proposer, puis de fusionner.

Ce que la suite en fait

Le bloc II attaque le premier vrai sujet : empaqueter l'application avec tout ce dont elle a besoin, pour que « ça marche sur ma machine » cesse d'être une excuse. Les droits Unix et les signaux de ce chapitre y reviennent dès le premier Dockerfile, et la question du port écouté y devient le premier bogue de tout le monde.

Et la revue de code retrouvera son sens au chapitre 5 : une demande de fusion est ce qui déclenche le pipeline, et le pipeline est ce qui rend la revue supportable — le relecteur n'a plus à vérifier que le code compile et que les tests passent.

À retenir

Flashcards · 5 cartes

Quelle est la différence entre SIGTERM et SIGKILL, et pourquoi compte-t-elle en DevOps ?
SIGTERM demande de s'arrêter PROPREMENT, SIGKILL tue sans préavis et sans possibilité d'y réagir. Docker et Kubernetes envoient un SIGTERM, attendent quelques secondes, puis un SIGKILL : une application qui n'écoute pas SIGTERM perd donc ses requêtes en cours à CHAQUE déploiement, et personne ne comprend d'où viennent les erreurs sporadiques.
Dans quel ordre diagnostique-t-on un « ça ne répond pas » ?
1) LE NOM SE RÉSOUT-IL ? (dig, nslookup) — un DNS mal propagé ressemble à une panne applicative. 2) LE PORT EST-IL ÉCOUTÉ ? (ss -tlnp en local, curl -v ou nc depuis l'extérieur) — un service qui écoute sur 127.0.0.1 marche en local et est injoignable ailleurs. 3) LA REQUÊTE ARRIVE-T-ELLE ? les journaux du serveur le disent ; sinon le problème est en amont (pare-feu, routage). 4) QUE RÉPOND-ELLE ? — et un 502 accuse le service en amont quand un 500 accuse l'application.
Fusion ou rebasage : que produisent-ils, et quelle est la règle absolue ?
MERGE préserve l'histoire telle qu'elle s'est produite, au prix d'un graphe ramifié. REBASE rejoue les commits au-dessus d'une autre base pour donner une ligne droite, plus lisible — mais il crée de NOUVEAUX commits. D'où la règle : on ne rebase JAMAIS une branche déjà partagée, les originaux devenant inatteignables pour ceux qui les avaient récupérés. Usage courant : rebaser sa branche LOCALE sur la principale avant de proposer sa contribution, puis fusionner.
Opposez GitFlow et trunk-based, et reliez au chapitre 1.
GITFLOW : branches durables (main, develop, fonctionnalité, version, correctif), semaines de vie, gros lots, conflits larges — adapté aux versions INSTALLÉES chez des clients, avec plusieurs versions maintenues. TRUNK-BASED : une branche principale, intégration au moins quotidienne, branches de quelques heures, inachevé masqué par des drapeaux de fonctionnalité — adapté au service en ligne. Le lien : une branche de trois semaines EST un lot de trois semaines, avec tous les maux du chapitre 1.
Quelles sont les trois règles d'une demande de fusion, et pourquoi la première commande les autres ?
PETITE : au-delà de quelques centaines de lignes, le taux de détection s'effondre — une demande de deux mille lignes est approuvée sans être lue. RAPIDE : une demande qui attend bloque son auteur et se désynchronise, donc produit des conflits. SUR LE CODE, PAS SUR LA PERSONNE : c'est le post-mortem sans blâme appliqué à la relecture. La taille commande le reste : petite, elle est relue vite et sérieusement.