RSocket, le protocole réactif

rsocket logo

RSocket (pour Reactive Socket) est un nouveau protocole de communication. Il spécifie des façons d’échanger des messages au format binaire entre applications. C’est un protocole de niveau applicatif qui permet des communications correspondant aux besoins modernes : push de données, échanges bi-directionnels, reprise de connexion, asynchronisme…

Il est conçu pour être utilisé autant pour de la communication de serveur à serveur, que serveur à périphérique (smartphone, navigateur web etc.).

Le protocole est open-source. Créé au départ par Netflix, il est désormais supporté par Facebook, Pivotal et Netifi. Il doit être intégré prochainement dans le framework Spring (cf issue). La spécification du protocole est actuellement en version 0.2 mais la release est proche, cette version étant considérée comme une release candidate 1.0. Rentrons un peu dans le détail !

 Les points clé de RSocket

Reactive streams – le contrôle des flux

La conception de RSocket s’appuie sur le manifeste réactif et la spécification Reactive Streams. Il s’agit d’implémenter des systèmes asynchrones et non bloquants, mais pas uniquement.

Un des apports fondamentaux apporté par le dogme réactif est la backpressure. Ce paradigme permet à un consommateur de données dans un système, d’informer les autres applications qu’il est surchargé. Les producteurs de cette donnée doivent assimiler cela et ne pas surcharger le flux. Le consommateur peut également choisir de consommer les données au rythme qu’il veut. Le but est de produire un sytème résilient sans devoir implémenter des mécanismes complexes de type circuit breaker.

RSocket introduit ces éléments dans sa spécification, le rendant indiqué pour implémenter des applications réactives.

Autres caractéristiques

  • RSocket est indépendant du transport sous-jacent. Actuellement il peut fonctionner avec TCP, WebSockets, Aeron, ou HTTP/2. Typiquement on choisira TCP pour des échanges de serveur à serveur, et Websocket pour navigateur à serveur.
  • Reprise de connexion. Si une connexion est coupée entre les 2 participants, cela peut être problématique pour les communications de type « longue durée » (abonnement à un flux de données). Le protocole fournit les moyens de reprendre la discussion au même endroit dans une nouvelle connexion, grâce à une notion de position implicite.
  • Rsocket est un protocole de haut niveau. Le but recherché par les créateurs est de fournir des impémentations directement utilisables au sein des applications. Ces librairies sont disponibles dans différents langages de programmation.
  • Les échanges sont au format binaire, afin de maximiser les performances et d’optimiser les resources. Cela peut rendre plus difficile le debug des messages. Cependant, c’est totalement cohérent quand on pense que l’immense majorité des échanges se font entre 2 machines et ne sont pas lus par un humain. Les applications devront donc implémenter la sérialisation et désérialisation de leur format natif vers du binaire.

 Modes d’interaction

La base du protocole tient dans les différents modes d’interaction proposés. RSocket fournit 4 modes distincts :

  1. Fire-and-Forget : requête unique, pas de réponse envoyée par le serveur
  2. Request/Response : « HTTP-like » : 1 requête, 1 réponse.
  3. Request/Stream : requête simple, réponse multiple. Le serveur renvoie une collection (stream) en réponse. Ce n’est pas une liste figée mais bien un flux qui arrive au fil de l’eau.
  4. Channel : échanges bi-directionnels. Les 2 participants s’envoient des flux de messages l’un à l’autre.

Ces modes d’interaction ont été pensés pour répondre aux besoins actuels des applications. Ainsi, le push de données est supporté par le mode request/stream. Cela permet par exemple, de gérer un flux d’informations à recevoir continuellement, sans avoir besoin de requêter plusieurs fois le serveur. Le mode fire-and-forget, avec sa requête unique sans réponse, permet d’optimiser dans des cas où la réponse peut être ignorée. Le mode channel implémente un dialogue complet entre deux composants.

Ces différents modes ainsi que les points clés listés ci-dessus sont le socle de RSocket.

 Les implémentations

A ce jour le protocole a des implémentations en Java, Javascript, C++ et Kotlin. Voyons un peu comment cela marche en Java dans la section suivante.

Exemples en java

L’implémentation en Java est basée sur la librairie Reactor. Au niveau du transport nous allons utiliser ici le transport TCP via le framework Netty. Les 2 dépendances suivantes sont suffisantes pour commencer à implémenter RSocket dans une application : io.rsocket:rsocket-core et io.rsocket:rsocket-transport-netty.

Démarrons un serveur en local :

RSocketFactory.receive()
    .acceptor((setup, socket) -> Mono.just(new AbstractRSocket() {})) // ne fait rien
    .transport(TcpServerTransport.create("localhost", 7000))
    .start()
    .subscribe();

Ce serveur ne va rien faire car on n’a pas spécifié de comportement concret sur la méthode acceptor. Il faut fournir une implémentation des interfaces SocketAcceptor et RSocket afin de déterminer ce que fait le serveur quand il reçoit un message. Il est intéressant de regarder l’interface RSocket pour constater qu’elle demande l’implémentation des 4 modes d’interaction évoqués plus haut :

public interface RSocket extends Availability, Closeable {
  // [...]
  Mono<Void> fireAndForget(Payload payload);
  Mono<Payload> requestResponse(Payload payload);
  Flux<Payload> requestStream(Payload payload);
  Flux<Payload> requestChannel(Publisher<Payload> payloads);
  // [...]
}

Prenons l’exemple d’un service de streaming de « news ». Lorsque le serveur reçoit une requête d’un client, il va envoyer un flux continu d’actualités qui se met à jour sans nouvelle requête. Nous allons devoir implémenter la méthode requestStream pour gérer cette interaction. La classe Payload est la classe qui représente un message binaire qui transite sur la connexion ; il faut donc effectuer les transformations entre les objets métier et ce type. Voici donc à quoi peut ressembler une implémentation du flux côté serveur :

SocketAcceptor socketAcceptor = (setup, sendingSocket) -> {
    return Mono.just(new AbstractRSocket() {
        @Override
        public Flux<Payload> requestStream(Payload payload) {
            return newsProducer.streamNews(payload) // service métier qui fournit le flux de données en fonction de la requête
                    .map(NewsMapper::toByte)        // sérialisation de l'objet métier
                    .map(DefaultPayload::create)    // creation du payload (methode fournie par l'implémentation rsocket-java)
                    ;
        }
    });
};

Sur le même exemple, créons la socket et utilisons là pour que le client puisse interroger le serveur et récupérer les news :

RSocket clientSocket = RSocketFactory.connect()
        .transport(TcpClientTransport.create("localhost", 7000))
        .start()
        .block();
        
clientSocket
    .requestStream(DefaultPayload.create("Donne moi les news s'il te plait"))
    .map(Payload::getData)            
    .map(NewsMapper::fromByteBuffer)  // désérialisation du message vers l'objet métier
    .doOnNext(newsConsumer::readNews) // appel du service métier de lecture des news reçues
    .doFinally(signalType -> clientSocket.dispose())
    .then()
    .block();

Ces quelques exemples démontrent que l’on peut utiliser RSocket dans une application Java très simplement. Cela nécessite en amont l’adoption de la programmation réactive.

Netifi proteus

Proteus est une plateforme basée sur RSocket. Elle fournit un broker auquel les applications vont se connecter, le broker se chargeant des échanges entre les applications. Il gère le routage entre les services, la sécurité, le load balancing. Une console web permet de visualiser et d’administrer la plateforme. Comme souvent, on dispose d’une version communautaire open-source avec les fonctionnalités de base, et la version enterprise contient des fonctionnalités avancées (connecteurs, métriques, alerting, …)

Les échanges sont encodés à l’aide de Protobuf. Ceci permet de spécifier les contrat d’échanges : mode d’interaction, types d’entrée/sortie, etc. Les interfaces client/serveur sont ensuite générées, et il suffit de les implémenter pour écrire notre logique métier. Proteus se charge de la sérialisation des objets et de la communication via RSocket.

Afin d’illustrer Proteus, voici quelques copies d’écran de la console web.

Gestion des brokers :
console web proteus

Statut des services connectés :
console web proteus

L’outil est intéressant mais semble encore un peu limité. Il est par exemple impossible d’envoyer un message à partir de la console, ce qui serait très pratique en développement. Je l’ai trouvé également assez lourd à démarrer via docker, sachant que c’est uniquement un « passe-plat » et qu’il ne stocke pas les messages. Les librairies sont pour l’instant disponibles pour Java, Javascript et Spring Framework mais d’autres devraient arriver prochainement. A suivre donc !

Pour conclure

Dans des systèmes de plus en plus distribués et découplés, les échanges de message asynchrones deviennent un standard de communication entre les applications. RSocket s’inscrit dans cette logique mais se démarque en apportant les principes réactifs au niveau du protocole de communication.

