Université de la Performance -retour sur DevoxxFR 2014

Sebastien VAIRA, Senior Developper JEE / Grails revient sur une session Performance de DevoxxFR 2014

La performance, partie intégrante du travail de chacun d’entre nous, se doit d’être au cœur de nos développements ; nous ne devons pas négliger cet aspect et l’intégrer au plus vite dans nos cycles de développements. Car s’attaquer aux mesures (métriques) suite aux développements est rarement gage de réussite ; cela peut même induire une forte refonte du code, voire une remise en cause de l’architecture. Et c’est là que les plus grosses dérives se produisent…
Dans cet article, nous allons revenir sur les outils de référence et les pratiques qui ont été abordés lors du Devoxx 2014.

1. Préparer les jeux de données – Benerator


Il n’est pas toujours aisé de pouvoir disposer d’un jeu de données d’une volumétrie comparable à celle de PROD mais aussi d’une qualité semblable.
Certains d’entre nous, en particulier dans le domaine bancaire, ont également des contraintes d’anonymisation à respecter ; ce qui complexifie encore davantage la mise en place (l’acquisition) d’un jeu de données cohérent (vis-à-vis de la PROD).

Pour répondre à cette problématique, il existe l’outil : Benerator
http://databene.org/databene-benerator

Qui présente (entre autres) ces avantages :

  • Génère des données de TEST sans limite de taille
  • Rend anonymes les données sensibles
  • Insère des données en base si besoin / les ressort en fichiers sinon (CSV/XML/plat etc.)
  • Il embarque de base différents domaines (adresse, personne, web, finance etc.) pour faciliter la création de données
  • Il est multi-plateformes
  • Il supporte la majorité des BDD (Oracle, DB2, MySQL, PostgreSQL etc.)
  • Enfin, il est gratuit.

2. Exécuter une mesure unitaire – Chrome developer tools

La mesure unitaire réfère ici à un test de performance avec un utilisateur sur une base de données chargée.
Ceci peut facilement être réalisé avec Chrome DevTools qui nous fournit de nombreux composants pour :

  • tracer le temps réseau :
  • tracer la chronologie des événements (timeline) et ce qui prend du temps (chargement des ressources, parsing JS, calcul des styles etc.) :

  • tracer le temps d’exécution des scripts et l’utilisation mémoire :

3. Exécuter des tests de charge – Gatling

Cette fois par test de charge, on entend ici une cible de 100 utilisateurs par ex. et ce sur un volume BDD de PROD.
Simuler l’utilisation simultanée de votre serveur par X utilisateurs est généralement réalisé par l’écriture d’un script.
On connaissait jMeter, outil open-source d’Apache, qui réalise correctement ce travail mais qui est considéré, du moins au Devoxx, comme « ancestral ».
LoadRunner, logiciel HP payant ;
la tendance serait plutôt désormais à l’utilisation de l’outil Gatling :
http://gatling-tool.org/

Ce dernier offrant comme particularités :

  • Un vrai langage de développement pour les scripts : le Scala (il s’agit donc bien ici d’un développement à part entière dans le cadre d’un projet)
  • Un module ‘Recorder’ (sorte de proxy entre votre navigateur et votre application) vous aidant à écrire des scénarios ; pour ce faire, il suffit simplement de l’activer et de naviguer au sein de votre application Web…
  • La mise à disposition d’un plugin Maven gatling
  • La génération de rapports HTML avec graphiques très sympas (visuel + significatif) :

A noter, toujours dans la rubrique des tests de charge réalisables via Gatling, que bien souvent les tests suivants ne sont pas considérés/réalisés :
a) Test de rupture
Ayant pour objectif de déterminer les limites de votre application (en terme de nombre d’utilisateurs max ou de temps de réponse non acceptable)
b) Test de vieillissement (test d’endurance)
La capacité de votre application à fonctionner sur une période étendue (ex : 48h) avec une charge constante égale à la charge moyenne.
Il est évidemment conseillé de ne pas les négliger…

4. Industrialisation/automatisation des tests de charge – Jenkins, Capistrano, Chef

La question des performances est souvent traitée à la fin du projet ;
Ce qui implique que si elles ne sont pas acceptables, des dérives risquent d’être observées pour rectifier le tir ; l’architecture risque même d’être revue !
Ainsi donc, pour éviter ce type de faux pas, il faut intégrer les tests de performance au plus tôt dans le cycle de développement de nos projets et ce de préférence au sein d’une intégration continue.

Jenkins, outil open-source d’intégration continue, est un bon candidat pour ce faire.
http://jenkins-ci.org/

Au sein d’un job spécifique gatling, il permettrait par ex. de :

  • a) Créer un environnement
  • b) Réaliser un/des tirs de performance
  • c) Détruire ce contexte.

