Auteur : Eric

ViaXoft Labs Days : quésako

Les Viaxoft Labs Days quésako ?

Au delà de notre quotidien qui nous conduit à faire évoluer nos applications, il est important d’accorder de la place à la veille technique liée directement ou non à nos préoccupations opérationnelles.

Pour répondre à cette problématique, nous organisons périodiquement les « Viaxoft Lab Days ». Les VLD sont des journées qui regroupent l’ensemble des équipes de développement de ViaXoft.

Notre objectif au cours de chacune d’elle, est de « débroussailler », creuser, explorer un sujet technique, un framework, un concept.

Le livrable en fin de journée est un POC (Proof of concept) qui permet de présenter la techno abordée. L’organisation de chaque journée est définie par l’équipe elle-même en fonction du sujet traité (pair programming, travail en équipe, micro étude préalable, …).

L’équipe liste les idées de sujets à aborder d’une réunion sur l’autre, puis nous votons pour sélectionner le ou les sujets qui seront abordés lors de la prochaine journée. Les journées déjà organisées ont traité des sujets tels que la mobilité, NoSql, API Google, Kinect, Leap Motion et Arduino.
Sujets plus ou moins proches de notre quotidien, mais qui nous permettent d’ouvrir le champ des possibles et d’emmagasiner des connaissances qui pourront peut être nous servir dans le futur.

Scrum : retour aux fondamentaux

Retours sur la méthode Scrum après 6 ans d’utilisation

Les 10 et 11 Avril se déroulait la quatrième édition du ScrumDay. L’occasion pour nous de découvrir pas mal de nouveautés mais également de revenir par certains aspects sur les notions essentielles de l’agilité.

La keynote d’ouverture était assurée par Alistair Cockburn, signataire et co-auteur du manifeste agile. Les slides de la keynote se trouvent ici.

Il est revenu sur les 4 valeurs du manifeste et leur trop (souvent) mauvaises interprétations puis a introduit un concept issu de l’Aïkido qui décrit les 3 étapes de l’apprentissage :

  • Shu (守:しゅ?, « protéger », « obéïr ») — sagesse traditionnelle – apprendre les fondamentaux
  • Ha (破:は?, « se détacher », « digresser ») — casser avec la tradition — trouver les exceptions à la sagesse traditionnelle, trouver de nouvelles approches
  • Ri (離:り?, « quitter », « se séparer ») — transcender — il n’y a pas de technique ou de sagesse traditionnelle, tous les mouvements sont permis

(source wikipedia)

Ce concept est pour lui réplicable quelque soit le domaine d’application et en particulier l’agilité.

Il a ensuite zoomé sur Scrum et ce qu’il nomme le coeur de Scrum :

  1. Livrer à chaque sprint
  2. Laisser l’équipe décider
  3. Inspecter et s’adapter tous les jours, et à chaque sprint
  4. Un Scrum Master a du temps pour lever les obstacles
  5. Le métier ne s’exprime qu’à travers une seule voix (Product Owner)

Mais le plus intéressant est qu’il considère « tout le reste » comme « barnacles » (parasite) ou pire rumeur et ouï-dire :

  • Burn-down charts
  • Kanban (Not-started | Started | Done) boards
  • ScrumMaster is / is not the project manager / product owner / tech lead
  • Product Owner is / isn’t invited to the dailies
  • The “3 questions” at the daily stand-up
  • User stories
  • Planning Poker
  • Fibonacci numbers / hours / story points

Il ne veut pas dire par là que l’on doit jeter toutes ces pratiques à la poubelle mais bien qu’elles doivent être choisi par l’équipe elle même si celle-ci considère que telle ou telle pratique apporte un plus aux 5 points points qui constituent le coeur de Scrum.

Et nous dans tout ça ?