Supporté par des grandes entreprises du numérique, son adoption en sera peut-être facilitée. A suivre…

Découverte du framework Micronaut

Micronaut logo

 Un nouveau framework de développement d’applications JVM

Que dit le pitch ? « A modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications. » Hé oui, rien que ça. A priori ça sert donc à développer des applications et les exécuter sur une JVM, et que c’est orienté sur les microservices et le serverless. La version 1.0 a été releasée en Octobre dernier. C’est l’occasion de faire un essai !

 La genèse de Micronaut

Le constat fait par l’équipe de développement est que depuis quelques années, énormément de choses ont changé dans le développement des applications. Aujourd’hui, on fait des micro-services, sous forme de containers ; on veut pouvoir scaler facilement, exposer des API consommées par plusieurs types de périphériques, avec un niveau de performance élevé. Les anciens frameworks JVM tels que Spring ou Grails n’ont pas été construits avec ces problématiques.
Le but affiché par les créateurs est de créer un framework de développement léger, rapide à démarrer, peu consommateur de mémoire et modulaire ! Clairement, on veut concurrencer Spring Boot en proposant une expérience de développement similaire, mais en s’affranchissant de la lourdeur de ce dernier. L’équipe de développement est la même que celle derrière le framework Grails.

 Comment ça marche – Runtime vs Compile time

Un framework tel que Spring est apprécié par beaucoup de développeurs pour la productivité qu’il apporte, même si la courbe d’apprentissage est raide. Une fois pris en main, il est simple d’exposer et sécuriser une API, d’accéder à de multiples bases de données, d’émettre ou consommer des messages, etc. Le souci est que pour permettre tout cela, il fait énormément de choses au démarrage de l’application : scan du code, injection de dépendances, activation de profils… tout cela est fait au runtime. Cela a pour conséquence que plus un projet Spring est important, plus le temps de démarrage et la consommation mémoire sont élevés.

Micronaut prend le parti de tout faire à la compilation : il n’utilise pas la réflexion (introspection dynamique des classes). Il utilise la compilation ahead-of-time (AOT) afin de gérer tout cela lors du build. Cependant, le framework propose le même genre de facilités aux développeurs que Spring et permet donc une grande productivité. Il promet donc le meilleur des deux mondes !

On va développer quoi avec Micronaut ?

On peut l’utiliser pour construire des API REST, clients d’API, services de traitement de données, messaging, … Micronaut propose un modèle asynchrone et non bloquant et le rend donc indiqué pour développer des applications réactives. Pour cela la couche réseau est basée sur le serveur Netty qui apporte la gestion de l’event loop. C’est le serveur utilisé également par les frameworks Vert.x et Spring Webflux par exemple (version réactive de Spring MVC). Le framework supporte les langages Java, Kotlin et Groovy. Voici quelques fonctionnalités en vrac :

  • service discovery (Consul, Eureka, …)
  • gestion des paramètres de configuration externe (Consul, Amazon ParameterStore)
  • support du serverless (AWS Lambda, OpenFaas)
  • Interaction avec les bases mongoDB, Neo4j, Postgres, Cassandra, Redis…
  • Support d’Apache Kafka
  • Mécaniques intégrées de retry et circuit breaker

 Allez, jouons avec !

Micronaut s’installe très facilement grâce à SDKMAN !

$ sdk install micronaut

 Pour démarrer, le CLI

Micronaut met à disposition un CLI (Command-line interface) efficace. Pour démarrer un squelette d’application :

$ mn create-app com.mycompany.mygreatservice

Cela créée une structure, une classe de démarrage, un Dockerfile, la configuration des dépendances… Le langage est Java par défaut et les dépendances gérées par Gradle, mais il est possible d’utiliser maven avec l’option -b maven. Il y a un certain nombre de features qui permettent de pré-configurer une fonctionnalité en ajoutant les dépendances et le squelette de configuration nécessaire, par exemple :

$ mn create-app com.mycompany.mygreatservice --features discovery-consul, mongo-reactive

Malheureusement il n’est pas possible d’ajouter une feature une fois que le projet a été créé. Création d’un controller :

$ cd mygreatservice/ 
$ mn create-controller com.mycompany.cars 

Une classe com.mycompany.CarsController est créée avec une route /cars par défaut, ainsi que la classe de tests associée. Du coup, on peut tester :

$ ./gradlew run 
$ curl -i http://localhost:8080/cars

HTTP/1.1 200 OK
Date: Wed, 2 Jan 2019 23:50:42 GMT
connection: keep-alive
transfer-encoding: chunked

Regardons un peu le code généré ! Voici la classe de démarrage de l’application :

public class Application {

    public static void main(String[] args) {
        Micronaut.run(Application.class);
    }
    
}

Et le controller :

@Controller("/cars") 
public class CarsController {

    @Get("/")
    public HttpStatus index() {
        return HttpStatus.OK;
    }
    
}

On remarque déjà des similitudes avec le modèle de développement Spring. Il est à noter que le framework fait des choix modernes par défaut : par exemple, les routes consomment et produisent du JSON par défaut, pas besoin de préciser le content-type dans ce cas.

 Adoption du modèle réactif

Nous avons évoqué le fait que Micronaut utilise Netty pour la couche réseau. Pour garantir l’exécution en mode non bloquant, il faut utiliser les types fournis par une librairie implémentant la spécification Reactive Streams, comme par exemple Reactor ou RxJava.

Prenons l’exemple d’un endpoint classique de sauvegarde. On va écrire quelque chose comme :

@Post 
public HttpResponse<Customer> save(@Body Customer customer) { 
    return service.insertInDatabase(customer); 
}

Dans ce cas, Micronaut utilise un thread pool classique. L’exécution est bloquante : le code du service n’est exécuté que lorsque l’objet est reçu en totalité. La réponse n’est envoyée que lorsque le service a fini son exécution. Entre ces étapes, le thread courant est bloqué, en attente. Si on a une latence réseau importante les resources du serveur ne sont pas exploitées au mieux. Voici le même exemple en utilisant RxJava :

@Post 
public Single<Httpresponse <Customer>> save(@Body Single<Customer> customer) {
    return customer.map(c -> { 
       service.insertInDatabase(c); 
       return HttpResponse.created(c); 
     }); 
}

Ici la requête est non bloquante et le modèle event-loop de Netty est utilisé. Entre les différentes étapes, le thread est capable d’exécuter d’autres requêtes plutôt que d’être bloqué.

Si on veut être réactif de bout en bout, il est préférable d’utiliser également une librairie réactive quand il s’agit d’accéder aux données. Ce n’est pas toujours possible. JDBC est une API bloquante par exemple. Dans ce cas l’exécution des accès à la base de données sera basculée dans le thread pool « bloquant » et on reviendra en mode non bloquant ensuite.

 Accès aux données avec mongoDB réactif

Du coup on opte pour le driver réactif pour MongoDB. Pour l’activer, on ajoute la dépendance dans le fichier de dépendances, ici dans build.gradle :

compile "io.micronaut.configuration:micronaut-mongo-reactive"

Et il faut déclarer le chemin vers la base dans le fichier application.yml

mongodb:
  uri: mongodb://localhost:27017

Avec cette configuration, on dispose d’un objet MongoClient qui peut être utilisé pour interagir avec la base de données. Cet objet peut être injecté à la manière d’un bean Spring. On peut ensuite requêter dans la base de données de manière réactive :

private MongoCollection<Customer> getMongoCollection() {
    return mongoClient
            .getDatabase("my-great-database")
            .getCollection("myGreatCollection", Customer.class);
}

public Maybe<Customer> getByLogin(String login) {
    return Flowable.fromPublisher(
            getMongoCollection().find(Filters.eq("login", login)).limit(1))
                .firstElement();
}

Service discovery avec Consul

Dans un environnement micro-services il est très utile de pouvoir faire du service discovery : chaque service va s’enregistrer au sein de l’éco-système, ce qui permettra aux autres services de l’appeler sans connaître son adresse réelle. Consul permet de faire cela et son intégration est facile dans Micronaut. Il faut ici encore ajouter une dépendance dans le build.gradle :

compile "io.micronaut:micronaut-discovery-client"

Puis on ajoute quelques paramètres dans le fichier application.yml pour activer l’enregistrement du service.

consul:
  client:
    registration:
      enabled: true
    defaultZone: "localhost:8500"

On constate l’enregistrement automatique du service dans consul lorsqu’il est démarré : consul services

Il est alors possible de simplifier grandement l’appel à un service qui expose une API REST. Si on veut appeler un tel service dans une autre application, il suffit de déclarer une interface annotée @Client avec l’identifiant du service, et les méthodes correspondant aux endpoints de l’API.

@Client("customer")
public interface CustomerServiceClient {

    @Get("/customers/{login}")
    public Single<Customer> getByLogin(String login);
    
}

