[Devoxx FR 2013] plus on est Foo, plus on release !

the-singularity-500x235Du “click droit > generate EAR…”  au continuous delivery, les techniques de fabrication et de livraison d’un logiciel ont bien changé en 10 ans…

Pourquoi de tels changements ? Quelles sont les dernières techniques qui vous permettront de livrer en production sans régression ni stress, et en 1 seul click ? Cet article, largement inspiré des talks et retours d’expérience présentés lors de l’édition 2013 de Devoxx France vous présentera les outils, techniques et pratiques qui vous feront aimer les mises en production…

Chronique d’un projet (…10 ans plus tôt)

Une histoire de Foo

it-crowd-mossVoici l’histoire de Bob. Nous sommes au tout début du XXIème siècle, Bob est un jeune développeur java. Il a un avenir prometteur, nous sommes en plein boom internet, et il vient de se faire embaucher sur le projet FOO, à la pointe de la technologie (java servlet et JSP sur serveur websphere)

Le projet FOO se terminera à la fin de l’année prochaine : 2 mois de spécifications, 9 mois de développement, puis 2 mois de recette et enfin mise en production… Enfin, tout cela reste très théorique pour Bob pour qui le job s’arrête à la mise en recette de l’application. Pour la suite, terra incognita… Mais de toute façon, ce ne sera plus de son ressort…

Les développements se déroulent, tant bien que mal, c’est même un peu rock’n’roll il faut le dire (mais ça, c’est une autre histoire !). Finalement, l’équipe de Bob finit par décider :

«  ok, on va dire qu’on a fait ce qu’on a pu. J’ai juste eu le temps de corriger un dernier truc; maintenant on croise les doigts et on envoie ça en UAT. Bob, tu fais l’EAR ?  ». Bob fait un “click droit  > generate EAR…” dans son WSAD , puis envoie son archive en pièce jointe dans un mail pour l’équipe de test.

… Et une histoire sans fin

Réponse le lendemain matin : «  Je ne peux pas déployer cet EAR, l’archive semble avoir été corrompue par notre antivirus »

Après un nouvel envoi par FTP : « A présent, l’EAR se déploie, mais l’application ne démarre pas : Can not connect to database ‘jdbc:mysql://localhost/foodb’ »

Bob : « Flûte, on a oublié de changer l’URL de la base pour celle d’UAT ! »

Après correction et nouvel envoi, nouvelle réponse : « Maintenant, on a le message : Column not found  ‘foo_creation_date’ »

Bob : « Argh, on a pas mis à jour le schema de la base ! »

Une fois le schéma de base corrigé, nouveau retour de l’équipe de test : « Cette fois le serveur démarre correctement, mais au 1er appel, j’ai l’erreur : java.lang.NoSuchMethodError : org.apache.log4j.Category.log(Ljava/lang/String) »

Après 1 semaine d’investigations, de mails, de dissection de war, de classpath et de libs, l’application a enfin démarré. 1er bug remonté, 1er bugfix pour Bob, 2nde livraison, et encore 3j pour que l’environnement de test soit de nouveau up & running. Nouveaux bugs, nouvelles corrections, nouvelle livraison, et 1ères régressions.

Bob : « C ‘était quelle version déjà dans laquelle ça fonctionnait  ?? »

Nouvelle correction, nouvelle livraison. La correction ne marche pas. « Bob, tu avais bien fait un update du code avant de faire l’EAR ? On dirait que ma correction n’a pas été livrée… ». Bob : « je ne sais plus ! Attend, je vais décompiler le .class pour vérifier… »

Bob a finalement très mal vécu ces UAT. Beaucoup de stress, beaucoup d’énergie dépensée à repackager l’application, des journées de travail sans aucune valeur ajoutée, un feedback très mitigé de la part de son client, perte de motivation, et pour seules perspectives une mise en production qui s’annonce tout aussi mouvementée, sans parler des trains de maintenance à venir…

moss-scared

Les raisons de l’échec

