Introduction

L'intégration continue est une pratique de développement logiciel où les membres d'une équipe intègrent leur travail fréquemment. En général, chacun intègre au moins quotidiennement - conduisant à de multiples intégrations par jour. Chaque intégration est vérifiée par un système automatisé de construction (y compris les tests) pour détecter les erreurs d'intégration le plus rapidement possible. Beaucoup d'équipes trouvent que cette approche conduit à une réduction considérable des problèmes d'intégration et permet à une équipe de développer un logiciel de qualité plus rapidement. Cet article est un aperçu rapide de l'intégration continue. Il résume la technique et son usage actuel.

Pour plus d'information sur le sujet, jetez un coup d'oeil à mon guide pour le déploiement. ThoughtWorks, mon employeur, propose une offre de conseil et de support autour de l'intégration continue. Le logiciel open source CruiseControl, le premier serveur d'intégration continue, a été initialement créé par ThoughtWorks. Récemment, ThoughtWorks Studios, notre suite de produits, a publié Go - un nouveau serveur d'intégration et de livraison continues. Il permet des constructions en parallèle avec de nombreux projets, un superbe tableau de bord, et des déploiements automatisés. C'est un outil commercial, mais libre d'utilisation pour de petites configurations.

Je me souviens très clairement d'une des premières fois où j'ai vu un grand projet de logiciel. Je faisais un stage d'été dans une grande entreprise d'électronique anglaise. Mon manager, qui faisait partie du service d'assurance qualité, m'a fait visiter un site et nous sommes entrés dans un immense entrepôt complètement rempli de cubes. On m'a dit que ce projet était en développement depuis plusieurs années et était actuellement en intégration depuis plusieurs mois. Mon guide me dit que personne ne savait vraiment combien de temps il faudrait pour terminer l'intégration. J'ai appris là une histoire classique de projets de logiciels : l'intégration est un processus long et imprévisible.
Mais il ne devrait pas l'être. La plupart des projets réalisés par mes collègues à ThoughtWorks, et par beaucoup d'autres à travers le monde, traitent l'intégration comme un non-événement. Tout travail personnel d'un développeur est seulement à quelques heures d'un état de projet partagé et peut être réintégré dans cet état en quelques minutes. Toute erreur d'intégration est détectée rapidement et peut être corrigée rapidement.
Ce contraste n'est pas le résultat d'un outil coûteux et complexe. Son essence réside dans le simple fait que chacun dans l'équipe intègre fréquemment, en général tous les jours, sur un dépôt de code source.
Lorsque je décris cette pratique aux gens, je trouve souvent deux réactions : « ça ne peut pas marcher (ici) » et « le faire ne fera pas beaucoup de différence ». Ce que les gens découvrent en essayant, c'est que c'est beaucoup plus facile qu'il n'y paraît, et que cela fait une différence énorme pour le développement. La troisième réaction habituelle est « oui, faisons-le - Comment pourrions-nous vivre sans ça ? »
Le terme « intégration continue » est apparu avec la méthode de développement d'Extreme Programming, en tant que l'une de ses douze pratiques. Quand j'ai commencé à ThoughtWorks, en tant que consultant, j'ai demandé à ce que le projet sur lequel je travaillais utilise cette technique. Matthieu Foemmel a tourné mes exhortations en une vraie action et nous avons vu le projet aller d'intégrations rares et complexes en un non-événement, comme je l'ai décrit. Matthieu et moi avons écrit notre expérience dans la version originale de cet article, qui est l'un des plus populaires sur mon site.
Bien que l'intégration continue soit une pratique qui ne nécessite aucun outillage particulier à déployer, nous avons constaté qu'il est utile de passer par un serveur d'intégration continue. Le serveur le plus connu est CruiseControl, un outil open source construit à l'origine par plusieurs personnes à ThoughtWorks et aujourd'hui maintenu par une large communauté. Depuis lors, plusieurs autres serveurs d'intégration continue sont apparus, à la fois open source et commerciaux - y compris Cruise de ThoughtWorks Studios.

Construire une fonctionnalité avec l'intégration continue

La meilleure façon pour moi d'expliquer ce qu'est l'intégration continue et comment elle fonctionne est de montrer un exemple avec le développement d'une petite fonctionnalité. Supposons que je doive faire quelque chose pour une partie d'un logiciel. Ce que fait la tâche n'est pas vraiment important. Pour l'instant, je suppose qu'elle est simple et qu'elle peut être réalisée en quelques heures.
Je commence par récupérer une copie des sources sur ma machine de développement. Je le fais en utilisant un système de gestion de code source en réservant une copie de travail de l'activité principale.
Le paragraphe ci-dessus aura un sens pour les personnes qui utilisent un système de gestion de code source, mais sera du charabia pour ceux qui n'en utilisent pas. Alors laissez-moi vous expliquer rapidement ce que c'est. Un système de gestion de code source conserve tout le code source d'un projet dans un référentiel. L'état actuel du système est généralement désigné comme « l'activité principale ». À tout moment, un développeur peut récupérer une copie de l'activité principale sur sa propre machine, c'est ce que l'on appelle « extraire ». La copie sur l'ordinateur du développeur est appelée une « copie de travail ». (La plupart du temps vous mettez à jour votre copie de travail sur l'activité principale.)
Maintenant, je prends ma copie de travail et je fais ce que je dois faire pour réaliser ma tâche. Cela consiste à modifier le code de production et ajouter ou modifier des tests automatisés. L'intégration continue suppose qu'un degré élevé de tests soit automatisé dans le logiciel : une caractéristique que j'appelle le code autotestant. Souvent ces tests utilisent les frameworks populaires de type XUnit.
Une fois que j'ai terminé (et en général plusieurs fois, quand je travaille), j'exécute une construction automatisée sur ma machine de développement. Elle prend le code source de ma copie de travail, le compile en un exécutable et effectue les tests automatisés. Seulement si l'ensemble compile et passe les tests sans erreur, la construction est considérée comme valide.
Avec une construction valide, je peux alors penser à propager mes changements dans le référentiel. Le revers, bien sûr, c'est que d'autres personnes peuvent, et en général ont apporté des modifications à l'activité principale avant que j'aie pu propager les miennes. Dans ce cas, je mets d'abord ma copie de travail à jour avec leurs modifications et je reconstruis. Si leurs modifications sont incompatibles avec mes changements, cela va se manifester par l'échec de la construction, soit dans la compilation, soit dans les tests. Dans ce cas, je suis responsable de la résolution des problèmes jusqu'à ce que je puisse construire une copie de travail qui soit correctement synchronisée avec l'activité principale.
Une fois que j'ai une construction d'une copie de travail valide et synchrone, je peux propager mes changements dans l'activité principale.