On peut ensuite appeler directement ce client dans notre code.

public class MySecondService {

    private final CustomerServiceClient customerClient;
    
    public MySecondService(CustomerServiceClient customerClient) {
        this.customerClient = customerClient;
    }

    public void callMyCustomerService(String login) {
        customerClient.getByLogin(login)
                .map(customer -> {
                    // do what you want here
                });
    }
}

 Et donc, les promesses de performance et de légéreté ?

Prenons un exemple d’application avec les features ci-dessus actives et développées : consul service discovery et MongoDB. Voir la taille du fat jar compilé :

$ du -h ./customer/build/libs/customer-0.1-all.jar 
14M ./customer/build/libs/customer-0.1-all.jar

L’exécutable fait une taille plutôt raisonnable de 14 Mo. Démarrons l’application pour constater le temps de démarrage :

$ java -jar ./customer/build/libs/customer-0.1-all.jar
12:02:55.340 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 1169ms. Server Running: http://localhost:8080
12:02:55.446 [nioEventLoopGroup-1-3] INFO  i.m.d.registration.AutoRegistration - Registered service [customer] with Consul

J’ai constaté un temps similaire sur plusieurs essais. Bien sûr ça n’a rien d’un benchmark dans les règles de l’art mais c’est plutôt encourageant.

Concernant l’empreinte mémoire, j’ai pu effectuer quelques tests. Sur une application de type serveur HTTP avec un seul endpoint, j’ai constaté une consommation mémoire d’environ 100Mo. Il est notable de constater que l’application fonctionne si on la lance avec une taille maximale de heap très petite (10Mo). Malheureusement c’est trop peu significatif sur une petite application et je n’ai pas eu d’effet « waouh ». Pour tirer des conclusions pertinentes et constater la plus-value réelle de Micronaut, il faudrait faire un benchmark complet sur une application plus proche d’une application de production, et comparer avec d’autres frameworks. Peut-être un sujet pour un prochain article 😉

Vous trouverez un exemple complet avec 2 services sur mon github !

 En conclusion

Micronaut est un nouveau framework JVM qui est vraiment prometteur. Il est simple à utiliser, rapide, la documentation est claire et concise. Le projet est très actif au niveau des contributions.

L’enjeu pour l’équipe de développement sera de garder le côté « micro » tout en augmentant le périmètre des fonctionnalités. Je pense qu’il peut se positionner comme une alternative crédible sur ce marché. En tout cas, je vais surveiller son évolution avec intérêt !

ADEO Dev Summit

Fin juin avait lieu Adeo Dev Summit, événement autour du développement organisé au sein du groupe Adeo. L’occasion de rassembler les personnes de l’IT des différentes entités du groupe au niveau mondial (France, Russie, Brésil, Grèce…) 4 jours durant lesquels se sont succédés conférences, codelabs et keynotes dédiés aux techniciens : on a parlé frameworks, architecture, déploiement, méthodologies, open/inner source…
On ne va pas faire ici un résumé complet, mais on a assisté à des talks de qualité que ce soit par des speakers « internes » Adeo ou du beau monde venu de l’extérieur (booking.com, github, google, confluent/kafka, traefik, …)., ainsi que des ateliers pour tous les goûts.
La keynote de fermeture nous a permis d’écouter Dirk-Willem van Gulik, un des fondateurs et ancien président de la fondation Apache, sans qui notre métier serait probablement assez différent aujourd’hui. Un grand moment.

De mon côté j’ai suivi un atelier sur le langage Go, vu une première approche pratique du serverless / Function-as-a-service, découvert les outils Skaffold et Bazel pour gérer le déploiement de briques hétérogènes dans Kubernetes, …
J’ai également participé activement : j’ai donné un talk sur les micro-services appliqués à la vraie vie, et j’ai animé des codelabs sur la programmation réactive dans Spring et les microservices en java avec RabbitMq et Spring cloud stream.
Le tout dans la langue de Shakespeare…
Cela a été une super expérience à tout point de vue. L’événement en lui-même, organisé en très peu de temps, a été une énorme réussite que ce soit au niveau de l’organisation, la qualité des intervenants, et la diversité des thèmes abordés.

Plus généralement cela m’a inspiré quelques réflexions sur le passé et le présent de notre métier…
Nous avons depuis longtemps la conviction

  • Que le développeur a quelque chose de différenciant, une plus-value à apporter dans les produits auxquels il participe.
  • Que l’on ne peut pas remplacer sans réfléchir un développeur A par un développeur B.
  • Que l’on devrait parler de personnes et non de ressources.
  • Que travailler main dans la main avec son client et son utilisateur est bien plus rentable sur le long terme que d’envoyer le développement se faire dans un centre de service à l’autre bout du monde.

La bonne nouvelle, c’est qu’aujourd’hui cette conviction est non seulement partagée, mais est partie intégrante de la culture des services informatiques des entreprises (que l’on évoque des entreprises très centrées sur leur IT ou non).
Et ce genre d’événement le démontre !
L’approche DevOps, les méthodologies Agile, le software craftsmanship, sont autant d’éléments qui rappellent que le développeur a aujourd’hui un rôle fondamental à jouer.

Il y a 10 ou 15 ans, le terme même de « développement » avait une certaine connotation négative.
Dans beaucoup d’esprits, c’était plutôt pour les juniors !
Dans cette vision, la voie normale de carrière était : obtention du diplôme > développeur (pas trop longtemps) > chef de projet > manager.
Bien sûr c’est un peu caricatural mais reflète une certaine réalité de l’époque.
Il semble que ce temps soit révolu.
Aujourd’hui le développeur ne doit pas uniquement coder :

  • Il participe aux réflexions avec les utilisateurs,
  • Il est responsable des tests, du déploiement sur les environnements distants, de l’intégration continue, du run.
  • Il explique régulièrement ce que fait le produit.
  • Il prouve que ça fonctionne.
  • Il partage ses pratiques avec les autres équipes de développement.

Bref, le technicien a gagné ses lettres de noblesse. A eux, à nous, de continuer à faire avancer les choses et les mentalités.
Le changement de mindset a opéré, enfin, le train est en marche.

En conclusion, un grand bravo à Adeo pour cette initiative ! Ce n’est que le début !

Alexandre Vandekerkhove

PS : Merci à Florian Petit pour la photo !

On était au Devfest 2018 – IMT Lille Douai

Le 21 Juin 2018 avait lieu le devfest 2018 à l’IMT Lille Douai. Il s’agit d’une journée de conférences, codelabs autour de sujets technologiques innovants.

Tout démarre par une keynote d’Alexandra Nemery & Sarah Colmon. Elles ont su captiver l’auditoire avec un petit quizz interactif sur le thème des jeux vidéo sur kahoot.it. Elles ont ensuite tenté de faire le lien entre le monde de l’UX et celui des jeux vidéo, en prenant quelques exemples de jeux dont l’ergonomie n’est pas adaptée soit au device, soit aux habitudes des utilisateurs (joueurs). Et si Shigeru Miyamoto, célèbre créateur de Mario et Zelda, était le premier vrai visionnaire de l’UX ?

On a ensuite assisté à une introduction de gRPC par Sébastien FRIESS. Ce framework s’appuie sur le protocole HTTP/2 et utilise Protocol Buffers pour permettre d’échanger des données entre briques applicatives de façon performante. Sébastien nous a présenté le schéma de description des messages (IDL), puis une démo client/serveur. Il a apporté des modifications aux schémas de données à chaud, côté serveur puis côté client, pour démontrer que les échanges n’étaient pas impactés.

Sébastien Pertus nous a présenté les modules dans EcmaScript 6 et nous a également parler de TypeScript. Il a d’abord fait un historique et un focus sur Node.js puis nous a présenté sous forme de démos l’utilisation des modules dans les navigateurs.

Alexis Hassler nous a fait une revue de HTTP/2 et de son support dans les navigateurs, frameworks web, serveurs d’applications… La démo d’Alexis nous a permis de constater l’efficacité du multiplexage (utilisation de la même connexion tcp) de HTTP/2 sur le chargement de plusieurs éléments d’une page web. Il nous a également parler de la compression des headers http et du « server push » qui permet au serveur de pousser des ressources avant même qu’elles soient demandées par le client. On a pu apprendre que le support d’HTTP/2 est assez hétérogène et que l’utilisation du « server push » n’est pas forcément très simple pour le moment et nécessite de vérifier toutes les briques (reverse proxy, …) séparant le client du serveur.

On a vu différentes méthodes pour protéger ses API avec Léo Unbekandt. Les « API tokens » sont parfaits pour débuter rapidement et sont simples mais ne sont pas idéaux dans un écosystème distribué. Oauth 2 permet d’utiliser un vrai standard partagé mais implique beaucoup de complexité et d’appels client/serveur, cela reste néanmoins une excellente méthode pour faire de la délégation d’identité. Les tokens JWT peuvent être une bonne alternative dans le sens où le token peut être directement validé par le serveur sans appel supplémentaire, les jetons étant signés avec une clé privée connue du serveur.