On pourrait ensuite visualiser les tableaux de bord Gatling résultants directement sous Jenkins.
Une des particularités intéressante en découlant serait de poser des limites sur les temps de réponse attendus pour votre application (ex : <=200 ms) ; De sorte que si un des développeurs impacte l’aspect performance, le build Jenkins soit en conséquence cassé et le développeur notifié par mail… A noter, dans l’optique de mise en place sous Jenkins d’un environnement adéquat pour réaliser les tirs de performance (ex : application déployée temporairement sur un espace virtuel), que ce dernier peut facilement être couplé aux outils suivants : ‘Chef’ et ‘Capistrano’. Chef permettant de déployer facilement serveurs et applications sur un emplacement physique/virtuel/cloud : http://www.getchef.com/chef/

Capistrano permettant d’exécuter des commandes SSH sur des serveurs distants :
http://capistranorb.com/

6. Problème de contention/identifier une fuite mémoire – VisualVM

Pour mesurer la consommation des ressources d’une application tournant sous la JVM, il est préconisé d’utiliser l’outil VisualVM, fourni dans les distributions Sun du JDK.
Egalement disponible en téléchargement ici :
http://visualvm.java.net/

Cet outil nous offre une petite interface graphique simple pour le suivi :

  • De l’utilisation CPU
  • De la consommation mémoire
  • Du volume de classes chargées par la VM
  • Des threads

Il permet aussi :

  • De réaliser le monitoring sur une application distante :
  • De réaliser un profilage CPU/mémoire :
  • Et enfin de diagnostiquer les fuites mémoires (par analyse des opérations qui consomment le plus)

Avec ce type d’outil, vous avez désormais de nombreuses possibilités pour analyser les problèmes de contention.

7. Identifier un problème d’index – jstack, explan plan

8. Mise en place du monitoring – collectd, Graphite et Metrics

  • Collectd : outil portable écrit en C et agissant tel un démon qui ‘monitore’ tout périodiquement et collecte des métriques relatives aux statistiques de performance du système
    http://collectd.org/

  • Graphite : outil écrit en Python proposant des interfaces graphiques avancées pour le suivi temps-réel des métriques de votre serveur
    http://graphite.wikidot.com/

  • Metrics : librairie Java intégrable dans votre projet via Maven et permettant de mesurer le comportement des composants critiques ; A noter qu’il se couple à Graphite pour la partie reporting.
    http://metrics.codahale.com/

  • metrics-spring : annotation Spring ‘@Timed ‘ sur les méthodes (sensibles) de votre serveur ;
    permettant d’obtenir ce type de sortie console/log:

9. Tuning système – Bonnie++

Les développeurs ne sont pas toujours à incriminer dans le cas de performances insatisfaisantes ;
N’oublions pas que les disques durs, CPU, cartes réseaux sont autant de composants qui peuvent influer sur les résultats d’une application ; et donc par conséquent l’aspect ‘Tuning d’infrastructure’ est alors à considérer.
A été abordé lors du Devoxx un petit outil de benchmark : « BONIE++ » pour réaliser une analyse système et vérifier si des optimisations sont possibles.
http://www.coker.com.au/bonnie++/

Conclusion

Cet article a plutôt vocation à être un recueil d’outils (en tout genre) pour réaliser des mesures unitaires/de charge, automatiser ces tests au sein d’une intégration continue, disposer de tableaux de bord modernes, être capable suite à l’obtention éventuelle de résultats négatifs de diagnostiquer un problème de contention/fuite mémoire/index et aller pourquoi pas jusqu’à du tuning système.
Chacun de ces outils a été abordé, ses facettes notables retranscrites ; je laisse néanmoins à chacun d’entre vous la possibilité de ‘creuser’ et de déterminer la réelle valeur ajoutée de l’outil au sein de vos projets.



BackboneJS et développement mobile : trucs et astuces (…fin)

Voici la suite (et fin) tant attendue de notre série sur le développement avec BackboneJS, framework JavaScript que nous utilisons pour nos créations de sites web / mobile HTML5, quand nous n’optons pas pour AngularJS.

Certaines bonnes pratiques sont communes à tout projet HTML5, notamment les techniques de responsive design, gestion des dépendances JavaScript et outillage des tests, ce qui constitue notre menu du jour :

Organisation des librairies

Require

Il s’agit de l’implémentation de l’API AMD la plus répandue. Elle permet de gérer les dépendances et chargement dynamique des scripts.
Pour démarrer, voici un lien très utile :
http://www.ringabell.org/un-simple-guide-pour-debuter-avec-requirejs/

Points d’attention

Nous avons fait face un certain nombre de fois des comportements étranges dans notre application qui étaient en fait du à des erreurs bêtes liées à l’utilisation de Require. Un bon réflexe quand vous ne comprenez absolument pas pourquoi votre code ne fait pas du tout ce qui est attendu est de vérifier les points suivants :

  • ne pas oublier le return avec le nom du module à la fin de la déclaration du dit module
  • l’ordre de déclaration des dépendances vers les modules qui doit correspondre exactement aux paramètres de la fonction (attention aux décalages par inadvertance!)