Alors qu’est ce qui ne fonctionne pas ?

  • remontée tardive des problèmes : les bugs et régressions sont découverts en UAT. Les problèmes de packaging également ! Les corrections coûtent cher…
  • pas de traçabilité : les livrables ne sont pas clairement identifiés. Il n’y a pas d’historique
  • pas de maîtrise du contenu du livrable : difficile de savoir quel code est ou n’est pas dans le livrable construit sur le poste de Bob. De même, les librairies (et leur version) proviennent de chez Bob sans vraiment connaître leur version ni même les « hacks » qu’ils ont pu subir, juste  « pour tester en dev » …
  • pas de tests automatisés : beaucoup d’énergie dépensée pour refaire toujours le même travail à chaque livraison
  • un  livrable qui dépend de l’environnement de déploiement, et donc les mêmes difficultés  de déploiement quand on change d’environnement cible
  • cloisonnement entre développeurs / testeurs / et exploitant ce qui implique des problèmes de communication et ralentit toute action
  • pas de contrôle de la qualité : rien pour contenir les dérives !
  • pas de stratégie d’amélioration continue donc une situation qui ne peut qu’empirer release apres release

Retour en 2013

Nous sommes de retour en 2013. Depuis ses débuts difficiles, Bob s’est repris en main et a décidé  d’investir sur sa carrière ; il travaille à présent chez So@t, et partage ses retours d’expérience et bonnes pratiques lors des « soirées 3T », il assiste régulièrement aux conférences organisées par le JUG, ainsi qu’à Devoxx France. Depuis lors, il a compris bien des choses : les équipes projets qui délivrent sont « agiles » et les processus de fabrication des logiciels sont « industriels »

Pratiques agiles et intégration continue

icLes « pratiques agiles » ont été introduites en réponse aux problèmes engendrés par les méthodologies de développement du siècle dernier jusqu’alors utilisées (type cycle en V). Elles apportent leur lot de solutions, mais nécessitent un outillage technique, permettant leur mise en place…C’est ce qu’a passé en revue pour nous Guillaume Rams lors de son talk intitulé De compiletout.bat a l’Usine Logicielle pour Java … Même si ces outils et pratiques n’ont plus rien de « révolutionnaires » en 2013, ils ne sont malheureusement pas encore devenus des standards courants dans toutes les entreprises… Cela méritait donc un état de l’art en la matière.

Outils de build

Il ne s’agit pas ici à proprement parlé d’intégration continue ou de pratiques agiles, mais un outil de build moderne a malgré tout son importance, et en particulier pour sa gestion des dépendances par repository : On trouvera ici le désormais classique Maven, ainsi que ses challengers Ivy et Gradle

SCM

Pour les gestionnaires de sources, même si Subversion n’a pas encore poussé son dernier soupir, les DVCS ont désormais le vent en poupe (Git, Mercurial). Leurs atouts : la facilité à gérer les branches/merges, et  la possibilité de travailler en mode déconnecté, ainsi que leur côté social network (le succès de Github  auprès des développeurs n’a plus a être démontrée, notamment dans la communauté open source)

Versionnement des livraisons

Versionner les sources, c’est bien, versionner les livraisons avec, c’est mieux ! Il est très important d’identifier et de conserver l’historique des livraisons. On pourra se faciliter la vie en utilisant les outils adaptés, tels que le plugin release de Maven par exemple.

Référentiel des binaires

Pour les binaires (tels que les jar, war…), un repisotory vous aidera à gérer les versions de vos binaires, ainsi qu’à les diffuser. Les Repository maven les plus utilisés aujourd’hui sont Nexus, Artifactory, ou encore Archiva

Usine (ou forge) logicielle

Agilité implique cycles courts itératifs, et donc nécessité de builds automatisés « continus ». Plus vous irez loin dans la couverture qu’offre votre build, plus vous serez en sécurité  :

  • build (compilation, packaging)
  • inspection de code (checkstyle, PMD, findbugs… ou tout en même temps dans Sonar)
  • couverture de code (cobertura, clover…)
  • TU (JUnit, TestNG…)
  • Déploiement automatique (Cargo…)
  • “Smoke” test (ou “health check”) qui consiste à voir si on arrive au moins à afficher la page par défaut sans exploser
  • Tests fonctionnels (Selenium…)

