jeudi 2 décembre 2010

Tests super sonics

Hier j'ai assisté à une conférence au Alpes JUG, enfin plutôt deux du même présentateur, David Gageot. Il a d'abord parlé de Git, puis de ce qui m'intéressait plus - comment rendre et les tests très très rapides.

Algodeal tourne leurs 600 classes de test et 60 classes de tests fonctionnels/intégration en moins de 4 minutes!! David n'est pas allé dans le détail des métriques, mais cela couvrirait 60.000 lignes de code.





Voici la liste de conseils que j'ai trouvé pertinents :

  • Effacer du code (et tests) non utilisé (fonctionnalités devenus obsolètes)
  • Faire tout in-memory (DB, mail-server, http-server, ...)
  • Paralléliser (soit les threads junit, soit un projet multi-module)
  • No file access (Apache VFS, Spring ressources)
  • Prenez une suite de tests fonctionnels, découpez en UN test fonctionnel, TOUS les autres cas en tests unitaires.
  • Dans les tests fonctionnels il n'hésite pas à mocker les parties coûteuses
  • Battez-vous contre la complexité de votre application - les utilisateurs veulent une application simple, et il y a un fort lien entre la simplicité de l'utilisation et la simplicité de l'implémentation.
  • Faites un maximum de choses server-side - c'est bcp moins coûteux en temps d'exécution et maintenance de tests.
  • Si vous faites du ajax, encore une fois UN test fonctionnel (éventuellement pas sur plusieurs browsers). Toutes les variations en TU de javascript)
  • Rendez votre application plus rapide, les tests le seront aussi :-)

En regardant cette liste je me dis qu'il n'y a rien d'extraordinaire, mais ça m'a quand même fait l'effet d'une délivrance. C'est une chose d'avoir un sentiment, voire une conviction. C'est autre chose de voir quelqu'un l'appliquer avec succès.

Il a répété plusieurs fois que certes on peut mieux se protéger contre des bugs en prod (et encore je ne suis pas si sûr), mais à quel prix?

Les slides

Il a aussi parlé de GIT. Je m'en sers au quotidien et je savais déjà que c'était génial. Maintenant je sais que c'est mieux que ça.
Les slides

samedi 11 septembre 2010

Faisons plus d’interfaces fluides – puisque c’est simple!

Ca fait quelque temps que j’utilise des frameworks avec une interface fluide (cf Martin Fowler Fluent Interface). Je pense à Mockito, JMock2 et Hibernate Query API entre autres. Depuis peu j’utilise fréquemment le Test Data Builder Pattern. Ces derniers sont plutôt des Domain Specific Languages, une version évoluée des interfaces fluides. Mais je n’avais jamais réalisé que ce soit un jeu d’enfant de créer sa propre interface fluide. Prenons par exemple

Math.pow(3, 2)

Ca fait quoi au juste? Ah oui c’est l’exposant 3 de 2. Ou peut-être l’exposant 2 de 3?  Et si on écrivait plutôt



exponent(3).of(2)

Ici on voit bien ce qui se passe! Certes la javadoc de Math.pow(double a, double b) est clair et ça marche bien quant on écrit du code, mais pas quand on lit du code déjà écrit. Puisqu’on estime que du code est lu 10-100 fois plus qu’il n’est écrit (sans parler d’un framework open source), il est très important de rendre la vie facile à nos lecteurs.


Mais alors qu’est-ce ça demande comme effort de transformer Math.pow(a, b) en exponent(a).of(b)? Très peu en fait :



class Exponent {
private final double exponent;

public Exponent(double exponent) {
this.exponent = exponent;
}

public static Exponent exponent(double exponent) {
return new Exponent(exponent);
}

public double of(double base) {
return Math.pow(base,exponent);
}
}

puis un import statique dans la classe client



import static Exponent.exponent;

L’import statique avec le Factory Method exponent() jouent un rôle centrale, ils évitent le bruit syntaxique qu’il y dans



new Exponent(3).of(2)
//ou
Exponent.exponent(3).of(2)


J’ai pensé à cette astuce lorsque j’étais entrain de travailler sur un défi TDD – faire un convertisseur du système décimal vers un système quelconque. Dans ma solution il y avait cette fonction qui sert à calculer le chiffre à une position donnée dans le nouveau système. Ainsi si je veux transformer 3 en système binaire, pour connaitre si j’ai le chiffre à la position 0 (le plus à droite) je fais (3 modulo 2^1) quotient 2^0. Et pour la position 1 soit la plus à droite (3 modulo 2^2) quotient 2^1.