Optimisation

Bien que nous ne l’ayons pas utilisé dans notre projet, il peut être intéressant de mentionner que Require JS propose également un outil pour optimiser le chargement des scripts en concaténant et minifiant tous les fichiers JS nécessaires au démarrage de l’application en un seul fichier : http://requirejs.org/docs/optimization.html
Et pour un tutoriel en français sur le sujet : http://www.ringabell.org/requirejs-une-utilisation-plus-poussee/

Optimisation des performances

Dans le cas où nous travaillons avec une seule page HTML, on peut généralement la découper en différentes portions qui correspondront chacun à des vues Backbone à l’intérieur d’une vue globale (la page).
Il paraît alors préférable, dans la vue globale, de garder des références vers les sous-vues et d’éviter, quand c’est possible d’instancier de nouvelles vues à chaque changement dans un modèle sous-jacent. Il est possible d’associer un nouveau modèle (ou un modèle modifié) à une vue avant de la rafraîchir (via #render).

Var MyView = Backbone.View.extend({
 
 
    initialize: function() {
	    ...	
	    this.subView1 = new SubView1 ({model : modelA}) ;
 	   ...
    }
 
       refresh : function() {
	...	
      	this.subview1.model = myNewModel ;
	this.subview1.render() ;
       }
});

A lire également, pour éviter quelques erreurs qui peuvent être à l’origine de fuite de mémoire si on ne fait pas attention. Y sont notamment abordés le binding d’événements dans une vue et le rendering d’une collections de vues :
http://ozkatz.github.io/avoiding-common-backbonejs-pitfalls.html

Responsive Design

Les techniques de Responsive Design permettent d’assurer que l’affichage d’un site s’adapte correctement aux dimensions et résolution du support, que ce soit un ordinateur, une tablette ou un téléphone mobile.

Utilisation des media queries

Les Media Queries CSS3 sont une spécification du W3C qui permettant de définir l’application de feuilles de styles en fonction des périphériques (média). Mais elles vont bien au-delà de ce qu’il était possible de faire avec CSS2 où on pouvait définir une feuille de style différente pour l’impression par exemple puisqu’on peut créer des règles très précises qui prennent en compte, en plus du type de média, la largeur ou la hauteur de l’écran, l’orientation (portrait ou paysage), le support de la couleur etc. (voir : http://reference.sitepoint.com/css/mediaqueries#mediaqueries__tbl_media-queries_media-features)

Comme l’écriture d’une Media Query correspond à une expression logique, évaluée à vrai ou fausse, on peut tout à fait combiner ces différents paramètres, ce qui s’avère très pratique pour le design de sites mobiles.
Par exemple, pour un style spécifique à l’iPad :

/* iPads (portrait and landscape) ----------- */
@media only screen
and (min-width : 768px)
and (max-width : 1024px) {
/* Styles */
}

On trouvera un squelette de CSS avec Media Queries pour débuter avec le Responsive Design pour mobiles à l’adresse suivante : http://www.paulund.co.uk/boilerplate-css-media-queries
Les Media Queries sont supportées par la plupart des navigateurs récents (voir http://caniuse.com/css-mediaqueries pour un tableau récapitulatif des compatibilités)

Meta-balise « viewport »

Le « viewport » désigne normalement la surface d’affichage de la fenêtre du navigateur.

Sur un navigateur de bureau, un pixel écran correspond à un pixel CSS.

Mais comme expliqué dans l’excellent article, http://www.alsacreations.com/article/lire/1490-comprendre-le-viewport-dans-le-web-mobile.html, la plupart des terminaux mobiles ont une surface « réelle » qui correspond aux nombre de pixels physiques composant la matrice de l’écran et une surface « virtuelle » en pixels CSS, plus connue sous le terme de device-width/device-height et sur lequel se base en fait le terminal pour afficher les pages.

Et comme on peut le voir sur le site http://screensiz.es/phone , surface réelle et virtuelles ne correspondent pas forcément !

Pour couronner le tout, sur les terminaux mobiles, la valeur par défaut du « viewport » peut ne correspondre ni à l’un ni à l’autre… et dépend en fait du navigateur.
C’est ce qui explique les différences de zoom initial à l’ouverture d’une même page.
Pour s’assurer du bon affichage de son site quel que soit le navigateur, on peut recourir à la meta balise « viewport ».
Son attribut content offre la possibilité de fixer la largeur de viewport à la valeur souhaitée

<meta name="viewport" content="width=320">

Ou de l’adapter automatiquement à la valeur de device-width du terminal.

<meta name="viewport" content="width=device-width">

A noter enfin que cette balise ne fait partie d’aucune spécification, car elle a été introduite par Apple. Mais elle est généralement prise en compte par les autres navigateurs.
Il existe en fait un équivalent « officiel » du W3C sous forme de règle CSS @viewport, mais qui n’est malheureusement pas encore supporté par tous.

Quelques liens complémentaires forts intéressants pour démystifier le concept de viewport :

Tests

Emulation mobile sous Chrome

Les outils de développement Chrome permettent de simuler les événements tactiles (touch events) propres aux terminaux mobiles et qui n’existent normalement pas sur un navigateur de bureau.
Plus d’informations sur la page : https://developers.google.com/chrome-developer-tools/docs/mobile-emulation?hl=fr
On voit qu’en plus du support des touch events, les Chrome Dev Tools permettent également de simuler des tailles de viewports, l’orientation, différentes vitesses de connexion réseau, qui sont des spécificités propres aux mobiles qu’il faut prendre en compte.

Débugger une page dans Chrome pour Android depuis l’ordinateur

Il est possible de débugger les pages affichées dans le navigateur Chrome d’un téléphone Android connecté via un câble USB directement depuis un ordinateur. Pour cela, il faut :

  • Chrome 28+ sur l’ordinateur (tout OS) avec l’extension ADB installée
  • Chrome 28+ pour Android
  • Activer le débogage USB sur le téléphone

La procédure d’installation détaillée officielle est expliquée sur cette page : https://developers.google.com/chrome-developer-tools/docs/remote-debugging#remote-debugging
A noter que sur Windows uniquement, il faut impérativement installer le driver USB correspond au modèle du téléphone.
Plus d’informations sur ce lien : http://developer.android.com/tools/extras/oem-usb.html

Quant aux drivers pour les terminaux Google (Nexus…), ils sont téléchargeables sur la page suivante : http://developer.android.com/sdk/win-usb.html
Même en suivant point par point ces instructions, il se peut qu’un téléphone connecté ne soit pas reconnu directement par le plugin de Chrome. Ce fut le cas pour nous (Chrome 30 sur Windows 7 et Android + téléphone Nexus 4), il est alors nécessaire d’installer quand même ADB sur l’ordinateur.

  • Télécharger le SDK Android : http://developer.android.com/sdk/index.html
  • Décompresser l’archive dans le répertoire de votre choix
  • Ouvrir une invite de commande dans /platform-tools : adb devices
  • et vérifier que le téléphone est bien listé parmi les terminaux connectés

A ce stade, le plugin ADB de Chrome de l’ordinateur devrait proposer le téléphone dans : chrome://inspect/
et il est alors possible d’inspecter toute page ouverte dans le navigateur du téléphone, avec les mêmes outils de développement que pour la version web.

Débugger une page dans Firefox Mobile pour Android depuis l’ordinateur

A l’instar de Chrome, il est possible de débuguer une page ouverte dans Firefox Mobile à partir de la version Desktop du navigateur, comme expliqué sur la page « Remote Debugging » de Mozilla :
https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging
Les prérequis sont :

  • Avoir activé le débogage USB sur le téléphone Android (et éventuellement le driver USB pour Windows, voir le paragraphe sur Chrome)
  • Avoir activé le débogage distant dans le navigateur Firefox Mobile du téléphone (Paramètres > Outils de développement > Débogage distant)
  • Pouvoir utiliser ADB sur son ordinateur (voir la procédure d’installation résumée plus haut pour Chrome)

Pour se connecter depuis l’ordinateur au téléphone mobile, il faut rentrer la commande ADB suivante :

adb forward tcp:6000 tcp:6000

puis autoriser la connexion entrante sur le téléphone Android.
Malheureusement, il n’est pas possible pour l’instant, via le débogage distant d’utiliser les outils habituels du panneau « Inspecteur » pour inspecter les éléments, éditer du code HTML et des styles CSS à la volée. D’après la documentation, cette fonctionnalité sera rendu accessible avec Firefox 26.



Migration vers IE9…

…ou la descente aux enfers d’une application JSF, RICHFACES, SPRING

Voici un retour d’expérience sur un événement projet comme on est appelé à en rencontrer beaucoup sur le cycle de vie d’une application, notamment lors de la maintenance, les enseignements à en tirer sont nombreux.

Il s’agissait de migrer de version une application développée en JSF1.2/RichFaces de navigateur Internet Explorer 8 (IE8) vers IE9.

Pour avoir passé il y a peu la redoutable montée de version de IE6 vers IE8, l’équipe en charge du développement, parfaitement rompue à ce contexte technique, avançait avec confiance sur ce sujet. Tout au plus devait-il s’agir d’ajustements cosmétiques isolés…. Mais ce petit battement d’aile de papillon a provoqué une belle tornade sur toute l’application, dont voici les détails.

Cadre technique: l’application en question est conçue selon une architecture multicouche avec les librairies & versions suivantes, qui ont leur importance pour la suite des événements :

dépendances JSF, Spring, RichFaces, JUnit

Après un premier test rapide, les comportements Ajax qui sont massivement utilisés par RichFaces ne fonctionnent pas sous IE9. C’est étonnant à double titre :

  • Les appels ajax sont motorisés par jQuery dont la portabilité est un gros point fort,
  • Les développements jQuery faits par ailleurs ne présentent aucune régression

RichFaces fonctionnant en ‘boîte noire’, il a été décidé de migrer en version 4.0, réputée compatible avec IE9.

Dépendances JSF, RichFaces, Spring, Junit 2

Des changements en cascade

Comme représenté ci-dessus, RichFaces 4 ne se branche que sur du JSF 2.0 : ce sera donc notre première étape de montée de version collatérale.

Puis s’enchainent les étapes suivantes :

  • JSF2.0 impose Spring Web Flow 2.3
  • Spring Web Flow nécessite Spring (Core & Security)>=3.0 et Spring Faces >=2.3.0
  • Spring 3.0 nécessite de passer JUnit en 4.9 ; en effet, le chargement du fichier de contexte applicatif bloquait lors des lancements de test.

En arrivant à la situation où les briques au cœur de notre application ont été remplacées… ainsi que celles qui sont plus ‘périphériques’ :

Dépendances JSF, RichFaces, Spring, JUnit 3

Il faut surtout se figurer que chacune de ces montées de version a provoqué de difficultés :

  • Bugs notoires ( a4j :commandLink non opérationnels dans Spring faces 2.3.0) : application d’un patch ou bien attente de la version corrective ? c’est selon vos priorités.
  • Changement de configuration xml (faces-config.xml, web.xml, tous ces fichiers au cœur de l’application sont impactés),
  • Incompatibilité de versions de servlet avec Tomcat,
  • Modification de certaines balises JSF : impact sur toutes les JSPs,
  • Perte des espaces de nom sous Eclipse (entrant en confilt au niveau des xsd préalablement utilisés : Une montée de version du plugin spring d’eclipse peut corriger ces problèmes
  • …et encore bien d’autres plus ou moins intrusives

De plus, ces incohérences étaient parfois à l’origine de problèmes rencontrés seulement à l’exécution du serveur au travers d’exceptions NoSuchMethodException et java.lang.UnsupportedOperationException pour lesquelles la difficulté était de trouver la librairie en cause. Elles survenaient au chargement de l’application ou bien au détour d’une page, ce qui exigeait de faire des tests exhaustifs.

Voici quelques conclusions que nous pouvons en tirer :

1) Basique : IE9 et RichFaces 3.3.3 ne font pas bon ménage !

2) Architecture : les applications composites sont une approche rationnelle pour tirer profit des innombrables librairies adressant chacune au mieux le rôle pour lequel elles ont été créées. Si elles mettent effectivement à disposition une grande richesse de fonctionnalités, elles peuvent se révéler instables quand on doit en modifier un élément technique.

3) JSF (et autres frameworks Web à forte cohésion avec le serveur): la structuration d’applications JSF est intrinsèquement orientée serveur (règles de navigation, de contrôle, cycle de vie des Beans…) et de ce fait entraîne des changements profonds dans les cas problématiques comme celui-ci. Les pistes désormais explorées vers une séparation plus nette entre client Web et un serveur orienté REST permettra de clarifier certaines pratiques et alléger la responsabilité du serveur dans la génération de l’interface utilisateur.

4) Gestion des dépendances : engagés dans une application composite, il faudra couvrir les versions parfois conflictuelles de librairies, d’implémentations. Même outillée avec Maven, cela reste difficile quand c’est à l’exécution, avec des erreurs absconses, que cela se caractérise.