Christophe Furmaniak & Yoan Rousseau, nous ont parlé de monitoring et d’alerting dans des environnements conteneurisés. Ils ont présenté l’outil de monitoring Prometheus dont le principe est de collecter les métriques en mode PULL dont l’un des principaux avantages est que les applications n’ont pas connaissance de l’infrastructure de monitoring, ce qui simplifie la configuration. Les applications ont tout de même la possibilité de venir Pusher des métriques via un composant intermédiaire que Prometheus utilisera pour collecter la donnée. Il nous ont également montré l’utilisation de Grafana qui permet la visualisation et la mise en forme des métriques collectées par Prometheus. Enfin, la problématique de mise en cluster de Prometheus a été rapidement abordée, le projet Thanos a été mentionné pour répondre à ce besoin. Nous pouvons conclure que Prometheus est adapté pour le monitoring mais n’est pas fait pour stocker des logs ou des événements, il n’est pas fait non plus pour tracer les requêtes dans une architecture microservices où il faudra utiliser des outils comme OpenTracing / Zipkin.

Le composant Istio nous a été présenté par David Gageot. Istio permet d’appliquer le pattern « façade » (ou sidecar) à son architecture micro-services au sein d’un environnement Kubernetes : un proxy HTTP Envoy est adossé à chaque micro-service, ce qui permet d’ajouter des traces, de monitorer, sans rien modifier dans son code ou son déploiement. Istio permet également de faire du TLS automatiquement entre les services, de router le traffic plus finement… David nous a fait une démo de canary deployment où un fix a été déployé pour un seul utilisateur en fonction d’une entête HTTP. Puis il a effectué un blue/green deployment, avec un routage d’une partie des requêtes vers la nouvelle version de l’application et une bascule progressive. Malgré l’effet démo subi par le speaker, cette conférence était très intéressante et Istio est vraiment prometteur.

Aurélien Loyer et Nathan Damie nous ont parlé de Javascript et des frameworks Javascript web. On a eu droit à une petite séance de pair-programming en live sur les bindings en Javascript via le « framework » Vanilla 🙂 Le message de fond est de démarrer simplement avec du pur Javascript, bien comprendre et spécifier son (éventuelle) problématique, et ensuite choisir (ou non) un framework qui colle rééllement à cette problématique.

Au niveau de l’organisation, c’était excellent, très bien organisé et fluide. Il y avait toujours de la place à condition d’arriver à l’heure dans l’amphi (quelques uns ont fini sur les marches, des souvenirs de début d’année en fac..) C’est également l’occasion de recroiser de nombreuses têtes rencontrées au détour de missions dans la région 🙂 Les sponsors proposaient de nombreuses animations, baby foot, bornes d’arcade, etc. On regrettera juste des soucis récurrents avec le projecteur de l’amphithéâtre principal qui a occasionné quelques coupures et slides tronqués. Globalement les présentations étaient de qualité avec des speakers au niveau.

On reviendra avec plaisir l’année prochaine !

Alexandre Vandekerkhove et Maxime Decronambourg

Tests automatisés d’api avec Karate

Dans le cadre de nos projets, nous sommes régulièrement amenés à définir et à développer des API et des services REST.

Nous avons à plusieurs reprises fait le choix d’utiliser Karate pour réaliser des scénarios de tests automatisés sur ce type de projet.

Karate est idéal pour mettre au point rapidement une série de tests représentant des enchaînements d’appels de services REST, de plus il s’intègre parfaitement avec l’ outil d’intégration continue Jenkins.

Nous allons voir comment écrire et organiser des scénarios de tests avec Karate, ainsi que son intégration à Jenkins.

Posons le contexte

Pour illustrer la réalisation de tests automatisés avec Karate, prenons l’exemple d’une api permettant à un client d’acheter des produits et de payer via une de ses cartes de paiements.

Notre api permet donc à nos clients:

1 – de créer un panier

POST /carts

2 – d’ajouter des produits dans le panier

POST /carts/{{cartId}}/items
{
  "itemId": '#(itemId)'
}

3 – de modifier la quantité des produits du panier

PUT /carts/{{id}}/items/{{itemId}}
{
  "quantity": '#(quantity)'
}

4 – de consulter son panier

GET /carts/{{cartId}}

5 – de récupérer ses moyens de paiements

GET /carts/{{cartId}}/wallet

6 – de payer le panier

POST /carts/{{cartId}}/payment?cardId={{cardId}}

Réalisons les scénarios réutilisables

Pensons réutilisabilité et commençons par créer 6 fichiers feature contenant chacun un scénario correspondant à une requête vers notre api.

Pour chaque requête, nous enregistrons le code de retour http et la réponse afin de pouvoir y accéder ultérieurement.

1 – Créer un panier

Fichier: createCart.feature

Feature: API - createCart

  Scenario: create a cart
    When url BASE_URL + '/carts'
    And request {}
    And method post 
    * def statusCode = responseStatus
    * def body = $

Ce scénario effectue une requête http POST sur /carts

2 – Ajouter des produits dans le panier

Fichier: addItemToCart.feature

Feature: API - addItemToCart

  Scenario: add an item to a cart
    When url BASE_URL + '/carts/' + cartId + '/items'
    And request
    """
    {
      "itemId": '#(itemId)'
    }
    """
    And method post
    * def statusCode = responseStatus
    * def body = $

Ce scénario effectue une requête http POST sur /carts/{{cartId}}/items

Il a deux paramètres:

  • cartId: identifiant du panier
  • itemId: identifiant du produit à ajouter au panier

3 – Modifier la quantité des produits du panier

Fichier: updateCartQuantity.feature

Feature: API - updateCartQuantity

  Scenario: update cart item quantity
    When url BASE_URL + '/carts/' + cartId + '/items/' + itemId
    And request
    """
    {
      "quantity": '#(quantity)'
    }
    """
    And method put
    * def statusCode = responseStatus
    * def body = $

Ce scénario effectue une requête http PUT sur /carts/{{id}}/items/{{itemId}}

Il a trois paramètres:

  • cartId: identifiant du panier
  • itemId: identifiant du produit à mettre à jour
  • quantity: nouvelle quantité pour le produit à mettre à jour

4 – Consulter son panier

Fichier: retrieveCart.feature

Feature: API - retrieveCart

  Scenario: retrieve a cart
    When url BASE_URL + '/carts/' + cartId
    And request {}
    And method get
    * def statusCode = responseStatus
    * def body = $

Ce scénario effectue une requête http GET sur /carts/{{cartId}}

Il a un paramètre

  • cartId: identifiant du panier

5 – Récupérer ses moyens de paiements

Fichier: getWallet.feature

Feature: API - getWallet

  Scenario: get wallet
    When url BASE_URL + '/carts/' + cartId + '/wallet'
    And request {}
    And method get
    * def statusCode = responseStatus
    * def body = $

Ce scénario effectue une requête http GET sur la ressource /carts/{{cartId}}/wallet

Il a un paramètre

  • cartId: identifiant du panier

6 – Payer le panier

Fichier: postPayment.feature

Feature: API - postPayment

  Scenario: Pay a cart
    When url BASE_URL + '/carts/' + cartId + '/payment'
    And param cardId = cardId
    And request {}
    And method post
    * def statusCode = responseStatus
    * def body = $

Ce scénario effectue une requête http POST sur /carts/{{cartId}}/payment

Il a deux paramètres

  • cartId: identifiant du panier
  • cardId: identifiant de la carte de paiement

Réalisons les scénarios de tests

Commençons par créer un nouveau fichier « feature » avec un block « Background » permettant de rejouer des instructions avant chaque scénario. Ici les instructions du block « Background » correspondent à la création d’un panier. Nous utilisons l’instruction call pour réutiliser les scénarios précédemment créés. Afin que l’identifiant du panier soit visible dans tous les scénarios, nous l’enregistons dans une variable cartId.

Feature: API

  Background:
    When def createCartResult = call read(createCart)
    Then match createCartResult.statusCode == 200
    * def cartId = createCartResult.body.id

Ajoutons un scénario qui vérifie simplement la bonne création d’un panier. La création du panier est effectuée dans le block Background, il est donc inutile de répéter cette opération dans le scénario.

  Scenario: Create a cart
    Then match createCartResult.body.status == 'IN_PROGRESS'
    And match createCartResult.body.items == []

Ajoutons un scénario qui teste la consultation d’un panier. Un nouveau panier est créé avant l’exécution du scénario grâce aux instructions du block Background

  Scenario: Retrieve a cart
    When def r = call read(retrieveCart)
    Then match r.statusCode == 200
    And match r.body.status == 'IN_PROGRESS'