Cependant, cette propagation n'est pas tout. À ce moment-là, nous construisons à nouveau, mais cette fois sur une machine d'intégration basée sur le code source de l'activité principale. Ce n'est qu'une fois que cette construction réussit qu'on peut dire que mes modifications sont terminées. Il y a toujours une possibilité que j'aie manqué quelque chose sur ma machine et que le référentiel n'ait pas été correctement mis à jour. Ce n'est que lorsque mes changements se construisent avec succès sur la machine d'intégration que mon travail est terminé. Cette construction d'intégration peut être effectuée manuellement, par moi, ou automatiquement par Cruise.
S'il y a un conflit entre deux modifications, il est en général mis en évidence lorsque le second développeur construit sa copie de travail mise à jour. S'il ne le fait pas, la construction d'intégration devrait échouer. Dans tous les cas, l'erreur est détectée rapidement. À ce point, le plus important est de corriger le conflit, et faire en sorte que la construction soit à nouveau valide. Dans un environnement d'intégration continue, un échec de la construction ne devrait jamais durer longtemps. Une bonne équipe devrait avoir de nombreuses constructions valides par jour. Les échecs de construction peuvent se produire de temps en temps, mais ils doivent être rapidement corrigés.
Le résultat de tout ça est un logiciel stable qui fonctionne et contient peu d'anomalies. Chacun développe hors de cette base commune stable et n'est qu'à quelques opérations d'une intégration. Moins de temps est passé à essayer de trouver des anomalies, car elles sont rapidement mises en évidence.

Les pratiques de l'intégration continue

L'histoire ci-dessus est une vue d'ensemble de l'intégration continue et son fonctionnement dans la vie quotidienne. Mettre tout cela en place est évidemment un peu plus compliqué. Je vais détailler maintenant les pratiques clés qui rendent l'intégration continue efficace.

Maintenir un dépôt unique de code source versionné

Les projets de logiciels impliquent beaucoup de fichiers qui doivent être assemblés pour construire un produit. Garder une trace de tout cela est un effort majeur, en particulier quand il y a plusieurs personnes impliquées. Il n'est donc pas surprenant qu'au fil des années, des équipes de développement logiciel ont construit des outils pour gérer tout cela. Ces outils - appelés outils de gestion de code source, gestion de configuration, systèmes de contrôle de version, référentiels ou divers autres noms - sont une partie intégrante de la plupart des projets de développement. Ce qui est triste et surprenant, c'est qu'ils ne sont pas mis en place sur tous les projets. C'est rare, mais il m'arrive de prendre part à des projets qui n'utilisent pas un tel système mais utilisent une combinaison désordonnée de disques locaux et partagés.
Alors pour commencer, équipez-vous d'un système de gestion de code source digne de ce nom. Le coût n'est pas un problème car des outils de bonne qualité et open source sont disponibles. L'outil de choix en ce moment est Subversion. (L'outil open source CVS est encore largement utilisé, et est beaucoup mieux que rien, mais Subversion est plus récent.) Lorsque je parle à des développeurs que je connais, Subversion est préféré à la plupart des outils commerciaux de gestion de code source. Le seul outil commercial dont j'ai toujours entendu les gens dire qu'il vaut la peine d'être acheté est Perforce.
Une fois que vous avez votre système de gestion de code source, assurez-vous que tout le monde connait son emplacement pour y récupérer les codes source. Personne ne devrait plus jamais demander « où est le fichier trucmuche ? » Tout doit être dans le référentiel.
Bien que de nombreuses équipes utilisent des référentiels, une erreur commune est qu'elles ne mettent pas tout dans le référentiel. Celles qui l'utilisent y mettent le code, mais tout ce dont vous avez besoin pour effectuer une construction doit y être aussi, y compris : les scripts de test, les fichiers de propriétés, les schémas de base de données, les scripts d'installation et les bibliothèques tierces. J'ai connu des projets qui stockaient leur compilateur dans le référentiel (c'était important avec les premiers compilateurs C++, car ils n'étaient pas très fiables). La règle de base est que vous devriez être capable de dérouler tout le processus : à partir d'une machine vierge, faire une extraction et être en mesure de construire le système entier. Seule une quantité minime de choses devrait être sur la machine vierge - généralement des choses qui sont grandes, compliquées à installer et stables. Un système d'exploitation, l'environnement de développement Java, ou un système de gestion de bases de données, par exemple.
Vous devez mettre tout le nécessaire pour une construction dans le système de gestion de code source, mais vous pouvez également mettre d'autres choses avec lesquelles les personnes ont l'habitude de travailler. Y mettre la configuration d'un environnement de développement est une bonne idée, car de cette manière, il est facile de partager les mêmes configurations.
Une des caractéristiques des systèmes de contrôle de version est qu'ils vous permettent de créer plusieurs branches, pour gérer plusieurs flux de développement différents. Ceci est une fonctionnalité utile, voire essentielle - mais elle est fréquemment surexploitée et peut causer des problèmes. Utilisez les branches au minimum. Ayez une activité principale et une seule branche du projet actuellement en cours de développement. Pratiquement tout le monde doit travailler sur cette activité principale la plupart du temps. (Les branches raisonnables sont des corrections d'anomalies des versions de production et des expérimentations temporaires.)
En général, vous devez stocker dans le contrôle de code source tout ce dont vous avez besoin pour construire quelque chose, mais rien de ce que vous construisez réellement. Certaines personnes stockent les produits construits dans le contrôle de code source, mais je considère que c'est une mauvaise pratique - une indication d'un problème plus profond, le plus souvent une incapacité à recréer de manière fiable le produit construit.