5) Tests : sur une application riche fonctionnellement, qui va traverser malgré elle quelques générations techniques, un portefeuille de tests automatisés sera le garant d’une bonne maîtrise de la non régression (Unitaires, Fonctionnels) ; Dans ce cas présent, l’application dispose d’un riche panel de tests JUnit à connotation ‘Intégration’, faute de scripts Selenium exhaustifs (seuls quelques uns on été créés).

6) Projet : De la difficulté récurrente de connaître l’impact d’une évolution, même sur un environnement technique et fonctionnel bien maîtrisé.

7) Open source : à la fois riche et ouvert, il peut se rendre problématique pour des demandes de support. Nous sommes dans le cas de communautés très importantes, très réactives, ce qui facilite la prise en compte des anomalies / évolutions ; mais cela sera dommageable sur des librairies de niches, peu documentée ou suivies…. La pérennité est à prendre en compte.



Pourquoi et comment mener un audit de code ?

« Jeter un regard objectif sur la qualité du code de l’application, pour en évaluer le niveau de maintenabilité »

Le contexte du projet

Une application connait plusieurs vies : la conception, la définition des principes d’architecture, l’implémentation, la mise en production du projet, puis la maintenance.
On définit classiquement en début de projet les éléments d’architectures logicielles, le rôle des différents composants, packages ou sous-projets et les bonnes pratiques à appliquer.