Ajoutons un scénario qui vérifie l’ajout d’un article dans un panier.

  Scenario: add an item
    When def r = call read(addItemToCart) { itemId: 'XXX' }
    Then match r.statusCode == 200
    And match r.body.status == 'IN_PROGRESS'
    And match r.body.items[0].itemId == 'XXX'

Ajoutons un scénario qui ajoute un article au panier et vérifie la mise à jour de la quantité

  Scenario: add an item and update its quantity
    When def addItemToCartResult = call read(addItemToCart) { itemId: 'XXX' }
    Then match addItemToCartResult.statusCode == 200

    When def r = call read(updateCartQuantity) { itemId: '#(addItemToCartResult.body.items[0].itemId)', quantity: 3 }   
    Then match r.statusCode == 200
    And match r.body.status == 'IN_PROGRESS'
    And match r.body.items[0].quantity == 3

Ajoutons un dernier scénario qui ajoute un produit dans un panier, récupère une carte de paiement, utilise cette carte pour payer, vérifie l’état du panier et la création d’un ticket.

  Scenario: Pay a cart with a card
    # add an item to cart
    When def addItemToCartResult = call read(addItemToCart) { itemId: 'XXX' }
    Then match addItemToCartResult.statusCode == 200

    # retrieve a card
    When def getWalletResult = call read(getWallet)
    Then match getWalletResult.statusCode == 200
    * def cardId = getWalletResult.items[0].id

    # pay
    When def r = call read(postPayment) { cardId: '#(cardId)' }
    Then match r.statusCode == 200
    And match r.body.status == 'PAYMENT'
    And match r.body.payment == 
    """
    {
      "id": '#string',
      "creationDate": '#string',
      "status": "SUCCESS"
    """

    # check status FINISHED and ticket is available
    When def r = call read(retrieveCart)
    Then match r.statusCode == 200
    And match r.body.status == 'FINISHED'
    And match r.body.ticket == { ticketNumber: '#string', creationDate: '#string' }

Intégration avec Jenkins

Le plugin Cucumber Reports permet d’intégrer facilement nos scénarios Karate à Jenkins.

En utilisant un jenkins pipeline, il suffit d’ajouter dans son Jenkinsfile après son build:

cucumber fileIncludePattern: '**/target/surefire-reports/*.json'

Jenkins construira un reporting comme illustré ci-dessous:

karate-report1
karate-report2

Conclusion

Karate est un outil très efficace pour tester des api et des services rest.

Sa faculté à s’interfacer avec du code java et javascript est très utile pour ajouter des scripts et interagir avec d’autres outils comme Selenium Webdriver.

Enfin Karate reposant sur Cucumber, nous pouvons profiter des plugins Cucumber pour une intégration avec Jenkins.

Continuous Documentation – Exemple de mise en place

Parmi les livrables d’un projet, la documentation est celle qui assurera sa pérennité. Dans le cadre d’une application, la documentation technique est la garante des fonctionnalités disponibles, de leurs usages mais aussi de son architecture, son intégration et son déploiement. Par conséquent, la documentation est vouée à vivre au rythme de l’évolution de l’application, et trouve naturellement sa place aux côtés du code source de l’application. Ainsi, quels sont les outils les plus adaptés à la rédaction de la documentation pour la faire cohabiter avec les sources ?

La documentation au plus proche du code source

Tout d’abord, certains outils de gestion de sources – tel que GitHub et GitLab – proposent des fonctionnalités dédiées à la documentation comme un wiki intégré et l’interprétation des fichiers MarkDown et AsciiDoc, deux formats de texte pour la rédaction de documentations. Ces formats simples on l’avantage d’être de simples fichiers textes et sont par conséquent facilement exploitable dans un outil de gestion de sources, contrairement aux formats Word et LibreOffice. L’emplacement de la documentation technique est alors idéale car au plus proche de l’application : elle pourra évoluer et être versionnée en même temps que le code.

Cependant, cela ne convient pas à tous les usages : le référentiel de codes est rarement ouvert à tous en entreprise, et la consultation de la documentation via GitHub ou GitLab n’est pas toujours le format le plus adapté pour la consulter ou la partager. C’est pourquoi, des formats plus traditionnels comme le HTML ou le PDF sont souvent plus pratiques à l’usage, à la publication, éventuellement pour l’impression ou encore pour un classement dans une GED.

A l’instar de HTML/CSS, les documents MarkDown et AsciiDoc peuvent servir à la génération d’un document plus lisible et esthétique aux formats HTML ou PDF par exemple. En outre, AsciiDoctor est un outil pour convertir les documents AsciiDoc vers HTML, PDF ou encore DocBook.

asciidoctor

Générer la documentation à la construction de l’application

Intégrer la génération de la documentation avec la construction de l’application garantie la livraison d’une documentation à chaque version. Dans cette optique, le plugin asciidoctor-maven-plugin convertit les documents AsciiDoc pour les projets Maven. Les projets Gradle ne sont pas en reste et disposent d’un plugin équivalent : asciidoctor-gradle-plugin.

En ce qui concerne le plugin Maven, son utilisation est des plus simple :

  • déclarer le plugin dans votre fichier pom.xml,
  • placer votre documentation asciidoc dans src/main/asciidoc.

La documentation sera alors générée dans target/generated-docs en même temps que la construction du projet.

Exemple d’utilisation du asciidoctor-maven-plugin:
<plugin>
  <groupId>org.asciidoctor</groupId>
  <artifactId>asciidoctor-maven-plugin</artifactId>
  <version>1.5.3</version>
  <executions>
    <execution>
      <id>generate-docs-html</id> <!-- <2> -->
      <phase>prepare-package</phase>
      <goals>
        <goal>process-asciidoc</goal>
      </goals>
      <configuration>
        <backend>html</backend>
        <doctype>book</doctype>
        <attributes>
          <snippets>${snippetsDirectory}</snippets>
        </attributes>
      </configuration>
    </execution>
  </executions>
</plugin>

<1> Dépendances requises pour supporter le PDF.
<2> La première exécution du plugin génère la documentation en HTML.
<3> La seconde exécution génère la documentation en PDF.

 

Documenter les API REST

La documentation d’une API est une partie des plus fastidieuses. En effet, celle-ci décrit l’utilisation de chaque services – que ce soit la requête attendue ou la réponse qui sera émise – et les différents cas d’usage. Un travail d’autant plus pénible que les contrats de services seront amenés à évoluer au fur-et-à-mesure des versions.

Swagger et JSONDoc : solutions invasives

A cette fin, des outils tels que Swagger ou JSONDoc sont populaires. Ils agrémentent tous les deux vos API afin d’exposer votre documentation, exploitable ensuite à travers une interface Web. Séduisantes au premier abord, elles vous permettent également de tester directement vos API.

Néanmoins, ces solutions sont intrusives dans le code de votre application. D’abord, elles imposent de les embarquer dans votre application et ajoutent une surcouche à vos API même en production. De plus, leur fonctionnement implique d’insérer des annotations dédiées à la documentation au cœur même du code source, sur chaque services de l’API. Une idée séduisante initialement : avoir la documentation au plus proche du code. Malheureusement, cela tend rapidement à réduire la lisibilité du code, et limite la maintenance de la documentation uniquement aux développeurs.

Par ailleurs, ces outils ne répondent que partiellement au besoin de documentation des API REST, notamment ils ne permettent pas de décliner aisément les différents cas d’utilisation et se limitent à la seule description des services, des champs à renseigner ou des réponses. Ils n’adressent pas non plus les liens HATEOAS. Dernier mauvais point, ils ne peuvent pas être inclus dans une documentation traditionnelle comme un guide de l’utilisateur.

Spring RESTDoc : une approche via les tests

Pour ces motifs, Spring RESTDocs propose une autre approche qui combine les avantages d’une documentation écrite manuellement et de la documentation générée. Ainsi, ce framework s’introduit également dans votre application, mais uniquement dans la phase de tests : il n’introduira alors en aucun cas de désagrément à vos services en production. Il n’y sera même pas présent. Son fonctionnement reprend l’idée de s’inviter dans le code source de votre application pour être au plus près de vos API, mais contrairement à Swagger ou JSONDoc, le code de votre application n’est pas impacté. En effet, Spring RESTDocs vient enrichir vos tests unitaires !

Exemple de test unitaire incluant de la documentation:
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andDo(document("index",
                responseFields( 
                        fieldWithPath("contact.email")
                                .description("The user's email address"), 
                fieldWithPath("contact.name").description("The user's name"))));

Dorénavant, lors de la construction du projet au moment où les tests seront exécutés, Spring RESTDocs générera des fichiers AsciiDoc contenant les requêtes et les réponses simulées lors du tests ainsi que les descriptions ajoutées dans les tests. Ensuite, ces documents pourront ensuite être inclus dans la documentation que vous avez produit pour décrire un cas d’usage. De cette façon, elle est non seulement plus lisible et plus détaillé, mais également toujours en concordance avec vos tests et par conséquent avec votre code.

