La vraie différence entre l'intégration continue et le déploiement continu

Il existe de nombreux contenus décrivant ce que sont l'intégration continue, la livraison continue et le déploiement continu. Mais à quels objectifs ces processus servent-ils en premier lieu?

Il est crucial de comprendre les problèmes que CI et CD résolvent pour les utiliser correctement. Cela permettra à votre équipe d'améliorer votre processus et d'éviter de faire des efforts pour rechercher des métriques sophistiquées qui n'apportent aucune valeur à votre processus.

L'intégration continue est un problème d'équipe

Si vous travaillez en équipe, il y a de fortes chances que plusieurs développeurs travaillent sur le même référentiel. Il existe une branche principale dans le référentiel contenant la dernière version du code. Les développeurs travaillent sur différentes choses sur différentes branches. Une fois que quelqu'un a terminé son changement, il le poussera ou le fusionnera dans la branche principale. Finalement, toute l'équipe apportera ce changement.

Le scénario que nous voulons éviter est qu'un commit défectueux parvient à la branche principale. Faulty signifie que le code ne se compile pas ou que l'application ne démarre pas ou est inutilisable. Pourquoi? Pas parce que l'application est cassée ou parce que tous les tests doivent toujours être verts. Ce n'est pas un problème - vous pouvez décider de ne pas déployer cette version et attendre un correctif.

Le problème est que toute votre équipe est bloquée. Tous les développeurs qui ont tiré le commit défectueux passeront 5 minutes à se demander pourquoi cela ne fonctionne pas. Plusieurs essaieront probablement de trouver le commit défectueux. Certains essaieront de résoudre le problème par eux-mêmes en parallèle de l'auteur du code défectueux.

C'est une perte de temps pour votre équipe. Le pire, c'est que des incidents répétés alimentent une méfiance à l'égard de la branche principale et encouragent les développeurs à travailler séparément.

L'intégration continue consiste à empêcher la branche principale de se rompre afin que votre équipe ne soit pas bloquée. C'est ça. Il ne s'agit pas d'avoir tous vos tests verts tout le temps et la branche principale déployable en production à chaque commit.

Le processus d'intégration continue est indépendant de tout outil. Vous pouvez vérifier manuellement que la fusion de votre branche et de la branche principale fonctionne localement, puis pousser uniquement la fusion vers le référentiel. Mais ce serait très inefficace. C'est pourquoi l'intégration continue est mise en œuvre à l'aide de contrôles automatisés.