De manière très schématique, le projet aboutit à une version stabilisée lors de la première mise en production, avec une équipe ayant une connaissance métier et technique importante pour avoir initié et réalisé cette première phase. Mais quand vient la période des premières maintenances correctives et évolutives, l’équipe en place a été réduite ou remplacée, et les nouvelles réalisations doivent le plus souvent être réalisées par une équipe qui ne possède pas ou incomplètement la « mémoire » du projet.

A cela s’ajoute un phénomène classique lorsque le projet a connu une évolution conséquente de son périmètre fonctionnel lors de la réalisation :

  • Contraint par les délais, la date de livraison n’est pas modifiée,
  • L’équipe est sous pression pour livrer l’application à temps en jonglant entre : la couverture fonctionnelle, la maîtrise technique et la documentation,
  • Les phases de recette et les dispositifs consacrés à la qualité (lecture croisée du code, exécution et analyse de la qualimétrie, documentation) sont sacrifiés au profit du respect du jalon.

Dérive Coût Qualité Délai

Les audits de codes servent la viabilité du projet et peuvent être menés :

  1. Périodiquement pendant les développements pour surveiller la stabilité du niveau de qualité,
  2. A des moments clés : transmission de responsabilité du projet à un nouveau fournisseurou à une nouvelle équipe, passage du mode projet en mode maintenance, opération de benchmark.