Pour réaliser ses tâches, une usine logicielle (Jenkins/Hudson, Teamcity, Bamboo…) sera votre ange-gardien, pour vous tapoter sur l’épaule quand votre “code push” suscite un problème (bug, régression, manque de test, code trop complexe, mal formaté…). Plus un problème est détecté tôt, moins il coûte cher à corriger !

boule_de_neige_calvinA noter qu’une usine logicielle peut être installée en intra (la question se pose alors de  la maintenance…) ou bien être utilisées out-of-the-box dans le cloud (on pourra par exemple utiliser une solution DEV@cloud de cloudbees)

Wiki

Pas de cohérence dans le travail d’équipe sans partage de la connaissance… C’est pourquoi un wiki est très intéressant, pour maintenir une documentation participative et à jour. On pourra en particulier y faire vivre :

  • une « welcome page » pour les nouveaux développeurs entrant dans l’équipe (référençant les outils, les configurations, les pointeurs vers les serveurs, un annuaire, etc…)
  • une base de connaissance (problème fréquemment rencontrés,  procédures classiques…)

Il en existe des quantités, pour ne citer qu’eux : Xwiki, Confluence

logo-xwiki

La qualité dans le logiciel

whatisquality… Et quand on parle de Xwiki, Vincent Massol n’est jamais très loin ! Castcodeur, contributeur du projet open source Xwiki, et CTO de la société « éponyme » 😉 , Vincent est en effet bien placé pour nous faire un retour d’expérience très concret sur l’implémentation de la qualité dans le cadre de ce projet, à travers 4 axes qu’il a développés durant 45 minutes…

Stabilité des API

Xwiki est une application en soit, mais c’est aussi une API. Comment garantir la stabilité de cette API à travers les releases successives de l’application ? Comment gérer les dépréciations ? Comment informer les utilisateurs des changements ? Comment identifier les méthodes public exposées au client, des méthodes public “internes” de l’application ?

L’approche naïve consiste à utiliser le mot clef Deprecated dans l’API. Mais cela reste assez limité… L’utilisation de clirr (ou de sa version maven plugin clirr) permettra en revanche de comparer une API entre 2 versions, et de générer automatiquement la « release note », qui aidera vos utilisateurs à migrer de version.

Par ailleurs, une convention  (telle qu’un package .internal) pourra être suivie pour isoler les classes contenant des méthodes public pour lesquelles on ne garantira pas de compatibilité au fil des versions (méthodes hors API quoique visibles).

Enfin, en scindant les APIs dans un module current et un module legacy, on sera capable de retirer petit à petit les méthodes dépréciées de l’API supportée, tout en gardant une solution de secours pour les clients qui tardent à migrer…

Et pour finir, et régler le cas des « APIs jeunes  » (ou instables), on pourra également suivre une convention (telle qu’une annotation @unstable) qui figurera dans la javadoc et indiquera à l’utilisateur  que l’API est à utiliser  « à vos risques et péril »

Jar Hell

jarhellIl s’agit là d’un problème malheureusement « usuel » en java dû au mécanisme du classpath, qui se produit lorsque l’application utilise une même classe dans 2 versions différentes. Il se manifeste souvent par une NoSuchMethodError, ou une ClassCastException, voire parfois même pire !

Plusieurs cas de figure peuvent nous amener à un jar hell :

  • Une « collision » entre les dépendances du projet : les dépendances étant sous forme arborescente à la compilation (dépendances transitives) mais malheureusement remises à plat au runtime !
  • un jar peut « embarquer » un autre jar dont il dépend (… Mais c’est maaaal !!!)

Alors comment les détecter ? Le plugin maven dependency (mvn dependency:tree) permettra déjà d’afficher l’arbre des dépendances. C’est déjà ça, mais ça reste quand même très manuel tout ça !

Pour aller plus loin, les plugin maven duplicate finder et plugin maven enforcer  seront d’une aide précieuse pour respectivement empêcher le build en cas de conflit de classes, et forcer l’utilisation d’une version de librairie en particulier.

Couverture de test