Automatiser la construction

Transformer les sources en un système fonctionnel peut souvent être un processus complexe comprenant la compilation, le déplacement de fichiers, le chargement des schémas dans les bases de données, et ainsi de suite. Cependant, comme la plupart des tâches décrites dans cette partie du développement logiciel, cela peut être automatisé - et par conséquent doit être automatisé. Demander aux gens de taper des commandes étranges ou de cliquer dans des boîtes de dialogue est un gaspillage de temps et un terrain fertile aux erreurs.
Les environnements automatisés de construction sont une caractéristique commune des systèmes. Le monde Unix en a depuis des décennies, la communauté Java a développé Ant, la communauté .NET a NAnt et maintenant MSBuild.
Assurez-vous que vous pouvez construire et lancer votre système en utilisant ces scripts en utilisant une seule commande.
Une erreur commune est de ne pas tout inclure dans le système automatisé de construction. La construction devrait inclure la récupération des schémas de la base de données du dépôt et l'envoyer dans l'environnement d'exécution. Je vais détailler mon astuce : quiconque devrait être en mesure d'apporter une machine vierge, récupérer les sources du référentiel, exécuter une seule commande, et avoir un système fonctionnant sur sa machine.
Les scripts de construction se déclinent en plusieurs parfums et sont souvent propres à une plateforme ou une communauté, mais ils n'ont pas à l'être. Bien que la plupart de nos projets Java utilisent Ant, certains ont utilisé Ruby (le système Rake Ruby est un outil de script très bien fait). Nous avons eu beaucoup de valeur ajoutée à automatiser un début de projet Microsoft COM avec Ant.
Une grande construction prend souvent du temps, vous ne voulez pas faire toutes ces étapes, si vous avez seulement fait un petit changement. Donc un bon outil de construction analyse ce qui doit être changé dans le cadre du processus. La façon courante de faire cela est de vérifier les dates des sources et fichiers et ne compiler que si la date de la source est plus récente. Mais les dépendances peuvent compliquer la chose : si un fichier change l'une d'elles, elle a aussi besoin d'être reconstruite. Les compilateurs peuvent gérer ce genre de chose, ou pas.
Selon ce dont vous avez besoin, vous aurez peut-être besoin de construire différentes sortes de choses. Vous pouvez construire un système avec ou sans code de test, ou avec différents ensembles de tests. Certains composants peuvent être construits en stand-alone. Un script de compilation devrait vous permettre de construire d'autres cibles pour les différents cas.
Beaucoup d'entre nous utilisent des environnements de développement intégrés (IDE), et la plupart ont une sorte de processus de gestion de construction. Toutefois, ces fichiers sont toujours la propriété de l'IDE et souvent fragiles. En outre, ils ont besoin que l'IDE soit ouvert. C'est bien pour les utilisateurs de créer leurs fichiers de projet et de les utiliser pour leur développement. Cependant, il est essentiel d'avoir une construction maîtresse qui est utilisable sur un serveur et exécutable à partir d'autres scripts. Ainsi, sur un projet Java les développeurs peuvent construire dans leur IDE, mais la construction maîtresse utilise Ant pour s'assurer qu'elle peut être exécutée sur le serveur d'intégration.

Rendre la construction autotestante

Traditionnellement, une construction signifie compiler, lier et toutes les autres tâches requises pour obtenir un programme exécutable. Un programme peut fonctionner, mais cela ne signifie pas qu'il fait la bonne chose. Les langages modernes fortement typés peuvent éviter de nombreuses anomalies, mais beaucoup plus glissent à travers ces mailles.
Une bonne façon de trouver les anomalies plus rapidement et efficacement est d'inclure des tests automatisés dans le processus de construction. Tester n'est pas parfait, bien sûr, mais peut révéler beaucoup d'anomalies - assez pour être utile. En particulier la montée de l'Extreme Programming (XP) et du Test Driven Development (TDD) ont fait beaucoup pour populariser le code autotestant et par conséquent beaucoup de gens ont vu la valeur de cette méthode.
Les lecteurs réguliers de mon travail savent que je suis un grand fan de TDD et XP, cependant je tiens à souligner qu'aucune de ces approches n'est nécessaire pour profiter des avantages du code autotestant. Ces deux approches font que les tests sont écrits avant le code. Code qui les rend ensuite valides - dans ce mode, les tests permettent aussi bien l'exploration de la conception du système qu'ils permettent de mettre en évidence les anomalies. C'est une bonne chose, mais elle n'est pas nécessaire pour l'intégration continue, où nous avons la plus faible exigence pour le code autotestant. (Mais TDD est ma façon préférée de produire du code autotestant.)
Pour le code autotestant, vous avez besoin d'une suite de tests automatisés qui puisse protéger une grande partie du code contre les anomalies. Les tests doivent pouvoir être démarrés à partir d'une simple commande et être auto-validants. Le résultat de l'exécution de la suite de tests devrait indiquer si des tests ont échoué. Pour qu'une construction soit autotestante, l'échec d'un test doit provoquer l'échec de la construction. Au cours des dernières années, la montée de TDD a popularisé la famille XUnit, des outils open source qui sont idéaux pour ce genre de tests. Les outils XUnit se sont révélés très précieux pour nous à ThoughtWorks et je suggère toujours aux gens de les utiliser. Ces outils, dont est à l'origine Kent Beck, rendent très facile la mise en place d'un environnement entièrement autotestant.
Les outils XUnit sont certainement le point de départ pour rendre votre code autotestant. Vous devriez également regarder d'autres outils qui mettent l'accent sur des tests de bout en bout, il y en a toute une gamme, y compris FIT, Selenium, Sahi, Watir, FITnesse, et bien d'autres dont je ne tenterai pas d'en faire une liste exhaustive ici.
Bien sûr, vous ne pouvez pas compter sur des tests pour trouver toutes les anomalies. Comme il est souvent dit : les tests ne prouvent pas l'absence d'anomalies. Cependant la perfection n'est pas le seul point que vous bénéficiez d'une construction autotestante. Des tests imparfaits, exécutés souvent, sont beaucoup mieux que des tests qui ne sont jamais écrits.

Tout le monde propage sur l'activité principale chaque jour