La finalité est d’éviter ce que l’on rencontre le plus souvent sur des projets nés dans la difficulté (et ils sont nombreux…) et dont la maintenance a été chaotique : un mille-feuille de code instable, où l’on détecte les interventions de multiples intervenants qui impriment leur approche personnelle du codage, des normes, et leur interprétation des règles métier.

Les enjeux de l’audit de code

Lorsque nous initions un audit de code complet, nous nous attachons à le mener selon :

  • 1. Un axe ‘Architecture technique globale’ où nous étudions le découpage en sous-projets, en couches techniques, l’industrialisation et le respect dans le temps des normes et méthodes en vigueur ;
  • 2. Un axe métier, en sélectionnant un échantillon de fonctionnalités critiques qui sont représentatives de l’application :
    • De par la complexité et le nombre des règles de gestion impliquées,
    • De par la fréquence d’utilisation de la fonctionnalité lors de l’activité normale des utilisateurs,
    • De par la spécificité liée au métier : forte transversalité (comme la gestion d’un workflow), forte réutilisation attendue ;
  • 3. Un axe ‘outillage’ reposant sur des mesures automatisées avec un usage pondéré et personnalisé des outils :

Quelques généralités :
L’audit de code ne doit pas être dogmatique et il est nécessaire de savoir faire la part des choses exigibles selon la typologie du projet : complexité, criticité pour les métiers, ou en fonction des jalons dans la vie du projet (début, maintenance, fin).

Audit par échantillonnage fonctionnel

A partir des fonctionnalités identifiées, on vérifie que le couple « documentation + code » nous fournit les points d’entrée et la lisibilité nécessaires pour une activité de maintenance :

  • Nécessité d’identifier où se trouvent les fonctionnalités, comment elles sont organisées ;
  • Nécessité d’avancer en confiance dans le code en sachant comment les traitements de validation, contrôle, métier, ou persistance sont implémentés. A ce titre, il est impératif de vérifier que les règles de nommage sont homogènes et pertinentes et que la Javadoc est correctement renseignée.

Diagnostic audit d'échantillon fonctionnel

L’équipe d’audit détecte ainsi les forces et faiblesses du code de l’application sur les pans les plus sensibles et qui seront les plus concernés par la maintenance évolutive, laquelle pourrait apporter une certaine instabilité en cas de non maîtrise des fondamentaux techniques et fonctionnels.

La prochaine évolution sera-t-elle la carte qui provoquera la chute de ce qui tenait en équilibre jusqu’à présent ?

Audit par outillage

Cette approche permet de couvrir l’exhaustivité de l’application( avec certes moins de discernement que par échantillonnage), et de relever toutes les non conformités existantes avec différents niveaux de criticité : informatif, mineur et majeur.