J’étais moyennement content de ça



private String digitAtPosition(int pos, int tenBaseNumber) {
int nextPos = pos+1;
long remainderOfNextPos = tenBaseNumber % nthPowerOfBase(nextPos);
long digitInTenBase = remainderOfNextPos / nthPowerOfBase(pos);
return convertSingleDigit(digitInTenBase);
}
protected final long nthPowerOfBase(int n) {
return round(pow(base,n));
}

on lit plus facilement



private String digitAtPosition(int pos, int tenBaseNumber) {
int nextPos = pos+1;
long remainderOfNextPos = tenBaseNumber % exponent(nextPos).of(base);
long digitInTenBase = remainderOfNextPos / exponent(pos).of(base);
return convertSingleDigit(digitInTenBase);
}

De plus cette classe extraite, Exponent, n’a rien de spécifique à mon convertisseur et pourrait potentiellement être utilisée ailleurs, promouvant ainsi la réutilisation. De plus et de manière générale une interface fluide est le début d’un DSL que l’on verrait plus facilement apparaitre à des parties du code fortement utilisés.


Et la performance?



Ca doit coûter cher de créer un objet à chaque fois, non?


Executer exonent(3).of(2) un million de fois prend 0,9s là ou Math.pow(2, 3) prend 0,64s sur mon portable, si j’augmente l’exposant à chacun des un millions d’appels (histoire de ne pas me faire avoir par une optimisation du JVM) j’obtiens 0,84s contre 1,4s. On reste tout de même à l’échelle du nano-second, alors à moins que cela soit le cœur de mon application je m’arrêtes volontiers à la deuxième étape dans ces bonnes principes



  1. Make it work


  2. Make it good


  3. (Then perhaps) Make it fast



Et si le besoin de rapidité apparait, il y a plusieurs façons d’attaquer le problème, revenir en arrière, ou introduire un cache des objets crées, ou en modifiant l’interface un poil :




exponent(3, of(2))
...

public static double of(double number) {
return number;
}



Des variantes de la classe Exponent



Pour ceux qui trouvent que la classe Exponent est trop verbeuse, il y a un peu plus bref ( en utilisant les double accolades pour accéder à l’instance initializer)



class Exponent {
double exp;
public static Exponent exponent(final double exponent) {
return new Exponent() {{
this.exp = exponent;
}};
}

public double of(double base) {
return Math.pow(base,exp);
}
}

encore une autre qui évite la variable d’instance et qui permet d’autres implementations de of() – par exemple log(logBase).of(someNumber).



class FluidMaths {
public static Exponent exponent(final int exponent) {
return new Exponent() {
public Long of(int base) {
return round(pow(base,exponent));
}
};
}

interface Exponent {
public Long of(int base);
}
}





 






  

mardi 20 avril 2010

Amélioration des rétrospectives – une approche minimaliste

Les rétrospectives dans l’équipe dans laquelle je travaille laissent beaucoup de place à l’amélioration : En fin de sprint de deux semaines on fait un tour de table pour avancer les points positifs et les points d’amélioration. Point.

On était deux dans l’équipe à souhaiter quelque chose de plus ambitieux. Alors on s’était renseigné pour avoir une personne extérieure à l’équipe, voire un coach. Pour divers raisons rien s'était passé en six semaines, alors on a décidé d’améliorer ce que nous avions déjà avec les moyens du bord. Le déclencheur a sans doute été notre présence à XP Days Suisse où nous avons assisté à une session sur les rétrospectives (compte rendu de Bruno Orsier, Luc Jeanniard)

Nous avons commencé par revoir la dernière rétrospective pour savoir s’il y avait des points importants qui n’avaient pas été adressés. Ensuite nous avons conduit la rétrospective comme avant.

Au lieu de laisser les points d’amélioration cachés dans une page au fond d’une application quelque part, nous avons priorisé les points. Chacun pouvait voter pour les deux points qu’il trouvait les plus important. L’engagement était de faire quelque chose pour les 2-3 points les plus importants dans le sprint suivant. Voici nos points :

  1. Améliorer la qualité des scénarios de BDD (Behavior driven development)
  2. Diminuer le délai entre le début d’une livraison et la mise en production (le problème étant surtout un problème de délai et non de charge de travail)
  3. Faciliter le ramp-up de deux collaborateurs arrivés récemment en imprimant un schema de la base de données.