Avoir des tests unitaires c’est bien… Mais si la couverture de code est minime, ça n’a pas beaucoup d’intérêt ! Le plugin JaCoCo empêchera le build si la couverture devient inférieure à un plancher donné.

Tests fonctionnels automatisés

Ces tests ont une forte valeur ajoutée pour la qualité du projet… Toute la difficulté réside dans la fiabilité de cet outil : ils sont très sensibles à tout un tas de paramètres liés à l’environnement d’exécution (crash JVM, indisponibilité réseau, nexus, git, lenteurs…) amenant à de nombreux faux positifs ! Le risque de ces faux positifs, et bien c’est l’histoire du garçon qui criait au loup…

Quelques conseils avant de prendre la route…

  • Les pratiques promouvant la qualité doivent être introduites petit à petit pour qu’elles prennent
  • Tout le monde doit se sentir concerné et participer à la démarche
  • Il ne faut pas avoir peur de supprimer des pratiques de qualité qui au final se révèlent avoir peu d’intérêt

Du développement à l’exploitation

frankenstein2

Ok, nous venons de voir que grâce à un processus moderne de développement, à l’intégration continue, aux pratiques agiles et à l’implémentation de la qualité, nous sommes capables de produire une nouvelle version de l’application :

  • qui « fonctionne »
  • qui respecte des standards de qualité
  • de façon fiable et avec une traçabilité
  • et en continu

Mais peut-on aller plus loin ? Si l’on est capable de produire en continu des livrables, saurait-on également les déployer en continu ? Et bien oui, c’est ce que font aujourd’hui Facebook, Flickr ou encore StackOverFlow, qui déploient quotidiennement de nouvelles versions de leur site en production : c’est ce que nous a présenté Axel Fontaine dans son talk Architecting for Continuous Delivery

Axel Fontaine - Architecting for continuous delivery - Devox fr 2013

Axel Fontaine – Architecting for continuous delivery – Devox fr 2013

Pour y parvenir, il faut d’abord avoir vaincu les prérequis suivants :

  • avoir un processus de dev itératif
  • une intégration continue avec :
    • le build
    • des tests automatiques (tests unitaires, tests de composant, tests de l’applicatoin)
  • un processus de release automatique (plugin release)
  • une stratégie de déploiement
    • capable de mettre en place  :
    • et sans interruption de service !

Stratégie de déploiement sans interruption de service

Elle n’est pas unique, mais une possibilité est de faire du déploiement « blue/green » :

toggle blue - green

La « mise en production » consiste à changer l’aiguillage des requêtes du serveur version « n » vers le serveur version « n+1 »

Bien entendu, cela soulève d’autres problématiques :

  • comment transférer l’état du serveur (sessions ouvertes en cours) du serveur « n » vers le serveur « n+1 » ? (attendre la fin des sessions ? sticky session ? session partagée dans un cache commun ?)
  • l’état du client reste-t-il compatible avec le serveur « n+1 »
  • comment gérer l’état de la base de donnée partagée entre les serveurs « n » et « n+1 » ?

Conclusion

Nous venons de voir que les techniques et pratiques de fabrication et de mise en production des logiciels ont bien changé depuis 10 ans… Nous sommes désormais pratiquement capables de livrer en production à volonté, tout en garantissant la disponibilité du service, sa qualité, et sa maintenabilité. Mais cela soulève un certain nombre de questions malgré tout, et certaines problématiques demeurent…

Le continuous delivery implique une nouvelle manière de penser les développements, qui nécessite de la part des développeurs d’avantage de conscience et de responsabilité vis à vis de la production ; un nouveau challenge apparaît : saurons-nous mettre un peu plus d’OPS dans nos DEV ?

Et pour finir, nous sommes désormais tous bien convaincus que l’industrialisation des processus et la démarche qualité dans le logiciel apportent beaucoup, en améliorant notre productivité, et en pérennisant l’application. Elles définissent un cadre qui néanmoins rigidifie les phases de développement. Ne sommes-nous pas en train de mettre en place des barrières qui risquent de freiner l’innovation ?

Nombre de vue : 102

AJOUTER UN COMMENTAIRE