En Java, les outils FindBugs et Checkstyle sont, par exemple, une première étape nécessaire pour révéler l’état de santé globale de l’application au regard des bonnes pratiques de codage.
On peut juger de manière globale chacun des domaines suivants :

  • Complétude de la JavaDoc,
  • Bonnes pratiques de codage (robustesse: default dans un switch, gestions des cas aux limites, des valeurs nulles),
  • Complexité du code : respect des tailles (nb de lignes par classe, par méthode, …), imbrications de boucles, lisibilité,
  • Duplication de code,
  • Règles de nommage
    • Pour plus de pertinence, l’équipe procède à une exécution par passes successives pour:

      • sélectionner les critères et calibrer les seuils,
      • avoir une vue plus précise en écartant temporairement les éventuels métriques tombés en alerte.

      Résultats synthétiques de l'audit avec CheckStyle

      En croisant les informations avec l’audit par échantillonnage, on dispose d’un maillage dense entre :

      • Le type de non conformités rencontrées,
      • L’homogénéité (ou non) de la répartition de ces manquements aux bonnes pratiques.

      Pour aller plus loin dans le détail et pouvoir juger dans la durée de l’évolution de la qualité du projet, nous utilisons InfoScope Source. Ce produit nous permet de donner une profondeur à plusieurs dimensions à notre audit technique :

      • 1. Une dimension technique
        InfoScope Source offre la possibilité d’ajouter / enlever des métriques, de définir des seuils de gravité.
      • 2. Une dimension de composants auscultés
        On observe au travers de l’outil les notes depuis une vue globale, par Projet, puis en profondeur :
        Projet > Package > Classe > Méthode

        A chaque niveau son tableau de bord récapitulatif.

      • 3. Une dimension temporelle
        InfoScope Source offre un suivi des tendances de la qualimétrie d’un projet dans le temps pour observer les progrès ou dérives.

      Stabilité parfaite de la qualité entre deux mesures

      Le résultat est une restitution navigable dans l’outil qui donne à chaque niveau le degré de maintenabilité du domaine observé ainsi que le détail des non-conformités entrant en jeu.


      Exemple de restitution exploitant Infoscope Source :

      Les problèmes se concentrent essentiellement dans les packages ‘services’ du projet. Cependant, ces classes sont notées très négativement et déprécient de ce fait la note globale. De plus, elles regroupent l’essentiel des lignes de code du projet et des règles métier, ce qui leur donne encore plus de poids dans la note finale.
      Les non-conformités les plus rencontrées sont :
      i. Non respect de la syntaxe des boucles ‘if’.
      ii. Complexité cyclomatique (imbrication de règles fonctionnelles, de boucles de traitement, de conditions) importante, voir extrêmement élevée.
      iii. Nombre de méthodes et du nombre de lignes par méthode non maîtrisé
      iv. Taux de commentaire insuffisant (notamment sur les classes les plus volumineuses et les plus importantes fonctionnellement)

      Les pré-requis pour mener un audit

      Les membres de l’équipe doivent disposer d’un solide bagage technique, c’est évident. Non seulement il faut avoir connaissance des bonnes pratiques générales pour pouvoir juger du code, mais il faut encore avoir travaillé sur suffisamment de projets et d’environnements pour comprendre les spécificités techniques et les choix d’architecture pour prendre le recul nécessaire.

      Il faut également connaître la façon dont fonctionne une équipe de développement, en mode projet ou en maintenance, pour identifier :

      • 1. Les raisons des dérives qui seraient observées,
      • 2. Pour caractériser la gravité de certaines non-conformités en termes d’impact sur l’organisation,
      • 3. Planifier de manière argumentée et priorisée les actions correctives.

      En sortie d’audit…

      L’équipe consolide les informations au travers de préconisations organisées selon leurs priorités : gravité de la non-conformité, ou impact potentiel sur l’application.
      On peut citer à titre d’exemple :

      • Renforcer la couverture de tests unitaires (en ciblant préférentiellement les classes au cœur de l’application),
      • Harmoniser et compléter la documentation de façon pertinente,
      • Respecter absolument l’inclusion des traitements cohérents dans une seule et même transaction (ACID),
      • Réduire les points et anomalies relevés par FindBugs et CheckStyle.

      Les équipes de maintenance disposent alors des éléments nécessaires pour faire vivre et évoluer l’application dans les meilleures conditions de sécurité, de coûts et de productivité.

      Enfin, Il est préconisé de mener ces audits de façon régulière, avant que la dérive n’exige un travail de mise à niveau trop conséquent et finalement décourageant.



Spring ROO, générateur d’applications JEE

Real Object-Oriented

ROO, c’est un générateur d’application JEE en mode ligne de commande, qui veut offrir une approche inspirée des meilleures pratiques retenues par Spring et offrir une grande productivité. Son positionnement en fait un candidat sérieux aux outils de mise en place technique d’un projet.
(Remarque : La version utilisée dans cet article est la 1.1.2.)

Console Roo sous Eclipse

La console ROO sous Eclipse (STS)

Principes généraux