Cela fait presque 6 ans que nous « utilisons » Scrum. Nous avons mis en place toutes ces pratiques et plus, puis en avons abandonné car l’équipe ne les trouvait pas/plus adaptées :

  • Pas adaptée comme par exemple le planning poker : nous trouvions que cette pratique était trop fastidieuse et pas assez productive par rapport à la taille de notre backlog. Avec cette pratique, nous estimions environ 10 user stories en une heure alors que nous en avions 100 à chiffrer. Nous avons depuis mis en place une autre pratique qui nous correspond plus.
  • Plus adapté comme par exemple la revue de backlog : dans les premiers mois/années nous faisions une revue de backlog périodique avec l’équipe qui permettait à notre PO d’affiner les User Stories grâce au commentaires de l’équipe et de débuter une conception de haut niveau. L’expérience (culture) aidant, nous n’avons plus eu besoin de cette revue et si certains points ne sont pas clairs, nous avons la faculté de donner des réponses « à la volée » lors des démarrages de sprint (connaissance partagée).

Depuis quelques mois nous avons débuté de nouveaux produits et patatra… Nous nous sommes pris les pieds dans le tapis, sans vraiment s’en rendre compte. Nous avons adopté les mêmes pratiques et réflexes (nature) que pour notre produit principal, mais en omettant quelques détails :

  • Nouveautés produits (fonctionnel et conception)
  • Manque d’expérience de l’équipe : pas en terme d’années mais sur les produits, les rôles (PO) et l’architecture

Ces problèmes sont, bien entendu, remontés lors des rétrospectives de sprint, mais le diagnostic que nous en avons fait était assez parcellaire.

Un petit détour par la Coach Clinic du ScrumDay (merci à Fabrice Aimetti@fabriceaimetti) suite à la keynote, quelques échanges de tweets, et la lumière fut : le contexte dans lequel nous évoluons n’est plus le même, nous devons (ré)apprendre, (re)pratiquer (Shu) pour ensuite progresser dans le Ha et le Ri. L’équipe va donc devoir se réapproprier certaines pratiques qui lui permettront sur ces nouvelles bases d’acquérir l’expérience nécessaire à son évolution.

En conclusion et avec une vision plus « aérienne », nous devons constamment évoluer dans une spirale dynamique : pratique, culture, nature, pratique, culture, nature, … afin de nous permettre de revenir aux fondamentaux lorsque c’est nécessaire pour ensuite transcender et transgresser l’existant pour toujours être en mouvement. (Merci à Pablo Pernot @pablopernot)

« Notre nature est notre culture, et notre culture est notre nature » Edgar Morin. (Pablo, La Horde Agile)

eXtreme Quotation à l’Agile Tour Montpellier

Retours sur la session eXtreme Quotation à l’Agile Tour Montpellier

J’ai assisté à la session eXtreme Quotation animée par Guillaume Duquesnay (@duquesnay) et Jonathan Scher (@jonathan_scher) à l’Agile Tour Montpellier.

Je profite de ce billet pour remercier les organisateurs et les sponsors pour cette journée riche et bien remplie et le super accueil réservé aux participants.

Revenons à nos moutons…

J’étais un peu septique en allant à cet atelier. Le titre était vendeur, m’attirait même. J’y allais surtout car le Planning Poker tel que nous le pratiquons me semble lourd et je cherchais plutôt quelques astuces pour l’améliorer…

Mais là on nous promettait de « chiffrer 90 stories en moins de 20 minutes  » !