Le point 3 étant très simple nous l’avons simplement fait le lendemain.

Pour améliorer la phase de déploiement nous avons décidé d’actions concrètes :

  • Evaluer en détail la faisabilité de plusieurs pistes connus pour simplifier la procédure de déploiement, notamment en éliminant les tâches manuelles.
  • Décrire au moins deux options d’architecture pour permettre un blue-green deployment.

Ces deux actions ont été planifiées dans le sprint AVEC une priorité importante – afin d’être sûr qu’elles seront adressées.

Finalement pour adresser le premier point nous avons discuté avec tous les acteurs concernés afin d’établir un plan d’action. Le voici :

  • Product Owner : exprime ses idées à travers un scenario
    • afin de venir avec des propositions plus réfléchies
  • Product Owner + Scrum Master + Dev Lead : font des backlog reviews systématiques avant le sprint planning. Cette réunion donne lieu à deux livrables
    • DraftScenarios (Des scenarios suffisamment précis pour une estimation en poker planning)
    • Une priorisation du product backlog
  • Product Owner + dévelopeur(s) : écrivent ensemble les GoScenarios ( ce qui signifie des scenarios prêts pour être développés).

Conclusion

Avec des améliorations ultra simples nous allons, je pense, tirer beaucoup plus des rétrospectives qu’auparavant. C’est tellement simple que j’avais l’impression que c’était bête d’en blogger, mais à mon avis cette équipe n’est pas la seule à faire des rétrospectives bâclées. Et cette expérience montre que même avec peu de moyens en temps et en connaissance on peut faire des améliorations importantes.

Prochaines étapes :

  • Lire ce livre
  • Faire une rétro un peu plus longue en jouant toutes les phases d'une rétro avec root cause analysis
  • Afin d'avoir des animateurs qui ne font pas parti de l'équipe (c'est plus facile d'animer une retro si on est partie prenante!) on a eu l'idée qu'un de nous fasse la rétro d'une autre équipe et que cette autre équipe nous rende le service.

dimanche 24 janvier 2010

Sonar & eclipse + PMD & Checkstyle & Findbugs

J’aime beaucoup le feedback rapide que donne les plugins PMD, Checkstyle et Findbugs dans Eclipse. Mais il y avait certains freins à l’utilisation:

  • Checkstyle a trop de règles activées par défaut. Le travail de désactivation est laborieux.
  • Ce n’est pas facile de faire en sorte que tout le monde dans une équipe utilise les même règles.
  • Surtout lorsqu’on décide de les mettre à jour.
  • Sonar utilise les mêmes plugins, avec une configuration différente par défaut. Le résultat est que beaucoup de violations perdurent car c’est bien plus compliqué d’aller voir l’interface Sonar que de réagir à un avertissement qui apparait dans l'IDE.

Une solution est de centraliser les règles de à travers Sonar permettant ainsi de maintenir un seul ensemble de règles. Ce que je décris ici est une suite à un billet sur Checkstyle par Freddy Mallet.

Installer les plugins CheckStyle, FindBugs et PMD

Dans Sonar aller dans Configuration => Quality Profiles. L'onglet Permalinks copier les liens vers les règles PMD/CheckStyle/Findbugs pour chacun des profiles.

Dans Eclipse => Menu Window => Preferences :

  • Checkstyle -> New... -> Remote Configuration
  • Location : http://<sonar>/rules_configuration/export/java/<rule-name>/checkstyle.xml
  • Press 'Set as Default'

  • PMD -> (subsection) Configuration des règles -> Tout effacer -> Importer un ensemble de règles :
  • http://<sonar>/rules_configuration/export/java/<rule-name>/pmd.xml
  • Press OK

  • Findbugs
  • download file http://<sonar>/rules_configuration/export/java/<rule-name>/findbugs.xml
  • Window –> Preferences -> Java -> (subsection) Findbugs -> Filter files
  • Add the downloaded file to 'include filter files'

Petit plus. Je trouve les règles Checkstyle dans le profil par défaut de Sonar beaucoup plus pertinentes que celles livrées avec le plugin pour Eclipse.

Depuis peu un plugin sonar pour eclipse est disponible.

Edit 2011-04-07 :
- mis à jour l'endroit dans sonar où on trouve les liens des profiles
- ajout lien plugin sonar