L’architecture est structurée selon les principes suivants :

  • Concision du code, par l’usage intensif d’aspects (avec AspectJ) ;
  • Affranchissement de la couche DAO, les instructions de persistance étant rattachées aux entités
    • on appelle par exemple Utilisateur.findUtilisateur(id)
    • ou bien utilisateur.persist();
  • Découplage du code généré et du code modifié, pour continuer à utiliser les lignes de commande après implémentations des spécificités du projet ;
    • Le code généré est maintenu dans les classes d’Aspect,
    • Le code personnalisé est intégré aux .java à la main du développeur.
  • Ouverture technologique, grâce à un portefeuille de plugins richement doté, et appelé à être complété au vu des développements en cours (JSF 2.0, JAXB,Vaadin,Wicket…),
  • Approche Top-Down du modèle : la génération de la base de données se fait à partir de la configuration JPA des classes métier.

Un portefeuille d’extensions riche

En termes de librairies et d’outillage, c’est du classique quand on est dans l’écosystème Spring :
Maven, JPA, Spring MVC+ Security ,…
Les bases de données couvertes sont: DB2, ORACLE, MYSQL, H2, POSTGRES…, et dernièrement, Google App Engine !

Certains choix techniques sont en cours de changement, Dojo est appelé à être remplacé par jQuery dans la version 1.1.3.

Pour ce qui est de la persistance, le choix est donné pour l’implémentation de JPA : Hibernate, EclipseLink, DataNucleus…

De même, pour la présentation, on peut choisir entre Spring MVC associés à des JSPs ou bien GWT.

Prise en main & Utilisation

L’intégration dans STS (déclinaison d’Eclipse à la sauce Spring) est tout à fait opérationnelle, et même sympathique, grâce à une complétion conviviale et un accès à l’historique des commandes.
Le cloisonnement entre le code écrit et celui généré en lignes de commande est particulièrement efficace, sur les parties Modèle et Contrôleur.

Enrichissement de la classe modele

Enrichissement de la classe modele : JSON et JPA

Ce cloisonnement est plus sensible pour le code des tests en selenium ; on gardera à l’esprit qu’il vaut mieux demander à Spring ROO de générer les scenarios de tests après avoir stabilisé le développement du modèle.

Comportements relevés au fil de l’utilisation de ROO

Concernant GWT, le plugin nous a apporté quelques désagréments…. ayant conduit à l’abandon de cette approche, pour l’instant tout du moins; du code GAE est généré systématiquement, à tort (correction attendue en 1.1.3).

De plus, Il faut noter que l’approche par AspectJ limite fortement la pertinence des tests de couverture, nous n’avons pas trouvé à ce jour le moyen de faire passer Cobertura dans les aspects… et ils sont nombreux avec Spring ROO.

Certaines extensions sont seulement intégrées pour amorcer les développements, notamment l’utilisation de Spring Web Flow, qui est un simple outillage du projet pour amorcer des développements à la main.

En revanche, l’approche REST associée à la génération simplifiée de code JSON (basée sur flexjson), plaide beaucoup en faveur de ROO quand il s’agit de mettre rapidement en place un backend de données, pour vos développements Mobile par exemple.

Utilisation de Selenium

L’outillage du projet avec selenium est intéressant. On pourra toujours gloser sur l’intérêt de tester ‘fonctionnellement’ des écrans qui sont surtout d’ordre ‘gestion de tables de référence’, mais on dispose en l’état d’une première brique opérationnelle pour notre campagne de tests :

Commande Roo Selenium

Ajout de scripts Selenium à un contrôleur Spring MVC

Que l’on lance dans la fenêtre de commande avec :
mvn selenium :selenese
A noter que si on ajoute un champ dans l’entité Appartement, le script selenium n’est pas impacté et l’exécution suivante tombe en erreur…. Il vaut mieux construire ses tests en fin de cycle, autant que possible.

De plus, les données en base ne sont pas nettoyées à chaque cycle. il faudra introduire un mécanisme de nettoyage ou de remise à niveau de notre jeu de test. On a l’assurance de pouvoir intégrer sans efforts les scripts générés grâce au plugin Selenium de FireFox dans le portefeuille de tests du projet ; mais dans ce cas, nous avons commencé à basculer vraiment vers le cœur de notre projet.

Quelques avertissements et précautions

ROO souffre encore de certains défauts de jeunesse qui, sans remettre en cause son utilisation, doivent conduire à prendre quelques précautions :

  • Les tests unitaires plantent à l’exécution (ceux de type ‘tests mock’) ;
  • Les relations ManyToMany ne sont pas correctement intégrées dans les JSPs ;
  • L’annotation @NotNull a provoqué des plantages d’écrans spectaculaires ;
  • Et quelques autres…

Mais la communauté est active et les versions se succèdent à un rythme soutenu.
On bénéficie en tout état de cause d’un référentiel de bonnes pratiques.