L'intégration est avant tout une question de communication. L'intégration permet aux développeurs d'informer les autres développeurs des changements qu'ils ont apportés. Une communication fréquente permet aux gens de connaître rapidement les changements qui se produisent.
La condition préalable pour qu'un développeur puisse propager son code sur l'activité principale est qu'il puisse construire correctement son code. Ceci, bien sûr, comprend le passage des tests avec succès. Comme avec n'importe quel cycle de propagation, le développeur met à jour sa copie de travail dans un premier temps pour être synchrone avec l'activité principale, résout tous les conflits, puis construit sur sa machine locale. Si la construction passe, alors il peut propager ses modifications sur l'activité principale.
En faisant cela souvent, les développeurs mettent rapidement en évidence un conflit entre deux changements. La clé pour corriger les conflits rapidement est de les trouver rapidement. Avec les développeurs qui propagent toutes les quelques heures, un conflit peut être détecté en quelques heures. À ce moment il n'y a pas eu énormément de changements et il est facile à résoudre. Les conflits qui restent inaperçus pendant des semaines peuvent être très difficiles à résoudre.
Le fait que vous construisiez lorsque vous mettez à jour votre copie de travail signifie que vous détectez les conflits de compilation ainsi que les conflits textuels. Puisque la construction est autotestante, vous pouvez également détecter les conflits dans l'exécution du code. Ces conflits sont des anomalies particulièrement gênantes à trouver s'ils passent inaperçus dans le code pendant une longue période. Puisqu'il y a seulement quelques heures de changements entre chaque propagation, il n'y a pas tellement d'endroits où le problème pourrait se cacher. Par ailleurs, puisqu'on n'a pas beaucoup changé, vous pouvez utiliser la commande diff-debugging pour vous aider à trouver l'anomalie.
Mon conseil est que chaque développeur devrait propager sur le référentiel tous les jours. En pratique, il est utile que les développeurs propagent plus souvent que cela. Le plus souvent vous propagez, le moins d'endroits vous aurez à chercher les erreurs lors de conflits, et le plus rapidement, vous résoudrez les conflits.
De fréquentes propagations encouragent les développeurs à découper leur travail en petits morceaux de quelques heures chacun. Cela permet de suivre l'avancement et procure un sentiment de progrès. Souvent, les gens ont d'abord le sentiment qu'ils ne peuvent pas faire quelque chose de significatif en seulement quelques heures, mais nous avons trouvé que l'encadrement et la pratique les aide à apprendre.

Chaque propagation doit déclencher une construction de l'activité principale sur une machine d'intégration

En effectuant des propagations tous les jours, une équipe obtient de fréquentes constructions testées. Cela devrait signifier que l'activité principale reste dans un état sain. En pratique, cependant, il arrive que les choses tournent mal. Une des raisons est la discipline, les gens ne font pas de mise à jour ni de construction avant de propager. Une autre est les différences d'environnements entre les machines des développeurs.
En conséquence, vous devez vous assurer que des constructions sont effectuées régulièrement sur une machine d'intégration et seulement si la construction réussit la propagation est autorisée. Puisque le développeur qui propage est responsable de cela, il a besoin de surveiller la construction de l'activité principale afin que l'équipe puisse la corriger si elle échoue. En gros, vous ne devriez pas rentrer chez vous avant que la construction de l'activité principale passe avec toutes les propagations que vous avez ajoutées en fin de journée.
Il y a deux manières principales pour garantir cela : à l'aide d'une construction manuelle ou d'un serveur d'intégration continue.
L'approche de construction manuelle est la plus simple à décrire. Essentiellement, c'est quelque chose semblable à la construction locale qu'un développeur effectue avant la propagation dans le dépôt. Le développeur se connecte à la machine d'intégration, vérifie l'activité principale (qui possède maintenant sa dernière propagation) et lance la construction de l'intégration. Il garde un œil sur la progression, et si la génération réussit, il en a terminé avec sa propagation. (Voir aussi la description de Jim Shore.)
Un serveur d'intégration continue agit comme un écran de surveillance de référentiel. Chaque fois qu'une propagation effectuée sur le référentiel se termine, le serveur récupère automatiquement les sources sur la machine d'intégration, initie une construction, et avise le propagateur du résultat de construction. Le propagateur n'a pas terminé jusqu'à ce qu'il obtienne la notification - en général un email.
À ThoughtWorks, nous sommes de grands fans des serveurs d'intégration continue - En effet, nous avons mené le développement initial de CruiseControl et CruiseControl.NET, des serveurs open source largement utilisés. Depuis, nous avons également réalisé une version commerciale : Cruise. Nous utilisons un serveur d'intégration continue sur presque tous les projets que nous faisons et avons été très heureux des résultats.
Tout le monde n'aime pas utiliser un serveur d'intégration continue. Jim Shore a donné une description bien argumentée de la raison pour laquelle il préfère l'approche manuelle. Je conviens avec lui que l'intégration continue est bien plus qu'une simple installation de quelques logiciels. Toutes les pratiques décrites ici ont besoin d'être mises en pratique pour faire de l'intégration continue de manière efficace. Mais aussi beaucoup d'équipes qui pratiquent l'intégration continue trouvent qu'un serveur d'intégration continue est un outil utile.
De nombreuses sociétés construisent régulièrement selon un planning prédéfini. Par exemple, tous les soirs. Ce n'est pas la même chose que l'intégration continue et n'est pas suffisant pour l'intégration continue. Tout l'intérêt de l'intégration continue est de détecter les problèmes dès que vous le pouvez. Les constructions nocturnes signifient que les anomalies restent indétectées pendant une journée entière avant que quelqu'un ne les découvre. Plus elles restent indétectées, plus cela prend de temps à les trouver et les supprimer.
L'élément clé de la construction continue est que si elle échoue, elle doit être corrigée immédiatement. Tout l'intérêt de travailler avec l'intégration continue, c'est que vous savez que vous développez toujours sur une base stable. Ce n'est pas une mauvaise chose que la construction échoue, mais si cela arrive tout le temps, cela signifie que les développeurs ne sont pas assez prudents en construisant sur leur poste local avant une propagation. Lorsque la construction de l'activité principale échoue, il est important qu'elle soit corrigée rapidement. Pour aider à éviter de casser l'activité principale, vous pouvez envisager d'utiliser la méthode « pending head ».
Lorsque les équipes mettent en place l'intégration continue, c'est souvent l'une des choses les plus difficiles à régler. Une équipe peut rapidement rencontrer des difficultés pour arriver à avoir une construction de l'activité principale qui fonctionne, surtout si elles travaillent sur du code existant. Patience et application sont les maîtres mots, alors ne vous découragez pas.

