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.