Un projet exemple est disponible sur GitHub.

 

QUALIFICATION DES BESOINS : Les 3B

Qualifier les besoins rapidement et efficacement est aujourd’hui un véritable facteur de réussite et de croissance pour les entreprises.

En effet, les technologies évoluant de plus en plus vite, elles permettent :

  • de mettre en œuvre de nouveaux concepts (net économie, objets connectés, digitalisation des entreprises, learning machine, etc.)
  • d’améliorer sensiblement des besoins existants (distribution omni canal, fidélisation multi support, etc.)
  • d’architecturer son SI en mode services

Les idées ne manquent pas mais si elles sont BELLES, sont-elles BONNES (et ne sont-ce pas plutôt des BUZZ) et me permettront-elles de générer du BUSINESS (les 3B) ?
Tout l’enjeu est d’avoir au plus vite les réponses pour prendre les bonnes décisions (l’éternel TIME TO MARKET).
Tout d’abord cadrons le contexte de cet article.

Celui-ci ne s’adresse pas aux créateurs d’entreprise ou de startup. Pour ceux-ci, je pourrai conseiller un ouvrage « Le Manuel du créateur de startup » de Steve Blank et Bob Dorf, qui leur permettra de répondre à leurs interrogations, si ce n’est pas déjà fait.

Il s’agit ici de s’inscrire dans le cadre d’une entreprise du type « Scale Up »  (Une scale up? Une start-up qui a déjà fait du chemin, qui a prouvé la valeur de son business model, trouvé son marché, connaît une croissance forte, a donc maintenant des directions RH, Marketing, Commercial, Financières et nourrit de sérieuses ambitions en termes de déploiement de son modèle sur son territoire ou à l’international.) ou du type « Corporate Collaborative » (Une entreprise organisée à travers de Business Units, de départements, des services, déployée sur différents sites géographiques et ayant la volonté de mettre en place des outils de collaboration).

Pour les premières, le challenge est de garder l’esprit créatif de la startup alors même que l’on ne partage plus le même bureau, voir le même site géographique.

Pour les secondes, il s’agit de profiter de l’esprit créatif de chaque collaborateur (dispersé géographiquement) en évitant l’effet « Boite à idées – Boite à coucou ».

Le cadre étant posé, prenons un exemple :

Je suis dans le département Marketing de mon entreprise dont le business est l’organisation d’événements.

Mon « Community Manager » me signale que les internautes sont de plus en plus friands de solutions de cagnottage pour le paiement de cadeaux d’anniversaire, de mariage, etc.

 

LA BELLE IDEE

N’existe-t-il pas un besoin de mise en place d’un nouveau moyen de paiement du type « Cagnotte » pour mon entreprise ?

A priori, c’est une Belle Idée, non ? Mais est-ce un besoin réellement implémentable dans le contexte de mon entreprise ?

 

UNE BONNE IDEE ?

Les questions auxquelles il faut alors, impérativement, apporter des réponses sont :

  1. Suis-je capable de la décrire ?
    • A minima : En tant que « X » Je souhaite « ….. » Afin de « …. »
    • Au mieux : Les processus métiers qu’elle crée ou modifie
  2. Y-a-il adhésion des autres responsables ?
    • Force de vente
    • Juridique
    • Finance
  3. Existe-t-il des contraintes techniques, légales, financières, etc. ?
  4. Mon entreprise est-elle prête (IT, Organisation, etc.) ?

C’est ici qu’un outil collaboratif trouve sa raison d’être à condition qu’il possède les fonctionnalités suivantes :

  1. Identification, création du besoin (En tant que, Je souhaite, Afin de)
  2. Description du besoin à travers du design de process (au mieux), de mind mapping, mockup, de documents joints
  3. Fonctionnalités de commentaires (échanges, demandes de précision, etc.) sur le besoin
  4. Mise en œuvre de Followers, Vote, Like (fonctions implémentées dans les outils de réseau sociaux)
  5. Historisation des événements ayant eu lieu sur le besoin et tableau de bord de synthèse permettant d’identifier au plus vite l’effet BUZZ (Nombre de commentaires dans le temps, Fluctuation dans le temps de Followers, Like, etc.)

Alors, cet outil existe-t-il ?

Loin de moi l’idée de faire ici une étude exhaustive des outils pouvant répondre à cette question.

Je me contenterai donc de vous citer deux outils faisant partie de mon retour d’expérience sur le sujet :

  1. JIRA – CONFLUENCE et ses PlugIn

    • Identification et création du besoin
      • Création d’un type de demande « NEED » (JIRA CORE)
      • Ajout de quelques données personnalisées (JIRA CORE) :
        • EN TANT QUE : Un champ « Liste » des départements de l’entreprise
        • JE SOUHAITE : Un champ « Texte libre »
        • AFIN DE : Un champ « Label »
      • Description du besoin
        • Mind Mapping : PlugIn « EasyMind »
        • Mockup : PlugIn « Balsamiq Mockup »
        • Process Design : PlugIn « Draw.io »
      • Echanges et précisions
        • Utilisation des fonctionnalités de « Commentaires » associés à chaque demande (JIRA CORE)
      • Contraintes
        • Ajout d’une donnée personnalisée (JIRA CORE) :
          • CONTRAINTES : Un champ « Liste à 2 niveaux » à choix multiple dont le 1er niveau est une liste de contraintes (Juridique, IT, Logistique, Concurrentielle, Conduite du changement, etc.) et le 2ème niveau est une liste de poids (Bloquante, Forte, Surmontable, etc.)
        • Adhésion
          • Utilisation des fonctionnalités de « Observateurs », « Vote » (JIRA CORE)
        • Tableau de bord
          • Utilisation des Gadgets adéquats sur les tableaux de bord (JIRA CORE)
  1. HAPPLIES

    Je vous invite à visiter le site de Happlies pour en découvrir tout le potentiel.

A ce niveau, en un minimum de temps et un maximum de collaboration, j’ai

  • partagé une idée,
  • décrit le(s) besoin(s) correspondant(s)
  • vérifié qu’elle suscitait (ou pas) un intérêt
  • qu’il n’existait pas de contraintes rédhibitoires (ou risques) associées à l’implémentation de celle-ci

Ce travail a été effectué sur notre implémentation d’un nouveau moyen de paiement « CAGNOTTAGE ».

Compte tenu des résultats obtenus (bons vous l’aurez deviné), nous considérons qu’il s’agit d’une BONNE IDEE.

D’ailleurs, n’étant pas le seul à avoir des idées (traduit en besoins), qui peuvent ne pas être toutes bonnes, il n’est pas inutile de leur donner un état et d’utiliser un workflow (présent à la fois dans JIRA et HAPPLIES) pour les « classer » et effectuer des statistiques (Taux de transformation, Durée de transformation, participant à la collaboration, etc.) afin d’améliorer mon processus de détection des bonnes idées :

3B Workflow Partie 2

 

Evidemment, comme toute entreprise « commerciale », la mienne cherche à faire des bénéfices même si elle doit passer par des investissements.

 

DU BUSINESS ?

A ce stade, il est temps de savoir si mon idée (MON BESOIN) est génératrice de profits (BUSINESS, le 3ème B).

Pour cela, j’ai l’habitude d’utiliser une déclinaison du « Business Model Canvas » d’Alexander Osterwalder (Livre « Business Model, nouvelle génération »).

Voici le canvas :

BMC

Les grands principes :

  1. Au centre
  • LE BESOIN
    • Identification
    • Le triptyque « En tant que – Je souhaite – Afin de »
    • Les fonctions à mettre en œuvre pour réaliser le besoins (Si le travail a été bien fait au niveau « BONNE IDEE ? », les échanges, les contraintes ont permis de les identifier).
      Ici, le nombre de fonctionnalités à mettre en œuvre ou à modifier est un 1er indicateur de complexité et d’effort de concrétisation
  1. En haut – A gauche
  • LES COUTS (Je me fais peur)
    • INITIAUX
      • ACHATS : Ce qu’il faut acheter pour commencer
      • ACTIVITES : Les activités de l’entreprise qu’il convient d’activer pour commencer (Souvent, il s’agit de coûts liés à des ressources d’expertise interne à l’entreprise)
    • RECURRENTS
      • Les achats et activités récurrentes (annuels) nécessaire au maintien du besoin dans le temps.

A ce niveau, je commence par identifier les postes.
2ème indicateur : Plus je mets en œuvre d’activités (surtout initialement), plus le projet sera complexe à lancer. Mon entreprise est-elle donc prête pour intégrer le besoin.

Si oui, j’essaie d’estimer les coûts associés à chaque poste.