Maintenir une construction rapide

Tout l'intérêt de l'intégration continue est de fournir un retour rapide. Rien n'est plus désagréable qu'une construction qui prend beaucoup de temps. Ça me fait sourire de voir ce qui est considéré aujourd'hui comme une longue construction. La plupart de mes collègues considèrent qu'une construction qui prend une heure est complètement inacceptable. Je me souviens d'équipes rêvant qu'elles puissent obtenir une construction aussi rapide - et parfois nous rencontrons encore des cas où il est très difficile d'obtenir des constructions aussi rapides.
Cependant, pour la plupart des projets, la recommandation XP d'une construction de dix minutes est parfaitement raisonnable. La plupart de nos projets aujourd'hui y arrivent. L'effort en vaut la peine, parce que chaque minute en moins pour le temps de construction est une minute gagnée pour chaque développeur, à chaque propagation. Puisque l'intégration continue nécessite de fréquentes propagations, c'est autant de temps gagné.
Si vous commencez avec une construction d'une heure, la raccourcir peut sembler difficile. Cela peut l'être d'autant plus en travaillant sur un nouveau projet et réfléchir à la façon de garder les choses rapides. Pour les applications d'entreprise, au moins, nous avons trouvé que le goulot d'étranglement habituel sont les tests - en particulier les tests qui impliquent des services externes comme une base de données.
Probablement que l'étape la plus cruciale est de commencer à travailler sur la création d'une construction par étapes. L'idée derrière une construction par étapes (aussi connue comme construction « pipeline » ou déploiement « pipeline ») est qu'il y a en effet plusieurs constructions réalisées à la suite. La propagation sur l'activité principale déclenche la première construction - ce que j'appelle la construction de propagation. La construction de propagation est celle qui est réalisée quand quelqu'un propage sur l'activité principale. Elle doit être rapide, mais les choix qui sont faits réduisent la capacité à détecter des anomalies. L'objectif consiste à trouver l'équilibre entre la nécessité de trouver des anomalies et la vitesse de telle sorte qu'une bonne construction de propagation soit suffisamment stable pour que d'autres personnes puisent travailler avec.
Jez Humble et Dave Farley ont étendu ces idées au domaine du déploiement continu avec de plus amples détails sur le concept de construction par étapes - ce qu'ils appellent les déploiements « pipeline ». Leur livre, Continuous Delivery, a justement reçu la récompense Jolt en 2011.

Une fois que la construction de propagation est bonne, les membres de l'équipe peuvent travailler sur le code en toute confiance. Cependant, il existe d'autres tests, plus lents, que vous pouvez commencer à faire. Des machines supplémentaires peuvent exécuter des routines de tests sur les constructions qui prennent plus de temps à s'exécuter.
Un exemple simple est une construction en deux étapes. La première étape serait de faire la compilation et d'exécuter les tests unitaires avec une base de données bouchonnée. Ces tests peuvent s'exécuter très rapidement, en restant dans la limite de dix minutes. Cependant, toutes les anomalies qui impliquent des interactions à grande échelle, en particulier celles impliquant la base de données réelle, ne seront pas trouvées. La deuxième étape crée une suite de tests différents qui utilisent cette fois réellement la base de données et impliquant l'ensemble du processus. Cette suite peut prendre plusieurs heures.
Dans ce scénario, les gens considèrent la construction de propagation comme la première étape et l'utilisent comme leur cycle principal d'intégration continue. La deuxième étape est une construction secondaire qui s'exécute quand elle peut, reprenant l'exécutable de la dernière construction réussie pour des tests supplémentaires. Si la construction secondaire échoue, il n'est pas aussi important de « tout arrêter tout de suite », mais l'équipe doit corriger ces anomalies aussi rapidement que possible, tout en gardant la première étape correcte. En effet, la deuxième étape n'a pas à rester correcte, du moment que chaque anomalie connue est identifiée et traitée en quelques jours. Comme dans cet exemple, les constructions secondaires, sont souvent des tests puisque c'est généralement les tests qui causent la lenteur.
Si la construction secondaire détecte une anomalie, c'est un signe que la construction primaire (de propagation) pourrait aussi en détecter un sur un autre test. Autant que possible, assurez-vous que tout échec dans la construction secondaire mène à construire de nouveaux tests dans la construction primaire pour mettre en évidence l'anomalie, dès la construction primaire. De cette façon, les tests de la construction primaire sont renforcés constamment. Il y a des cas où il n'y a aucun moyen d'obtenir un test qui soit rapide, donc vous pouvez décider de ne l'inclure que dans la construction secondaire. La plupart du temps, heureusement, vous pouvez ajouter ces tests à la construction primaire.
Cet exemple est une construction en deux temps, mais le principe de base peut être étendu à un nombre illimité de constructions supplémentaires. Les constructions après la première peuvent également être effectuées en parallèle, donc si vous avez deux heures de tests secondaires, vous pouvez améliorer la réactivité en ayant deux machines qui gèrent la moitié des tests chacune. En utilisant des constructions secondaires comme cela, vous pouvez introduire toutes sortes de tests automatisés supplémentaires, y compris des tests de performance, dans le processus de construction. (J'ai rencontré un grand nombre de méthodes intéressantes autour de ça quand je voyais des projets à ThoughtWorks au cours des deux dernières années - J'espère persuader certains développeurs de partager leurs méthodes)

Tester dans un environnement qui est une copie de la production