Les contrôles garantissent qu'au strict minimum:

  • L'application doit se créer et démarrer
  • La plupart des fonctionnalités critiques doivent être fonctionnelles à tout moment (inscription de l'utilisateur / parcours de connexion et principales fonctionnalités commerciales)
  • Les couches communes de l'application sur lesquelles s'appuient tous les développeurs doivent être stables. Cela signifie des tests unitaires sur ces pièces.

En pratique, cela signifie que vous devez extraire n'importe quel framework de test unitaire qui fonctionne pour vous et sécuriser les couches communes de l'application. Parfois, ce n'est pas beaucoup de code et cela peut être fait assez rapidement. Vous devez également ajouter un "test de fumée" vérifiant que le code se compile et que l'application démarre. Ceci est particulièrement important dans les technologies avec des injections de dépendances folles comme Java Spring ou .NET core. Dans les grands projets, il est si facile de mal connecter vos dépendances qu'il est indispensable de vérifier que l'application démarre toujours.

Si vous avez des centaines ou des milliers de tests, vous n'avez pas besoin de tous les exécuter à chaque fusion. Cela prendra beaucoup de temps et la plupart des tests vérifient probablement les fonctionnalités "sans blocage d'équipe".

Nous verrons dans les sections suivantes comment le processus de livraison continue fera bon usage de ces nombreux tests.

Ce n'est pas une question d'outils

Les outils et les contrôles automatisés sont tous très bien. Mais si vos développeurs ne fusionnent que des branches géantes sur lesquelles ils travaillent pendant des semaines, ils ne vous aideront pas. L'équipe passera beaucoup de temps à fusionner les branches et à corriger les incompatibilités de code qui surviendront éventuellement. C'est autant une perte de temps que d'être bloqué par un commit défectueux.

L'intégration continue ne concerne pas les outils. Il s'agit de travailler par petits morceaux et d'intégrer votre nouveau code à la branche principale et de tirer fréquemment.

Fréquemment signifie au moins quotidiennement. Divisez la tâche sur laquelle vous travaillez en tâches plus petites. Fusionnez votre code très souvent et tirez très souvent. De cette façon, personne ne travaille séparément pendant plus d'un jour ou deux et les problèmes n'ont pas le temps de devenir des boules de neige.

Il n'est pas nécessaire qu'une tâche importante se trouve dans une seule branche. Cela ne devrait jamais l'être. Les techniques pour fusionner le travail en cours avec la branche principale sont appelées «branchement par abstraction» et «bascule de fonctionnalité». Consultez l'article de blog Comment démarrer avec l'intégration continue pour plus de détails.

Points clés pour une bonne construction CI

C'est très simple. Soyez bref. 3-7 minutes devraient être le maximum. Ce n'est pas une question de CPU et de ressources. Il s'agit de la productivité des développeurs. La première règle de productivité est la concentration. Faites une chose, terminez-la, puis passez à la chose suivante.

Le changement de contexte est coûteux. Des études montrent qu'il faut environ 23 minutes pour se recentrer profondément sur quelque chose lorsque vous êtes dérangé.

Imaginez que vous poussez votre branche pour la fusionner. Vous démarrez une autre tâche. Vous passez 15-20 minutes à y entrer. La minute après que vous êtes dans la zone, vous recevez une notification "Échec de la construction" de votre build CI de 20 minutes pour la tâche précédente. Vous revenez pour le réparer. Vous le poussez à nouveau. Vous avez facilement perdu plus de 20 minutes de va-et-vient.

Multipliez 20 minutes une à deux fois par jour par le nombre de développeurs de votre équipe ... C'est beaucoup de temps précieux perdu.

Imaginez maintenant si les commentaires sont arrivés dans les 3 minutes. Vous n'auriez probablement pas du tout commencé la nouvelle tâche. Vous auriez une preuve de lire votre code une fois de plus ou révisé un PR en attendant. La notification échouée viendrait et vous la répareriez. Ensuite, vous pouvez passer à la tâche suivante. C'est le genre de focalisation que votre processus devrait permettre.

Garder votre CI courte en fait un compromis. Les tests qui s'exécutent plus longtemps ou fournissent peu de valeur dans le contexte de CI doivent être déplacés vers l'étape CD. Et oui, les pannes doivent également être corrigées. Mais comme ils n'empêchent personne de faire son travail, vous pouvez prendre les correctifs comme "tâche suivante" lorsque vous avez terminé ce que vous faites. Désactivez simplement les notifications pendant que vous travaillez et vérifiez de temps en temps. Gardez le changement de contexte au minimum.

La livraison et le déploiement continus sont des problèmes d'ingénierie

Fixons-nous les définitions pour éliminer cela.

La livraison continue consiste à pouvoir déployer n'importe quelle version de votre code à tout moment. En pratique, cela signifie la dernière ou la pré-dernière version de votre code. Vous ne déployez pas automatiquement, généralement parce que vous n'êtes pas obligé ou limité par le cycle de vie de votre projet. Mais dès que quelqu'un en a envie, un déploiement peut être effectué en un minimum de temps. Ce quelqu'un peut être l'équipe de test / QA qui souhaite tester des choses sur un environnement de pré-production ou de préparation. Ou il est peut-être temps de déployer le code en production.

L'idée de la livraison continue est de préparer les artefacts aussi près que possible de ce que vous souhaitez exécuter dans votre environnement. Il peut s'agir de fichiers .jar ou .war si vous travaillez avec Java, ou d'exécutables si vous travaillez avec .NET. Il peut également s'agir de dossiers de code JS transpilé ou même de conteneurs Docker, ce qui rend le déploiement plus court (c'est-à-dire que vous en avez pré-construit autant que vous le pouvez à l'avance).

En préparant des artefacts, je ne veux pas dire transformer le code en artefacts. Il s'agit généralement de quelques scripts et de quelques minutes d'exécution. Préparer signifie:

Exécutez tous les tests possibles pour vous assurer qu'une fois déployé, le code fonctionnera réellement. Exécutez des tests unitaires, des tests d'intégration, des tests de bout en bout et même des tests de performances si vous pouvez automatiser cela.

De cette façon, vous pouvez filtrer les versions de votre branche principale qui sont réellement prêtes pour la production et celles qui ne le sont pas. La suite de tests idéale:

  • S'assure que les fonctionnalités clés de l'application fonctionnent. Idéalement toutes les fonctionnalités
  • S'assure qu'aucune rupture de contrat de performance n'a été introduite afin que lorsque votre nouvelle version frappe vos nombreux utilisateurs, elle a une chance de durer
  • Exécuter à sec toutes les mises à jour de base de données dont votre code a besoin pour éviter les surprises

Cela n'a pas besoin d'être très rapide. 30 minutes ou 1 heure est acceptable.

Le déploiement continu est la prochaine étape. Vous déployez la version la plus à jour et la plus prête pour la production de votre code dans un environnement. Idéalement production si vous faites suffisamment confiance à votre suite de tests de CD.

Notez que, selon le contexte, cela n'est pas toujours possible ou en vaut la peine. La livraison continue est souvent suffisante pour être productive, en particulier si vous travaillez dans un réseau proche et que vous disposez d'environnements limités dans lesquels vous pouvez effectuer un déploiement. Il se peut également que le cycle de publication de votre logiciel empêche les déploiements non planifiés.

La livraison continue et le déploiement continu (appelons-les désormais CD) ne sont pas des problèmes d'équipe. Il s'agit de trouver le juste équilibre entre le temps d'exécution, les efforts de maintenance et la pertinence de votre suite de tests pour pouvoir dire «Cette version fonctionne comme il se doit».

Et c'est un équilibre. Si vos tests durent 30 heures, c'est un problème. Voir cet article épique sur à quoi ressemble la suite de tests de base de données Oracle Mais si vous passez tellement de temps à maintenir vos tests à jour avec le dernier code que cela entrave la progression de l'équipe, ce n'est pas bon non plus. Aussi, si votre suite de tests n'assure quasiment rien ... c'est fondamentalement inutile.

Dans un monde idéal, nous voulons un ensemble d'artefacts déployables par commit dans la branche principale. Vous pouvez voir que nous avons un problème d'évolutivité verticale: plus nous passons rapidement du code aux artefacts, plus nous sommes prêts à déployer la dernière version du code.

Quelle est la grande différence?

L'intégration continue est un problème d'évolutivité horizontale. Vous voulez que les développeurs fusionnent souvent leur code afin que les vérifications soient rapides. Idéalement en quelques minutes pour éviter que les développeurs ne changent de contexte tout le temps avec un retour hautement asynchrone des builds CI.

Plus vous avez de développeurs, plus vous avez besoin de puissance de calcul pour exécuter des vérifications simples (build et test) sur toutes les branches actives.

Une bonne version de CI: garantit qu'aucun code qui rompt les éléments de base et empêche les autres membres de l'équipe de travailler n'est introduit dans la branche principale, et est suffisamment rapide pour fournir des commentaires aux développeurs en quelques minutes afin d'éviter le changement de contexte entre les tâches.

La livraison et le déploiement continus sont des problèmes d'évolutivité verticale. Vous avez une opération assez complexe à effectuer.

Une bonne construction de CD: garantit que le plus grand nombre de fonctionnalités possible fonctionnent correctement. Le plus vite sera le mieux, mais ce n'est pas une question de vitesse. Une construction de 30 à 60 minutes est OK.

Une idée fausse courante est de voir le CD comme un problème d'évolutivité horizontale comme CI: plus vite vous pouvez passer du code aux artefacts, plus vous pouvez réellement traiter de validations et plus vous vous rapprochez du scénario idéal.

Mais nous n'en avons pas besoin. Produire des artefacts pour chaque commit et aussi vite que possible est généralement exagéré. Vous pouvez très bien aborder le CD au mieux: ayez une seule version de CD qui choisira simplement le dernier commit à vérifier une fois qu'une construction donnée est terminée.

Ne vous méprenez pas sur le CD. C'est vraiment dur. Obtenir une confiance de test suffisante pour dire que votre logiciel est prêt à être déployé automatiquement fonctionne généralement sur des applications à faible surface comme les API ou les interfaces utilisateur simples. C'est très difficile à réaliser sur une interface utilisateur complexe ou un grand système monolithique.

Conclusion

Les outils et principes utilisés pour exécuter CI et CD sont souvent très similaires. Les objectifs sont cependant très différents.

L'intégration continue est un compromis entre la vitesse de la boucle de rétroaction aux développeurs et la pertinence des contrôles que vous effectuez (build et test). Aucun code qui entraverait la progression de l'équipe ne devrait atteindre la branche principale.

La livraison continue du déploiement consiste à exécuter des contrôles aussi approfondis que possible pour détecter les problèmes sur votre code. L'exhaustivité des contrôles est le facteur le plus important. Il est généralement mesuré en termes de couverture de code ou de couverture fonctionnelle de vos tests. La détection précoce des erreurs empêche le code cassé d'être déployé dans n'importe quel environnement et fait gagner un temps précieux à votre équipe de test.

Créez vos builds CI et CD pour atteindre ces objectifs et maintenir la productivité de votre équipe. Aucun flux de travail n'est parfait. Des problèmes surgiront de temps en temps. Utilisez-les comme des leçons apprises pour renforcer votre flux de travail à chaque fois.

Publié le 27 nov.2019 sur le blog Fire CI.