3ème indicateur : Plus je détaille les postes et coûts, plus je limite le risque de dérapage financier.

  1. En haut – A droite
  • LES REVENUS (je me rassure)
    • OFFRES : Déclinaison en offres du besoin (Abonnement, Support, etc.) et les revenus qu’elles génèrent à l’an 0 et l’an 1.
      4ème indicateur : Tant que je n’ai pas d’offre, cela reste une bonne idée.
    • CIBLES CLIENTS : Déclinaison des cibles clients adressés (Ménagères de moins de 50 ans, Adolescents et jeunes adultes) et les revenus nouveaux ou supplémentaires que le besoin générera.
      5ème indicateur : Tant que je n’ai pas de cible, cela reste une bonne idée.
  • LES CANAUX DE DISTRIBUTION
    • Par quel canal mon besoin sera distribué (Internet, Magasin, Partenaire, Porte à Porte -sic-, etc.)
      6ème indicateur : Pas de canal identifié, cela reste une bonne idée.
  1. Au milieu – A droite
  • CHECK OFFRES – CIBLES CLIENTS
    Je vérifie que ma vision des revenus liés aux « OFFRES » est cohérente avec celle des revenus « CIBLES CLIENTS ».

J’ai souvent eu des surprises à ce niveau et cela m’a évité de me lancer dans des développements non rentables.

7ème indicateur : Inutile de continuer tant que les deux visions ne sont pas alignées.

(NB : On comprend mieux ici que ce modèle est plus adapté aux entreprises du type « Scale Up » et « Corporate » qui sont sensés connaitre leurs coûts, leurs clientèles contrairement aux Start-Up qui sont plutôt en phase de découverte).

  1. Au milieu – A gauche

Une synthèse des coûts : Initiaux & Récurrents

  1. En bas – A gauche
  • La durée de l’amortissement (c’est ici un gros abus de langage, que les DAFs ne me jettent pas la pierre), permettant de calculer les besoins de financement (ici encore, on ne m’en voudra pas pour l’approximation) par année.
  1. En bas – A droite
  • Le calcul de rentabilité sur la durée de l’amortissement.

(Remarque : Le fichier Excel à télécharger ici BMC.xlsx est une déclinaison sans prétention de cette adaptation)

A ce niveau, si le calcul est positif, l’idée (les besoins) est génératrice de BUSINESS.

Si vous êtes encore là, en train de lire et encore suffisamment attentif (et pas las), vous ne manquerez pas de me poser la question suivante « Existe-t-il un outil de ce type ? ».

Pour ma part, je n’en ai pas trouvé de ce type.

Le PlugIn « Comala » pour Confluence, permet de créer des canvas (notamment basés sur le « Business Model Canvas » d’Alexander Osterwalder).

Le site https://canvanizer.com permet créer différents types de canvas et de les partager.

Je suis preneur pour toute suggestion de votre part.

 

Enfin, quant à savoir si l’idée de « Cagnotte » est génératrice de BUSINESS : …
D’ailleurs, ce n’est pas parce qu’une idée n’est pas bonne (ou génératrice de business) à un instant T dans un contexte C, qu’elle ne peut pas le devenir.

A ce propos, j’allais oublier : Complétons notre workflow « NEED » comme ceci, toujours dans l’idée que les informations qu’il fournira, permettront d’améliorer les performances de qualification de besoin dans le futur et éventuellement de revenir sur celle-ci pour la requalifier ultérieurement en gardant la trace des travaux précédents :

EB Workflow Partie 2

En espérant que cet article vous aura été profitable.

La cohabitation PMO, GANTT et SPRINT

Peut-on faire cohabiter PMO (Project Management Office), GANTT et SPRINT ?

 

La problématique qui m’est posée est de plus en plus la suivante :

Les COMITES DE DIRECTION élaborent des ROAD MAP sur la base de BESOINS (valorisés en coûts estimés et bénéfices attendus) qui se déclinent en PROJETS (PMO) que les DIRECTEURS DE DEPARTEMENT planifient en fonction des ressources et des contraintes externes (GANTT). Ces projets sont VERSIONNES pour être DELIVRES à travers des SPRINTS (de 3 semaines à un mois).

L’article « Ending the agile/PMO tug of war » aborde la même problématique.pmo-vs-agile

La question sous-jacente est donc « Comment faire une synchronisation en temps réel ou à la demande de ses différents éléments ? ».

Prenons un cas d’école pour essayer de répondre à cette question :

Une entreprise voulait mettre à disposition de ses utilisateurs des fonctionnalités « QuickWins » a créé une « cellule AGILE » qui -comme son nom l’indique- applique les concepts de l’AGILITE.

Son objectif : Une mise en production de qualité, 1 fois par mois au pire.Agile

Cette « cellule AGILE », adepte du « devops », par ailleurs créative et efficace (Mode « Start Up »), s’est tout de suite outillée en utilisant JIRA AGILE (maintenant JIRA Software) pour la gestion de ses projets et a intégré certains de ses plugIns pour faire de l’intégration continue.

Après quelques explications aux KeyUsers, producteurs des QuickWins, sur ce qu’est une épopée, un récit, un backlog et un sprint, ce qu’ils doivent contenir à minimum, ceux-ci alimentent JIRA avec leurs récits (QuickWins) et les fameux « En tant que – Je souhaite – Afin de ».

Premier planning poker (KeyUsers, Team Leader et Team Dev), premier lancement de Sprint, c’est parti.

Collaboration, daily meeting, entre-aide, recette et ajustements, 3 semaines passent.

Bingo, 1ère mise en production. Un succès.

Les Sprints passent et se ressemblent : les objectifs sont remplis.

Entre temps, bien sûr, la « cellule AGILE » continue l’industrialisation de ses processus de production (Intégration d’un plugIn de gestion des tests, Automatisation des tests de non régression, Dockerisation, etc.) et le scrum master mesure le capacitif de réalisation de son équipe permettant ainsi de définir au mieux le contenu des sprints.

Par ailleurs, les KeyUsers ravis (c’est un cas d’école, je le répète), mettent la pression sur la direction pour élargir l’agilité à autre chose que des QuickWins.

Cette demande est évoquée lors d’un comité de direction.

C’est alors que le DAF signale qu’il n’a pas de visibilité sur les coûts associés aux réalisations de la « cellule Agile », ce qui n’est pas grave quand il s’agit de « QuickWin » (budgété en ON GOING à l’année sur une taille d’équipe constante) mais peut le devenir pour des réalisations de plus grande ampleur.portfolio

Sur ce, le DSI rebondit en mettant en évidence que la taille de l’équipe pouvant fluctuer dans le temps (on est plus dans un cadre de ON GOING), il conviendra d’avoir une visibilité sur les charges de réalisation pour anticiper les besoins en ressources de la cellule.

Les directeurs de départements, quant à eux et unanimement, souhaitent maintenir les arbitrages sur les priorités de réalisation et continuer à avoir une visibilité sur les dépendances de réalisation.Gantt

Tous conviennent que l’idée est belle mais que pour qu’elle soit bonne, il convient de mettre en place un outillage permettant de préserver l’AGILITE mais aussi de planifier (GANTT) et de « coster » (PORTFOLIOS).

Qu’à cela ne tienne, demandons à notre prestataire préféré (encore une fois, c’est un cas d’école), de faire une étude de faisabilité.

Ce dernier mène sa réflexion sur le constat suivant :

Ne remettons pas en cause ce qui fonctionne. La cellule AGILE ayant basé son industrialisation sur JIRA, il faut le préserver à tout prix.

Etudions les plugIn JIRA qui permettent de répondre à la problématique qui m’est posée.

Atlassian (éditeur de JIRA) et les éditeurs de plugIn permettent d’avoir des versions d’évaluation d’un mois, c’est justement ce qu’il me faut se dit le prestataire pour faire une démonstration et la présenter au prochain comité.

Voici des extraits de cette présentation :

Solutions étudiées

JIRA SOFTWARE

BACKLOG & SPRINT

Backlog - Sprint

  • Un Backlog qui reprend l’ensemble des tâches non terminées et non affectées à un sprint
  • Création de 1 à n Sprint

SCRUM – SPRINT ACTIF

 sprint actif

  • Scrum = KANBAN
  • Paramétrage du Workflow des états des demandes par type de demande

BIG PICTURE

GANTT & BASE LINE

Gantt

  • Cross Projet ( 1 programme = 1 à n projets)
  • Synchronisation avec JIRA :
    • Tout changement dans JIRA (Ajout, Modification, Suppression de tâches, etc.) se répercute sur le GANTT
  • Ordonnancement des tâches
    • Au sens WBS è N’impacte pas le ranking du Backlog et des Sprints
  • Déplacement dans le temps et changement des durées des tâches
    • Ne modifie pas la Due Date de JIRA SoftWare
  • Intégration des dépendances entre tâches
    • Fin-Début, Début-Début avec délai en jours
  • Planification automatique ou à la demande
  • Base Line
    • Mémorisation à la demande des dates de début, fin de toutes tâches ou de tâches sélectionnées
  • Gestion de tâches non JIRA Software (Tâches artificielles)