L'intérêt des tests est de débusquer, dans des conditions maitrisées, tout problème que le système aura en production. Une partie importante de ceci est l'environnement dans lequel le système de production sera exécuté. Si vous testez dans un environnement différent, chaque différence entraine un risque que ce qui arrive en cours de test n'arrivera pas en production.
Vous devez donc configurer votre environnement de test pour qu'il soit aussi proche de votre environnement de production que possible. Utilisez le même logiciel de base de données, avec les mêmes versions, utilisez la même version du système d'exploitation. Mettez toutes les bibliothèques qui sont appropriées dans l'environnement de production, dans l'environnement de test, même si le système ne les utilise pas vraiment. Utiliser les mêmes adresses IP et les ports, le même matériel.
Bon, en réalité il y a des limites. Si vous écrivez un client lourd, il n'est pas possible de le tester dans un clone de chaque environnement possible avec tous les logiciels tiers que les personnes utilisent. De même, certains environnements de production peuvent être trop coûteux à reproduire (même si j'ai souvent rencontré des fausses économies en ne reproduisant que les environnements modérément coûteux). Malgré ces limites votre objectif doit toujours être de reproduire l'environnement de production autant que vous le pouvez, et de comprendre les risques que vous acceptez pour chaque différence entre le test et la production.
Si vous avez une configuration assez simple, sans trop de communication complexe, vous devez être capable d'exécuter la construction de propagation dans un environnement entièrement identique à la production. Souvent, cependant, vous devez utiliser les « test doubles » parce que les systèmes répondent lentement ou de façon intermittente. Par conséquent, il est fréquent d'avoir un environnement très artificiel pour les tests primaires pour la vitesse, et utiliser un clone de production pour les tests secondaires.
J'ai remarqué un intérêt croissant dans l'utilisation de la virtualisation pour rendre des environnements de test facile à mettre en place. Les machines virtualisées peuvent être enregistrées avec tous les éléments nécessaires. Il est alors relativement simple d'installer la dernière version et exécuter les tests. De plus, cela peut vous permettre d'exécuter plusieurs tests sur une machine, ou de simuler plusieurs machines dans un réseau sur une seule machine. Comme les problèmes de performance causés par la virtualisation diminuent de plus en plus, cette option est de plus en plus intéressante.

Rendre facilement disponibles les exécutables les plus récents

Une des parties les plus difficiles du développement logiciel est de s'assurer que vous construisez le bon logiciel. Nous avons constaté qu'il est très difficile de préciser ce que vous voulez au départ et correctement ; les utilisateurs trouvent qu'il est beaucoup plus facile de voir quelque chose qui ne correspond pas exactement à leur besoin et dire comment cela doit être changé. Les processus de développement Agile profitent de cette partie du comportement humain.
Pour aider à ce processus, toute personne participant à un projet de logiciel devrait être en mesure d'obtenir les derniers exécutables et être capable de les exécuter : pour des démonstrations, des essais exploratoires, ou juste pour voir ce qui a changé cette semaine.
C'est assez simple : assurez-vous qu'il y ait un endroit connu de tout le monde où chacun peut trouver les derniers exécutables. Il peut être utile de mettre plusieurs exécutables à cet endroit. Pour les plus récents, vous devriez mettre ceux qui passent les tests de propagation - de tels exécutables devraient être suffisamment stables dans la mesure où la dernière propagation est raisonnablement solide.
Si vous suivez un processus avec des itérations bien définies, il est généralement sage d'y mettre aussi les produits de construction de la fin de l'itération. Les démonstrations, en particulier, ont besoin de logiciels dont les fonctionnalités sont familières, c'est pourquoi il vaut généralement la peine de sacrifier les plus récents pour quelque chose que le démonstrateur sait comment faire fonctionner.

Tout le monde peut voir ce qu'il se passe

L'intégration continue, c'est la communication. Alors assurez-vous que chacun puisse facilement voir l'état du système et les changements qui y ont été faits.
Une des choses les plus importantes à communiquer, c'est l'état de la construction de l'activité principale. Si vous utilisez Cruise il y a un site web livré nativement qui vous montrera s'il y a une construction en cours et quel était l'état de la dernière construction de l'activité principale. Beaucoup d'équipes aiment rendre ça encore plus évident en y connectant un affichage continu - avoir une lumière verte lorsque la construction est valide, ou rouge si elle échoue est assez répandu. Les lampes à lave rouge et verte sont particulièrement répandues - et pas seulement pour indiquer l'état de la construction, mais aussi pour indiquer depuis combien de temps elle est dans cet état. Des bulles sur une lampe rouge indiquent que la construction a été rompue depuis trop longtemps. Chaque équipe fait ses propres choix sur ces capteurs - soyez ludiques dans vos choix (récemment, j'ai vu quelqu'un essayer un lapin dansant).
Si vous utilisez un processus manuel d'intégration continue, cette visibilité est indispensable. Le moniteur de la machine physique de construction peut montrer l'état de l'activité principale de la construction. Vous pouvez poser un jeton de construction sur le bureau de celui qui est en train de faire la construction (à nouveau quelque chose de rigolo comme un poulet en caoutchouc est un bon choix). Souvent les gens aiment faire du bruit lorsque la construction est valide, comme faire sonner une cloche.
Les pages web des serveurs d'intégration continue peuvent montrer plus d'information, bien sûr. Cruise fournit une indication non seulement de qui exécute la construction en cours, mais aussi quels changements ont été faits. Cruise fournit également un historique des modifications, permettant aux membres de l'équipe d'obtenir un bon aperçu de l'activité récente sur le projet. Je connais des team leaders qui aiment l'utiliser pour avoir une idée de ce que les gens font et garder une bonne vue des changements apportés.
Un autre avantage d'utiliser un site web est que ceux qui sont éloignés géographiquement peuvent se faire une idée de l'état du projet. En général, je préfère avoir tout le monde travaillant activement sur un projet ensemble, mais souvent il y a des gens périphériques qui aiment garder un œil sur les choses. Il est également utile pour les groupes de regrouper des informations de constructions provenant de projets multiples - fournissant un statut simple et automatisé de différents projets.
L'affichage d'informations de qualité n'est pas seulement celui que l'on voit sur un écran d'ordinateur. Un de mes affichages préférés a été pour un projet pour lequel l'intégration continue se mettait en place. Il avait un long historique d'être incapable d'avoir une construction stable. Nous avons mis sur le mur un calendrier qui montrait une année complète avec un petit carré pour chaque jour. Chaque jour, le groupe d'assurance qualité mettait un autocollant vert sur la journée si elle avait reçu une version stable, ayant passé les tests de propagation, sinon un carré rouge. Au fil du temps, le calendrier révélait l'état du processus de construction montrant une amélioration constante jusqu'à ce que les carrés verts soient si répandus que le calendrier disparut - son but était rempli.