Vous trouverez une description détaillée de l’atelier ici et ici (la forme présentée à #atmtp utilisait les tailles de T-Shirt).

Au cours de l’atelier nous avons estimé le backlog de création d’un service de messagerie tel que Gmail.
Nous étions répartis en deux groupes d’environ 20 à 25 personnes (la taille des groupes mettait également à mal l’atelier). Au final nous avons bien estimé environ 50 stories en 20 minutes et malgré la méconnaissance du projet et du contexte général, la répartition s’est faite assez facilement.
Le résultat est donc vraiment positif.

Je pense que nous allons tester cet atelier à la place de notre prochain planning poker ; nous perdrons un peu de précisions sur nos estimations en point mais ce n’est en réalité pas très grave :

  • à ce moment là des estimations nous ne recherchons pas un chiffrage à l’heure près,
  • nous gagnerons beaucoup de temps de l’équipe et pourrons de ce fait en faire plus souvent, notamment pour décomposer et réestimer les user stories atteignant les tailles XL ou XXL,
  • cela permettra de créer une dynamique d’équipe que nous n’avions pas dans le planning poker.

Merci en tout cas aux deux intervenants pour la mise en avant de cette technique, qui montre encore une fois les bienfaits de l’amélioration continue.

Agile Games : soirées Agile After Work

L’Esprit Agile a le plaisir de vous faire découvrir ses prochaines soirées Agile After Work !

Découvrez les ateliers Agile Games !

Voici une brève description de l’apport des jeux proposés :

  • agile gamesChaises non musicales : Le but est de mettre en avant l’importance de l’auto organisation, la communication, la simplicité et la confiance au sein d’une équipe.
  • Agile Airplane : permet de mettre en avant certaines notions d’agilité : priorisation, démo au client, itération, auto-organisation, test d’acceptation, rétrospective.

 

agile games

  • Business Value Game : ce jeu donne des techniques pour trouver la valeur métier des user stories.
  • Marshmallow Challenge : mets en avant l’importance des itérations, des tests et de l’autorganisation.
  • Multitâche : montre que le multitâche est beaucoup moins productif.

 

N’hésitez pas à voter pour votre session préférée sur Doodle.

Agile After Work du 13 juin 2012 – Lego4Scrum

 Agile After Work

 

L’Esprit Agile a le plaisir de vous inviter à participer à sa prochaine soirée Agile After Work :

Le mercredi 13 juin 2012 à partir de 18h30

à L’Hôtel Technologique – 45 Rue Frédéric Joliot Curie – 13013 Marseille

Sur le thème : « Agile Games : Lego4scrum ».

Animateur : Romain Vignes

(https://twitter.com/#!/rvignes)

Le nombre de places à la soirée Agile After Work étant limité, les inscriptions sont obligatoires

Inscription Plan d’accès

Marshmallow Challenge ou l’importance du prototypage

Retours sur le Marshmallow Challenge

Nous avons été invité par le PMI lors d’une après-midi dédiée à l’agilité.

Suite à une présentation de l’agilité par Laurence Hanot du CARA, j’ai fait une présentation de SCRUM en 15 minutes, suivie d’un mini retour d’expérience sur son application.

Puis nous avons scindé la salle en deux ateliers, « Artistes et spécifieurs » pour Laurence et le « Marshmallow Challenge » pour Claire (@clrh) et moi.

C’est de ce dernier atelier dont je vais parler dans ce billet.

 

J’avais assisté à l’atelier animé par Pablo Pernot lors de l’Agile Tour Marseille 2011 et j’avais été surpris par le dynamisme de celui-ci (Pablo y était pour beaucoup  ).

L’objectif de cet atelier est de mettre en exergue l’importance du prototypage (itération) et de tester très tôt.

Vous trouverez toute la documentation nécessaire pour l’organisation du challenge et quelques retours chiffrés très intéressants (nous reprenons ces données dans notre présentation) sur le site du Marshmallow Challenge.

 

Je voulais revenir dans ce billet sur l’évolution de nos équipes tout au long du challenge.

Le groupe était constitué de 2 équipes de 4 personnes et 2 équipes de 3 personnes. Sans trahir la fin du billet, le différenciel de nombre n’a pas eu d’impact sur le résultat.

La première chose frappante est que dès le début de l’atelier les 4 équipes ont sorti une feuille de papier pour commencer une phase de conception.

La première équipe à avoir testé la résistance des spaghettis par rapport au Marshmallow l’a fait au bout de 4’30 (équipe 2), ce qui en soit est pas mal ; mais elle n’a pas su capitaliser sur cet avantage et est reparti dans sa phase de conception.

Les deux équipes suivantes (1 et 3) ont fait leurs premiers tests avec le Marshmallow entre 7’ et 7’30. Pour l’équipe 1 ce n’était pas vraiment un test de résistance mais un début de construction.

Ce qui est intéressant avec l’équipe, c’est qu’en ayant validé cette première phase de construction, ils ont repris une phase de conception sans se soucier réellement du temps qui passait.

L’équipe 4 a fait toute sa phase de construction de la structure à plat sur la table et a voulu la mettre debout au bout de 16’30. Bien sûr ce qui devait arriver arriva, la pyramide n’a pas supporté le poids du Marshmallow.

Au final les 4 équipes ont suivi à peu prêt le même cheminement avec une phase conception assez longue et très peu de tests.

Seule l’équipe 1 a présenté une structure à la fin des 18 minutes, en gardant celle de leurs premiers tests.

Nous avons ensuite fait une rétrospective avec chaque équipe. Le point qui est le plus ressorti de ces rétrospectives est que les membres des équipes se sont bien entendus. Finalement le résultat (et l’échec) passe au second plan car ils ont eu l’impression d’avoir suivi des phases précises mais qu’ils ont eu du mal à gérer le temps assez court.

 

Au final c’était un atelier très intéressant qui je pense a fait comprendre l’importance des itérations et des tests à tous les participants.

1370961836_doc_pdfVoir la présentation Marshmallow Challenge (PDF, 3.41MB)

Sélection de colonnes dans Hibernate

 Liste avec seulement quelques colonnes dans Hibernate

Les outils d’ORM (Hibernate, Ibatis, …) sont aujourd’hui à maturité et ne souffrent plus des problèmes de performance des premières versions. Le code SQL généré, ainsi que la transformation en objets, ont été très largement optimisés.

Cependant selon la taille de la grappe d’objet remontée, il peut subsister des problèmes de performance, liés au nombre et à la complexité des objets à créer. Le cas d’utilisation typique sur une application de gestion est l’affichage d’une liste d’objet complexe sur un écran de recherche.

Prenons le cas par exemple d’une liste de commande (que nous simplifions volontairement) :

public Class Commande {

private Destination destination;

private Set<LigneCommande> lignes;

private Client client;

...

}
Une requête Criteria qui permet de remonter la liste des commandes ressemble à ceci :
DetachedCriteria criteria = DetachedCriteria.forClass(Commande.class,"commande")

.createAlias("commande.Destination", "destination", CriteriaSpecification.LEFT_JOIN)

.createAlias("commande.Lignes", "lignes", CriteriaSpecification.LEFT_JOIN)

.createAlias("commande.Client", "client", CriteriaSpecification.INNER_JOIN);

List<Commande> listCommande = getHibernateTemplate().findByCriteria(criteria);
Cette requête remonte donc tous les objets Commande (toutes les colonnes de la table correspondante), ainsi que les objets liés (et donc toutes les colonnes associées). Dans le cas où la grappe d’objets est importante et composée de nombreux objets, le nombre de lignes remontées devient très important et la constitution des objets très coûteuse  Rappelons que nous souhaitons simplement afficher une liste avec seulement quelques colonnes… au moins 95% des données remontées ne nous servent donc à rien.

Depuis la version 3 d’Hibernate a été introduite la notion de Projection. Celle-ci va nous permettre non seulement de remonter seulement les colonnes qui nous intéressent mais également une liste d’objets non gérés par Hibernate ; par exemple nous allons pouvoir remonter directement une liste de DTO (data transfert object) en se basant sur le même objet criteria (forClass(Commande.class, »commande »)) :

On définit le DTO :

public class DealListDto implements Serializable  {
    private static final long serialVersionUID = 1L;

    private java.lang.String id;
    private String ref;
    private Date date;
    private String nom;
    private String prenom;
    private String destNom;
    ...
}
puis les propriétés que l’on souhaite remonter, ainsi que le bean avec lequel on veut les mapper :
criteria.setProjection(Projections.projectionList()

.add(Property.forName("commande.Id"), "id")

.add(Property.forName("commande.Ref"), "ref")

.add(Property.forName("commande.Date"), "date")

.add(Property.forName("client.Nom"), "nom")

.add(Property.forName("client.Prenom"), "prenom")

.add(Property.forName("destination.nom"), "destNom")

.setResultTransformer( Transformers.aliasToBean(DealListDto.class));

List<DealListDto> listCommande = getHibernateTemplate().findByCriteria(criteria);
Nous obtenons donc une liste de DealListDto, beaucoup plus légers, que nous pouvons  faire remonter directement sur la couche client.

Datasources en hosted mode avec GWT 1.6

Configuration des datasources en hosted mode avec GWT 1.6

La migration de GWT de la version 1.5 à la version 1.6 s’est accompagnée de changements en profondeur au niveau du hosted mode. En effet, celui-ci utilisait précédemment une version embarquée du serveur tomcat, qui a été remplacée dans la dernière version par jetty.

Ce changement a donc entraîné une restructuration au niveau de la configuration des projets et notamment de la déclaration des datasources *. Cet article va donc présenter la configuration des datasources en hosted mode avec GWT 1.6.

La première étape est de rajouter les jar jetty-naming-6.1.11.jar et jetty-plus-6.1.11.jar à notre projet. Ceux-ci permettront d’utiliser notre propre fichier de configuration de jetty. Si comme nous vous utilisez Maven pour gérer les dépendances de votre projet, rajoutez les deux entrées suivants à votre pom.xml :

<dependency>
<groupid>org.mortbay.jetty</groupid>
<artifactid>jetty-naming</artifactid>
<version>6.1.11</version>
</dependency>
<dependency>
<groupid>org.mortbay.jetty</groupid>
<artifactid>jetty-plus</artifactid>
<version>6.1.11</version>
</dependency>

Sinon, vous trouverez ces jars sur le site de jetty.
Il faut maintenant crée un fichier nommé jetty-web.xml qui contiendra la déclaration de nos datasources sous la forme suivante :

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure>
<New id="dataset1">
<Arg>java:comp/env/jdbc/dataset1</Arg> <Arg>
<New>
<Set name="Url">jdbc:mysql://localhost:3306/database1?autoReconnect=true</Set>
<Set name="User">username1</Set>
<Set name="Password">password1</Set>
</New>
</Arg>
</New>
<New id="dataset2">
<Arg>java:comp/env/jdbc/dataset2</Arg>
<Arg>
<New>
<Set name="Url">jdbc:mysql://localhost:3306/database2?autoReconnect=true</Set>
<Set name="User">username2</Set>
<Set name="Password">password2</Set>
</New>
</Arg>
</New>
</Configure>

C’est cette configuration qui va indiquer à quelle base de donnée correspond chacun des datasets, et les paramètres de connexion. Dans cette exemple, nous déclarons deux datasets qui utilisent les bases de donnée MySql database1 et database2 du serveur local.
Il faut ensuite référencer les datasources dans le web.xml pour que notre application soit en mesure de les utiliser :

<res-ref-name>java:comp/env/jdbc/dataset1</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<res-ref-name>java:comp/env/jdbc/dataset2</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

Une fois cette étape réalisée, vous pouvez utiliser vos datasets au sein de votre application comme vous le feriez normalement :

DataSource source = (DataSource)ctx.lookup("java:comp/env/jdbc/dataset2");
Connection connection = source.getConnection();
//...

 

Caches applicatifs avec Spring et ehcache

La recherche de performances applicatives passe souvent par la mise en place de caches applicatifs.

Ces caches permettent de maintenir en mémoire les données souvent requêtées par les utilisateurs mais peu mises à jour (paramétrages, villes, …).
Spring offre la mise en place d’un tel cache assez facilement par l’intégration d’un framework tel que ehcache.

Dans le fichier configuration de Spring on ajoute les lignes suivantes :

<ehcache:config failQuietly="false" />
<ehcache:annotations>
<ehcache:caching id="test" cacheName="testCache" />
<ehcache:flushing id="test" cacheNames="testCache" when="after" />
</ehcache:annotations>

Dans le fichier configuration de ehcache.xml, on retrouve la définition du cache : nombre d’objet en cache, écriture sur disque, politique d’éviction,…Le cacheName et ce que l’on va retrouver dans le fichier de configuration de ehcache,
L’id et ce que l’on va retrouver dans l’annotation,
caching l’id est utilisé pour la mise en cache,
flushing l’id est utilisé pour la libération du cache.

<cache name="testCache" maxElementsInMemory="1000"overflowToDisk="true" eternal="true" memoryStoreEvictionPolicy="LFU"/>
On ajoute les annotations suivantes dans le source Java afin de les mettre en cache après le premier appel :
@CacheFlush(modelId = "test")

public String saveObject(Object objet) {…}

 

@Cacheable(modelId = "test")

public User findObject(String idObject) {…}

Caption Panel GWT

Les contraintes ergonomiques des IHM d’applications professionnelles sont bien plus souples que celles qui régissent la conception de portails de ventes en ligne.
L’offre Viaxeo proposant un moteur de génération de sites e-business intégrés in extenso dans son CRM multicanal, nous avons du développer les outils nous permettant de répondre efficacement aux canons graphiques actuels afin de nous conformer aux chartes graphiques de nos clients.

Le premier obstacle rencontré était inhérent à la physionomie rudimentaire de nos panels de bases. Point de formes géographiques fantaisistes ni d’habillage recherché sans passer par l’utilisation manuelle, au cas par cas, de groupes de widgets englobant et de feuilles de style.

GWT propose de palier à ces difficultés via le DecoratorPanel, un widget à 9 « conteneurs », 8 panels périphériques destinés à accueillir images et styles, et un SimplePanel central dévolu à l’ajout de contenu.
En voici un exemple d’utilisation. Le but initial étant de créer un Caption Panel à bords arrondis, dont le style du titre soit paramétrable.

public class CaptionPanel extends Composite {private VerticalPanel absolutePanel;
private DecoratorPanel titlePanel;
private DecoratorPanel bodyPanel;
private VerticalPanel verticalPanel;
private HorizontalPanel horizontalPanel;
public CaptionPanel(int width, int height, int titleWidth, int titleHeight, String title){
this(width, height, null, titleWidth, titleHeight, title, null);
}

public CaptionPanel(int width, int height, String style, int titleWidth, int titleHeight, String title, String titleStyle){
absolutePanel = new VerticalPanel();
titlePanel = new DecoratorPanel();
bodyPanel = new DecoratorPanel();
verticalPanel = new VerticalPanel();
horizontalPanel = new HorizontalPanel();
horizontalPanel.
setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
absolutePanel.
setSize(width+"px", (height + titleHeight)+"px");
if (titleStyle != null){
horizontalPanel.setStyleName(titleStyle);
titlePanel.setStyleName(titleStyle);
}
if (style != null){
bodyPanel.setStyleName(style);
}

titlePanel.setSize(titleWidth+"px", titleHeight+"px");
titlePanel.add(new Label(title));
bodyPanel.setSize(width+"px", (height-titleHeight/2)+"px");
bodyPanel.add(verticalPanel);
horizontalPanel.add(titlePanel);
absolutePanel.add(horizontalPanel);
absolutePanel.add(bodyPanel);
initWidget(absolutePanel);
}

public void addTitleStyle(String style){
horizontalPanel.setStyleName(style);
titlePanel.addStyleName(style);
}

public void addBodyStyle(String style){
bodyPanel.addStyleName(style);
}

public void addContent(Widget content){
verticalPanel.add(content);
}

public void clearContent(){
verticalPanel.clear();
}

Voici quelques exemples de rendu :

Caption Panel

Loading...
X