CHEMIN CRITIQUE

chemin critique

  • Représentation des chemins critiques

MATRICE DES RISQUES

Risk Matrix

  • Placement des demandes à risque dans une matrice que l’on peut afficher sous-forme de Gadget dans le tableau de bord

ROAD MAP

Roadmap

  • Représentation d’une road map et des dépendances de réalisation

UTILISATION DES RESSOURCES

Team Occupation

  • Table de représentation des temps passés et planifiés par équipe et ressource

TEMPO FOLIO

COUTS DES RESSOURCES

Cout personnel

  • Gestion des coûts des ressources

PREVISION DES COUTS

Coût prévisionnel

  • Rapport des coûts réalisés, planifiés

RAPPORT DES DEPENSES

Rapport des dépenses

  • Gestion des dépenses (CAPEX – OPEX)
  • Reporting associé

RAPPORT DES COUTS DE DEMANDES

Rapport des coûts des demandes

RAPPORT COMITE DIRECTEUR

Rapport du comité directeur

Continuous Documentation – Une pratique saine

continuous-documentation

Souvent perçue comme une corvée, la documentation est dans la plupart des cas délaissée. Au mieux elle est reportée en fin de projet, au pire elle finit à la trappe. Les raisons de cette perdition sont souvent le manque de temps ou de budget. En fin de course, il est difficile de négocier une rallonge pour rédiger de la documentation.

Pourtant, l’utilité de celle-ci fait souvent l’unanimité. Elle est un facteur clef pour le maintien en condition opérationnelle (MCO), sur l’intégration de l’application au sein du SI ou encore facilite la prise en main du projet par une nouvelle recrue.

Par ailleurs, celle-ci fait partie des bonnes pratiques de la méthodologie Agile. En effet, les préceptes de la méthodologie agile recommande d’arriver à un état livrable de votre application à la fin de chaque sprint, alors pourquoi ne pas l’y inclure?

Les bénéfices

Une documentation disponible à chaque fin de sprint/jalon

La rédaction au-fur-et-à-mesure vous garantie d’avoir une documentation synchronisée avec l’évolution de votre application. Vous éviterez ainsi l’écueil d’une application sans documentation en fin de projet, et de ne plus disposer de moyens – temps et/ou budget – pour la réaliser. De plus à la fin de chaque sprint, votre application est dans un état livrable. Elle contiendra, à minima, d’un guide de l’utilisateur, d’une notice d’installation ou d’un manuel d’exploitation. Bref, votre application prend l’air d’un produit fini et délivrable à un client.

Une documentation précise

D’autre part, vous vous rappellerez plus aisément des points les plus importants en rédigeant leurs documentations dans un délai assez court après leurs réalisations. En conséquence, votre documentation en sera d’autant plus précise.

Une documentation pour chaque version de l’application

Enfin, cette synchronisation du code et de la documentation les met dans un cycle de vie identique. Code source et documentation évoluent ensemble, et pourquoi pas versionnée ensemble. Vous disposez, alors, d’une documentation adéquate en fonction de chacune des versions de votre application. Ainsi, par exemple, la dernière version de la documentation décrit les nouveaux paramètres d’une fonctionnalité, la précédente ceux devenus obsolètes mais encore valable pour la version antérieure (tout est bien dans le meilleur des mondes).

Les écueils

Documenter au moment opportun

Les exigences sont susceptibles d’évoluer tout au long du projet, vous obligeant à retravailler la documentation pour refléter ces changements. Aussi, il est préférable de ne pas documenter trop vite ou trop souvent. Mieux vaut attendre un état stable du produit. Vous éviterez ainsi de perdre du temps et de l’argent à retravailler la documentation.

Une documentation concise et adaptée aux besoins du produit

Il n’est pas nécessaire de tout documenter. La documentation n’a pas vocation à remplacer un cahier des charges ou des spécifications techniques. Bien souvent il sera préférable d’y trouver le manuel d’utilisation (accompagné d’exemples), un guide d’intégration, la description des API, etc. De plus, mieux vaut ne pas trop en faire. La rédaction doit rester rapide et concise.

Dans un prochain article, je vous présenterai un exemple de mise en oeuvre d’une « Continuous Documentation ».

Migration REDMINE vers JIRA AGILE

Aujourd’hui, notre client utilise deux outils de gestion des demandes (REDMINE – JIRA AGILE) :

  • Redmine
    PROJETS « METIERS »

 

  • JIra Agile
    PROJETS ERP et ECOMMERCE gérés en mode AGILE

 

Celui-ci souhaite vérifier qu’une migration REDMINE vers JIRA AGILE est possible et qu’elle lui permettra de retrouver ses fonctionnalités de reporting orienté KPI (indicateurs de performance).

Il a donc confié à SALTO cette prestation pour laquelle nous avons adopté le phasage suivant :

  • PHASE « MIGRATION REDMINE – JIRA AGILE« 

    1. Migration de 3 projets REDMINE vers JIRA AGILE
    2. Configuration de JIRA AGILE pour minimiser la conduite du changement (Filtres, rapports, vues et écrans similaires à ceux utilisés dans Redmine)
    3. Restitution des résultats
    4. Documentation de la procédure de migration

 

  • PHASE « KPI« 

    1. Intégration de PlugIns JIRA AGILE complémentaires
    2. Paramétrage et configuration pour retrouver les fonctionnalités ISO, notamment au niveau des KPIs
    3. Restitution des résultats
    4. Documentation des PlugIn JIRA AGILE

La suite de cet article se propose de synthétiser les résultats de la phase « MIGRATION », l’autre phase étant trop spécifique au contexte client.

Sachez cependant, que nous avons pu obtenir des résultats équivalents aux reportings Redmine grâce notamment aux plugings « Tempo TimeSheet », « Tempo Planner » et « Tempo Folio ».

MIGRATION

JIRA offre en standard des outils d’importation dont voici la liste :

Importation natives JIRA

On y retrouve notre composant d’importation pour Redmine.

Celle-ci se fait en plusieurs étapes :

Etapes de migration Redmine

  1. La connexion à l’instance de Redmine sur laquelle se trouve les projets à migrer
  2. Le choix des projets Redmine à migrer et le mapping vers les projets JIRA AGILE d’accueil
  3. Le mapping des champs personnalisés de Redmine vers les champs personnalisés JIRA
  4. Les champs à mapper (priorité, statuts, type de ticket)
  5. Pour chaque champ à mapper, l’association avec les valeurs de JIRA (plus exactement aux valeurs configurées pour les projets JIRA sélectionnés)
  6. Le mappage des liens entre les tickets (exemple : Copied à mapper avec Cloned)
  7. Et enfin, le lancement de la migration

On s’aperçoit vite qu’il convient, préalablement à la migration, de créer les projets JIRA AGILE d’accueil et de les configurer de façon à ce que l’on puisse mapper en 1 pour 1 les champs, les valeurs et les liens.

Nous avons donc créé une configuration « Migration RedMine » dans JIRA AGILE (dont chaque projet d’accueil « héritera »), comme par exemple :

  • Les types de demande et le schéma associé
  • Les écrans et les systèmes associés
  • Le WorkFlow et son système
  • Les champs personnalisés et leur système
  • Les droits pour l’intégration des rôles « Redmine » spécifiques (voir ci-après)
  • Etc.

Nous avons par ailleurs du créer :

  • les liens spécifiques
  • les rôles spécifiques

entre autres.

C’est une fois ce travail préparatoire effectué que la migration se fait sans problème.

Remarquons que l’on retrouve les identifiants des tickets Redmine dans le champ JIRA « External issue ID« . Cependant, pour ceux qui ont créé des liens entre les trakers dans les commentaires (#idt tracker) c’est un peu perdu.

Pour ce qui concerne la conduite du changement, JIRA offre tellement de possibilités de paramétrage, qu’elle peut être réduite au strict minimum, cela d’autant plus que

  • l’ergonomie proposée par JIRA est « Up To Date »
  • les TABLEAUX KANBAN, les DASHBOARDs, les WIDGETs

sont de véritables outils de productivité.

Enfin, l’intégration de PlugIn JIRA permet de palier à certains manques de JIRA (versus Redmine, s’entend) :

  • Le Gantt (mais est-ce réellement utile avec l’AGILITE) : Les plugIn sont nombreux quand il ne s’agit que d’un affichage
  • La feuille de temps : TEMPO TIMESHEET est une solution plus que satisfaitante

Pour conclure, l’importation des projets Redmine vers JIRA AGILE est réellement efficace à condition de l’avoir correctement préparée.