Automatiser le déploiement

Pour l'intégration continue, vous avez besoin d'environnements multiples. Un pour exécuter les tests de propagation, un ou plusieurs autres pour exécuter des tests secondaires. Puisque vous êtes amenés à déplacer les exécutables entre ces environnements plusieurs fois par jour, vous aurez envie de faire ça automatiquement. Il est donc important d'avoir des scripts qui vous permettront de déployer facilement l'application dans n'importe quel environnement.
Une conséquence directe de ceci est que vous devriez également avoir des scripts qui vous permettent de déployer en production avec la même facilité. Vous ne déploierez pas en production tous les jours (même si j'ai connu des projets qui le font), mais le déploiement automatique permet à la fois d'accélérer le processus et de réduire les erreurs. C'est aussi une option bon marché, car elle utilise les mêmes fonctionnalités que vous utilisez pour déployer dans des environnements de test.
Beaucoup de gens sont préoccupés par la façon de faire avec des bases de données mises à jour fréquemment. Pramod Sadalage et moi avons écrit cet article expliquant comment gérer cela avec la réfactorisation automatisée et la migration des bases de données.
Si vous déployez en production une fonctionnalité automatisée supplémentaire, envisagez un retour arrière automatisé. Des erreurs peuvent se produire de temps en temps, et si une substance brunâtre et nauséabonde frappe le métal en rotation, il est bon d'être en mesure de revenir rapidement à la dernière bonne version connue. Être capable de revenir automatiquement à l'état précédent réduit aussi beaucoup la tension de déploiement, encourage les gens à déployer plus fréquemment et ainsi obtenir de nouvelles fonctionnalités pour les utilisateurs plus rapidement. (La communauté Ruby on Rails a développé un outil appelé Capistrano qui est un bon exemple d'outil faisant ce genre de chose.)
Dans des environnements clusterisés, j'ai vu fonctionner des déploiements tournants où le nouveau logiciel est déployé nœud par nœud, remplaçant progressivement l'application en l'espace de quelques heures.
Une variante particulièrement intéressante de ce que j'ai rencontré avec des applications web publiques est l'idée de déployer une construction d'essai à un sous-ensemble d'utilisateurs. L'équipe voit alors comment la construction d'essai est utilisée avant de décider de la déployer à l'ensemble de la population. Cela vous permet de tester de nouvelles fonctionnalités et des interfaces utilisateur avant de s'engager à faire un choix définitif. Le déploiement automatisé, associé à une bonne discipline d'intégration continue, est essentiel pour faire fonctionner le tout.

Avantages de l'intégration continue

Dans l'ensemble, je pense que le plus grand bénéfice de l'intégration continue est la réduction des risques. Je me souviens encore de ce projet de logiciel que j'ai mentionné dans mon premier paragraphe. Ils étaient à la fin (ils l'espéraient) d'un long projet, mais sans aucune idée de combien de temps il restait avant qu'ils aient terminé.
Le problème avec l'intégration manuelle, c'est qu'il est très difficile de prédire combien de temps il faudra pour la réaliser, et pire encore, il est très difficile de voir à quelle étape du processus vous vous trouvez. Le résultat est que vous vous mettez dans une situation complètement opaque à l'un des moments les plus cruciaux d'un projet - même si vous êtes dans un des rares cas où vous n'êtes pas déjà en retard.
L'intégration continue résout complètement ce problème. Il n'y a pas d'intégration longue, vous éliminez complètement cette situation opaque. À tout moment vous savez où vous en êtes, ce qui fonctionne, ce qui ne fonctionne pas, les anomalies restantes dans votre système.
Les anomalies - ce sont ces choses désagréables qui détruisent la confiance et désordonnent les calendriers et les réputations. Les anomalies dans les logiciels déployés rendent les utilisateurs en colère contre vous. Les anomalies dans les travaux en cours se mettent en travers du chemin, ce qui rend plus difficile de faire fonctionner le reste du logiciel correctement.
L'intégration continue ne se débarrasse pas des anomalies, mais elle les rend beaucoup plus faciles à trouver et à supprimer. À cet égard, c'est comme le code autotestant. Si vous introduisez une anomalie et qu'elle est rapidement détectée, il est beaucoup plus facile de s'en débarrasser. Puisque vous avez seulement changé une petite partie du système, vous n'avez pas à chercher loin. Puisque cette petite partie est celle sur laquelle vous venez de travailler, c'est frais dans votre mémoire - encore une fois cela la rend plus facile à trouver. Vous pouvez également utiliser le diff debugging - comparer la version actuelle avec une version antérieure qui n'avait pas l'anomalie.
Les anomalies sont également cumulatives. Plus vous avez d'anomalies, plus il est difficile d'en retirer une. C'est en partie à cause des interactions qu'il peut y avoir, où les anomalies sont la résultante de multiples défauts - ce qui rend chaque erreur difficile à trouver. C'est aussi psychologique - les gens ont moins d'énergie pour trouver et se débarrasser des anomalies quand il y en a beaucoup - un phénomène que les « Pragmatic Programmers » appellent le syndrome « Broken Windows ».
Une conséquence pour les projets utilisant l'intégration continue est qu'ils ont tendance à avoir nettement moins d'anomalies, autant en production que dans le développement. Cependant, je dois souligner que cette conséquence est directement liée à la qualité de vos tests. Vous devriez vous rendre compte qu'il n'est pas trop difficile de mettre au point une suite de test faisant une différence notable. Habituellement, cependant, cela prend un certain temps avant qu'une équipe parvienne à un faible niveau d'anomalies. Et pour y parvenir, cela demande beaucoup de travail et l'amélioration continue de vos tests.
Avec l'intégration continue, vous supprimez l'un des plus grands obstacles à de fréquents déploiements. Le déploiement fréquent est précieux car il permet à vos utilisateurs d'obtenir de nouvelles fonctionnalités plus rapidement et, à vous d'avoir un retour plus rapide sur ces fonctionnalités. Et, généralement, les utilisateurs collaborent plus dans le cycle de développement. Cela aide à briser la frontière qu'il peut y avoir entre les clients et le développement - frontière qui, je crois, est le plus grand obstacle à un développement de logiciels réussis.

Présentation de l'intégration continue

Alors vous avez envie d'essayer l'intégration continue ? Mais par où commencer ? L'ensemble des pratiques décrites ci-dessus vous présente tous les avantages - mais vous n'avez pas besoin de commencer avec toutes en même temps.
Il n'y a pas de recette fixe - cela dépend beaucoup de la nature de votre structure et de l'équipe. Mais voici quelques indications que nous savons utiles.
Une des premières étapes est d'obtenir une construction automatisée. Mettez tout ce dont vous avez besoin dans le gestionnaire de code source afin que vous puissiez construire l'ensemble du système avec une seule commande. Pour de nombreux projets, ce n'est pas si simple - mais c'est essentiel pour tout faire fonctionner. Au départ, vous n'exécuterez des constructions que ponctuellement, au besoin. Ou vous ferez tout simplement une construction nocturne. Bien que ce ne soit pas complètement de l'intégration continue, une construction automatisée nocturne est un premier pas.
Introduisez des tests automatisés dans vos constructions. Essayez d'identifier les principaux domaines où les choses vont mal et rédigez des tests automatisés pour mettre en évidence ces échecs. Sur un projet existant, il est difficile d'obtenir une suite de tests vraiment bonne et rapide - il faut du temps pour construire les tests. Mais vous devez bien commencer quelque part - tous les clichés à propos de la construction de Rome s'appliquent ici.
Essayez d'accélérer la construction de propagation. L'intégration continue pour une construction de quelques heures, c'est mieux que rien, mais parvenir au nombre magique de dix minutes est beaucoup mieux. Cela nécessite généralement d'importantes modifications de votre code pour supprimer les dépendances sur les parties lentes du système.
Si vous commencez un nouveau projet, faites de l'intégration continue dès le départ. Gardez un œil sur les temps de construction et prenez des mesures dès que vous commencez à dépasser les dix minutes. En agissant rapidement, vous allez faire les restructurations nécessaires avant que le code ne soit tellement long qu'il devienne le principal obstacle.
Et surtout, entourez-vous bien. Trouvez quelqu'un qui a déjà fait de l'intégration continue avant. Comme toute nouvelle technique, il est difficile de l'introduire quand vous ne savez pas à quoi ressemble le résultat final. Cela peut coûter de l'argent d'avoir un coach, mais sinon vous devrez le payer en temps et en productivité si vous ne le faites pas. (Publicité - oui, nous à ThoughtWorks nous proposons ce genre de prestations. Après tout, nous avons fait la plupart des erreurs qu'il y a à faire.)

En conclusion

Depuis que Matt et moi avons écrit l'article original, l'intégration continue est devenue une technique communément utilisée pour le développement de logiciels. Pratiquement tous les projets à ThoughtWorks l'utilisent - et nous en voyons d'autres qui utilisent l'intégration continue dans le monde entier. Je n'ai presque jamais entendu des choses négatives au sujet de cette approche - contrairement à certaines des pratiques les plus controversées de l'Extreme Programming.
Si vous n'utilisez pas l'intégration continue, je vous encourage vivement à l'essayer. Si vous l'utilisez, il y a peut-être dans cet article quelques idées qui pourront la rendre plus efficace. Nous avons beaucoup appris sur l'intégration continue au cours des dernières années, j'espère qu'il y a encore beaucoup à apprendre et à améliorer.

Lectures complémentaires

Un article comme celui-ci ne peut couvrir complètement le sujet. Mais celui-ci est important alors j'ai créé une page guide sur mon site pour donner quelques informations supplémentaires.Pour explorer l'intégration continue de manière plus détaillée, jetez un coup d'œil au livre justement intitulé de Paul Duvall sur le sujet (qui a remporté le prix Jolt - plus que je n'ai jamais réussi). Pour plus d'information sur les méthodes de déploiement continu, jettez un coup d'oeil au livre de Jez Humble et Dave Farley qui a lui aussi reçu une récompense Jolt.

Remerciements

D'abord et avant tout à Kent Beck et mes nombreux collègues sur le projet Chrysler Comprehensive Compensation (C3). Ce fut ma première occasion de voir l'intégration continue en action avec une quantité significative de tests unitaires. Il m'a montré ce qu'il était possible de faire et m'a donné une inspiration qui m'a conduit pendant de nombreuses années.
Merci à Matt Foemmel, Dave Rice, et tous les autres qui ont construit et maintenu l'intégration continue sur Atlas. Ce projet était un exemple d'intégration continue à grande échelle et a montré les bénéfices qu'on a pu tirer d'un projet existant.
Paul Julius, Jason Yip, Owen Rodgers, Mike Roberts et de nombreux autres contributeurs open source ont participé à la construction des variantes de CruiseControl. Bien qu'un serveur d'intégration continue ne soit pas indispensable, la plupart des équipes le trouvent utile. CruiseControl et d'autres serveurs d'intégration continue ont joué un grand rôle dans la popularisation et ont permis aux développeurs de logiciels d'utiliser l'intégration continue.
Une des raisons pour lesquelles je travaille à ThoughtWorks est d'avoir accès aux projets réalisés par des gens talentueux. Presque tous les projets que j'ai rencontrés m'ont appris des choses sur l'intégration continue.

Révisions importantes

1 mai 2006: Réécriture complète de l'article pour le mettre à jour et clarifier la description de l'approche.
10 Septembre 2000: Publication de la version originale. L'article original sur l'intégration continue décrit nos expériences quand Matt nous a aidé à mettre en place l'intégration continue sur un projet ThoughtWorks en 2000.

Remerciements du traducteur

Je tiens à remercier Martin Fowler pour son aimable autorisation de traduire son article et Claude Leloup pour sa relecture.