Devoxxfr – java.lang.Invoke

Finir Devoxx avec Rémi, c’est toujours cool.

On va s’intéresser aux loggers quand ils ne loggent pas. Et plus précisément quel est le coût d’exécution d’un logger qui ne logge pas ? Avec JMH, on a le résultat : ça prend 1 ns avec log4j2.

Et avec une lambda ? Ou un logger.isDebugEnabled() ? Ca prend autant de temps.

Normalement, le logger.isDebugEnabled() est un ensemble d’appels à des valeurs constantes. Et ça prend du temps parce que …​

La JVM ne croit pas que les champs final sont des constantes.

A cause de la …​ (de)serialization !!! Autrement dit, comme la JVM peut (dé)sérialiser tous les champs final ne peut pas inliner leurs appels.

java.lang.invoke

Avec cette API, on peut appeler du code dynamiquement d’une façon plus efficace que l’introspection pour plusieurs raisons :

  • Mise en cache de la sécurité
  • Pas de boxing

L’API est plus riche pour implémenter des langages. Elle est utilisée pour les lambdas, et pour la concaténation de chaînes de caractères en Java9.

Avec cet API on crée un MethodHandle qui est une espèce de pointeur de fonction. Il permet aussi de faire de l’invocation partielle. Et l’élément sur lequel il sera bindé sera considéré par le JIT comme une constante. En plus, il permet d’ajout des arguments qui ne seront pas passés à la méthode. Et de créer des MethodHandle vides ou de placer des gardes sur le MethodHandle.

Avec tout ça, Rémi recréée son Logger en utilisant exclusivement des MethodHandle. Le code est verbeux, mais on sent bien qu’il est particulièrement optimisé.

Bon, ça marche pas aussi bien que prévu. Allons donc voir le code source …​ de la JVM !

Le seul cas qui soit exploitable, c’est celui des classes anonymes (au sens de la JVM, pas du langage). Et ces classes anonymes au sens du langage sont les lambdas.

Ca marche bien.

Allons voir plus loin.

Avec une simple boucle et un logger.debug(…​) mais cette fois-ci avec log4j, on se retrouve avec des temps d’exécution différents là où il ne devraient pas être différents. Le tout à cause d’un volatile dans log4j. Ca amène Rémi à nous parler de SwitchPoint.

Et tout ce code est dans le projet Beautiful Logger. Qui lui est capable de ne pas logger sans consommer de temps CPU. En utilisant les mêmes techniques, Rémi a créé un projet de classes exotiques.

Mon avis

Je savais que ça allait dépoter, et je n’ai pas été déçu. Rémi nous en a mis plein la tronche avec des phrases qui se retrouveront sur son compte Twitter (ça n’est pas vraiment lui qui twitte).

Publicités

Devoxxfr – Lazy Java

Mario vient nous parler de la progammation lazy, fortement utilisée en programmation fonctionnelle, mais rare en style impératif. Qu’est-ce que c’est ? C’est une stratégie d’évaluation qui fait en sorte que la valeur ne soit calculée que quand elle est utilisée. En programmation fonctionnelle, une fonction stricte évalue ses valeurs immédiatement, quand une fonction lazy évalue ses valeurs uniquement quand elles sont nécessaires.

Et donc, globalement, Java est un langage strict …​ A quelques exceptions près (les opérateurs booléeans, l’opérateur ternaire, certaines structures de contrôle, et les streams). Mais on peut rendre Java beaucoup plus lazy. Ca rend par exemple l’opérateur ternaire difficile à remplacer par une fonction …​ sauf en utilisant des lambdas (en l’occurence les Supplier<T>). C’est utilisé dans les apis de log modernes, par exemple (voir dans log4j 2).

Et franchement, c’est une sacrément bonne méthode d’optimisation : rien ne va plus vite que de ne pas appeler le code !

Et les streams utilisent ça : on peut créer un stream d’entiers infini : IntStream.iterate(1, i → i+1). Et du coup, un stream, ça n’est pas une structure de données, mais la spécification de données qu’on peut obtenir. Et grâce à ça, l’aspect lazy du code des streams permet la séparation des responsabilités. Et pour Mario, c’est cet aspect, plus que la compacité du code, qui rend les streams utile.

Et là, Mario nous sort un exemple de calcul de nombre premiers classique d’abord, puis récursif avec un head et un tail sur la liste des nombres. Cet exemple ne marche pas …​ Parce qu’un stream ne peut être terminé qu’une fois. Et que sa récursion n’est pas lazy, du coup, pouf, une boucle infinie ! Alors qu’en Scala, il y a une lazy concatenation (l’opérateur #::) qui aurait permis au premier exemple de marcher.

Donc, Mario recrée une liste lazy à base de head/tail et de Supplier<T>. Et je comprend à peu près comment ça marche ! L’explication graphique du code mérite vraiment d’être vue.

Et pour les afficher, on peut utiliser une itération externe ou interne. Et sur sa collection magique, on peut aussi (et surtout) utiliser la récursion. La récursion, c’est pratique. Mais en Java, ça peut conduire à des StackOverflow. Parce que Java n’implémente pas la tail call optimization, qui permet des récursions infinies en enlevant certaines méthodes de la pile d’appel. Et Scala l’implémente également, bien sûr (par contre il faut annoter les méthodes où on veut l’appliquer).

Par contre, la tail recursion ne marche que si la méthode récursive est appelée en dernier.

Heureusement, on peut utiliser la technique des trampolines pour permettre cette récursion infinie. En fait, le trampoline, c’est une manière de transformer une récursion en une suite d’appels de méthodes, à grand coups de Supplier<T>.

Et pour finir, Mario se propose de nous implémenter de l’injection de dépendances lazy. Et honnêtement, qund il fait la liste des défauts de l’injection de dépendances classique, ça fait peur.

Et pour ça, il introduit la monade Reader qui est une façon commode d’invoquer une fonction dans un contexte.

Et ça marche d’une façon très chouette, voire même lisible.

Conclusion

L’approche lazy, c’est quand même la meilleure des optimisations parce que ça permet de n’exécuter que le code vraiment utile. Par ailleurs, la démarche fonctionnelle apporte souvent des intérêts en remettant au moment de la compilation des comportements qui doivent y être traités.

Mon avis

j’avais déja vu sur Twitter les slides de Mario, je crois, ou au moins une partie, que je n’avais alors pas parfaitement compris. Avec la présentation en live, j’ai beaucoup mieux compris, et j’ai pris une claque. C’est vraiment chouette.

Devoxxfr – Architecture hexagonale

Pourquoi ? Parce qu’à priori, les présentations sur DDD et l’architecture hexagonale ne parlent pas de framework, alors que les développeurs utilisent pas mal les frameworks.

Chez Saagie, le code s’exécute sur les bonnes machines, grâce à un container manager. Et celui-ci se trompe parfois. Ce container manager est développé avec un ensemble de couches classique. Et le code qui permettait au container manager de choisir où exécuter du code était éclaté dans plusieurs ensembles de service/DAO. Youen a donc refactoré ça dans une méthode à trois ifs.

Comment éviter ça ?

Théoriquement, l’architecture hexagonale permet d’éviter ça.

Domaine

Dans le domaine, il ne faut pas de framework. Des librairies simples peuvent en revanche être utiles.

Le domaine ne doit jamais contenir de code d’infrastructure.

Ports

Les ports sont les moyens d’appeler le domaine. Les ports primaires sont des méthodes du domaine appelables depuis l’extérieur. Et les ports secondaires sont des interfaces définies dans le domaine lui permettant d’appeler des éléments extérieurs.

Adapteurs

Les adapteurs permettent au monde extérieur d’appeler les ports.

Mise en oeuvre

Ensuite, Youen se lance dans une session de live-refactoring pour mettre en place ce type d’archiecture avec une application Spring Boot. Je vous ferai le diagramme de classe plus tard, mais globalement, en deux astuces, il arrive à écrire une application dont les règles métier sont totalement indépendantes des outils techniques utilisés pour communiquer avec le monde extérieur.

Conclusion

Avec ça, Spring est dans son cas nominal, tout comme le domaine. L’architecture hexagonale est donc parfaitement respectée, en utilisant les techniques classiques du monde Java. Le domaine est donc propre, les tests faciles et le code s’adapte beaucoup plus facilement aux échelles. En revanche, les DTO sont un peu lourds, les développeurs doivent être formés et il n’y a pas forcément de starters pour les frameworks.

Mon avis

En fait, l’architecture hexagonale, ça n’a rien de nouveau ni rien de vraiment sexy. Ca consiste surtout à séparer le code réseau du code métier et du code de stockage « d’une façon ou d’une autre ». Et si la méthode choisie par Youen est présentée comme astucieuse, elle est en fait uniquement un bon découpage en classes. Autrement dit ça n’est pas bien compliqué, c’est facilement adaptable et ça me parle puissamment.

Devoxxfr – Effective Java

Là, c’est du sérieux ! Joshua Bloch, l’auteur d’Effective Java vient nous parler de la troisième version de son livre. La deuxième datait de 2008, alors inutile de dire qu’il y a eu des changements depuis.

Joshua ne va pas nous parler de tout son livre (parce qu’il n’a que 45 minutes)

Préférer les lambdas aux classes anonymes

Avant, les classes anonymes, c’était bien (et adapté aux patterns OO). Mais ça n’est plus très pratique avec la programmation fonctionnelle. Donc les lambdas, c’est mieux. Mais avec les comparateurs, c’est un peu plus pratique (puisqu’ils construisent les lambdas à la volée).

Eviter les types sauf si ils sont nécessaires

Dans les lambdas, avec l’inférence de type, oublier les types, c’est cool. Parce que l’inférence de type, c’est magique, mais ça marche. Par contre, ça repose sur les informations des types génériques. Donc utilisez les génériques, sinon les lambdas ne marchent pas.

Utiliser les lambdas dans les enums

Avant, on pouvait faire des enums avec comportement variable grâce aux classes anonymes. Maintenant, grâce aux lambdas, c’est nettement plus facile : il suffit de passer une lambda avec l’opération variable dans le constructeur de l’enum.

Attention aux lambdas

Elles n’ont ni nom, ni documentation. Donc si vous ne comprenez pas ce que fait une lambda, faites-en une méthode. Et les classes anonymes ont quelque avantages :

  • pas besoin d’interface fonctionnelle
  • une classe anonyme a un this, et pas la lambda

Préférez les références de méthodes aux lambdas

Ca rend le code d’autant plus lisible qu’il y a de nombreux paramètres à la méthode. Et dans une lambda, il faut faire attention au nom des paramètres

Parfois, les lambdas sont plus courtes à écrire

Par exemple (x → x) est plus efficace que (Function.identity()).

Attention aux types de référence de méthode

Chaque type de méthode a sa propre déclaration de référence, ce qui peut rendre les choses un peu compliquées (mais toujours plus simples que les lambdas).

Préférez les interfaces fonctionnelles standard

L’exemple est un peu long, mais globalement, l’idée, c’est que si une interface fonctionnelle existe déjà, utilisez-la plutôt que de redéclarer la vôtre. Parce que bon, Java a déjà 43 interfaces fonctionnelles ! Les plus importantes sont UnaryOperator, BinaryOperator, Predicate, Function, Supplier, Consumer. Et puis elles fournissent déjà une API, ce qui limite ce qu’un utilisateur de votre API doit apprendre. En plus, elles fournissent des méthodes par défaut intéressantes (comme Predicate qui fournit combine et negate)

Quand ne pas les utiliser ?

Evidement, quand aucune de ces interfaces ne correspond à votre besoin. Ou alors quand votre interface fonctionnelle définit un contrat clair (comme par exemple Comparator).

Donc, écrivez votre interface fonctionnelle si

  • elle sera beaucoup utilisée
  • elle a un nom clair
  • elle définit un contrat fort
  • elle pourrait bénéficier de méthodes par défaut

Mais n’oubliez pas vos responsabilités : vous définissez une interface, et ça c’est pas de la tarte.

Utilisez les streams avec justesse

Donc un stream, c’est un flux de données traité par un pipeline dans lequel on trouve

  • un générateur
  • zéro ou plus d’opérations intermédiaires
  • et enfin une terminaison

Joshua nous montre ensuite un exemple dans lequel il remplace toutes les collections par des streams. Et c’est illisible. La conclusion est évidente : utilisez les streams avec justesse pour éviter de faire de votre code un bazar sans nom. Notez par ailleurs que les streams de caractères …​ ça ne marche pas. Par ailleurs, dans certains cas, il est difficile de déterminer si les streams seront meilleurs que les itérations traditionnelles.Enfin, la parallélisation des streams donne parfois des résultats désastreux (comme dans son exemple de calcul de nombre de Mersenne où chaque valeur dépend de toutes les valeurs précédents.

Conclusion

Java est maintenant un langage multiparadigme. Choisissez avec soin les parties que vous utilisez.

Vavr, tu vas voir !

Merci à Norsys, qui offre le food truck ce soir (!). (par contre, le chauffage semble être mort dans la soirée).

Guillaume nous vient de chez Saagie (où je connais quelqu’un …​ depuis pas mal de temps) dans l’équipe d’outillage. Et quand il parle, j’ai presque l’impression d’entendre parler un collègue tout aussi barbu, mais plus rancheros …​

La programmation fonctionnelle

C’est de la programmation sans effet de bords, et c’est cool. Et pour faciliter ça, les fonctions sont des éléments essentiels du langage, les variables sont immutables (ça n’a pas de rapport avec la programmation fonctionnelle, en vrai, mais ça fait maintenant partie du canon fonctionnel).

Enfin, ce qui est important pour Guillaume, c’est aussi la transparence référentielle (ou referential transparency) qui garantit que deux appels à la même fonction retourneront le même résultat.

Par exemple, Math.random() ne respecte pas cet aspect, mais Math.max(1, 2) oui.

Java 8 est déja fonctionnel ?

Ben oui, avec les lambda, les streams, les Optional<?>, c’est un début. Mais pour Guillaume il manque des trucs.

Vavr …​ anciennement Javaslang

La librairie a été créée par un développeur Scala frustré par Java. Et le nouveau logo retourne joliment Java …​

Les collections Java, c’est pas pratique

Il y en a en java …​ mais c’est loin d’être pratique. Parce qu’à la base les collections sont conçues pour être mutables (et pas parce qu’ils n’ont pas compris l’immutabilité). Autrement dit, elles ne sont pas joliment immuables, ce qui fâche les puristes. Donc vavr redéfinit ses collections (y compris un Vector).

Typiquement, les méthodes Collection.stream() et Stream.collect() n’existent pas en vavr, parce que les collections sont conçues autour de concepts fonctionnels.

Guillaume mentionne ensuite un point intéressant : en programmation fonctionnelle, on n’aime pas trop les exceptions parce qu’elles ne respectent pas trop le flux d’opérations.

Streams inconsistents

Parfois, les streams Java démarrent leurs opérations un peu en avance. Du coup, deux appels successifs à Stream.map`(…​.)` peuvent balancer des exceptions, ce qui est insupportable pour Guillaume …​ Peut-être pas autant pour moi.

Streams de Map

En Java, utiliser un stream sur une map, c’est l’enfer (il faut faire le stream sur l’ entrySet() et reconstruire la Map au moment du collect). Par contre, en vavr, c’est super facile parce que vavr va mapper les couples clé/valeur vers des Tuple.

Value types

Ils arriveront en Java 10, peut-être (il faudrait demander à Rémi Forax). En revanche, il y en a un paquet dans vavr, inspirés de Scala, évidement.

Option

Hey, mais l’ Option de vavr est sérialisable ! C’est très cool !

Try

Qui permet d’encapsuler du code susceptible de lancer une exception. L’exception sera gentiment catchée et conservée. C’est pas bête du tout.

Either

Là, on est en plein dans la scalaterie. Ca permet de retourner deux types différents en disant que le retour est soit l’un soit l’autre. Bon, jusque là, c’ets un tuple. La particularité d’ Either, c’est qu’on prend comme convention que le bon cas est celui de droite …​ autrement dit le deuxième …​ Je trouve ça pénible et un authentique retour en arrière.

Validation

Qui ressemble furieusement à Bean Validation dans l’objectif …​ mais pas du tout dans l’implémentation, puisque c’est encore un Tuple, voire même un Either particulier.

Des fonctions

vavr ajoute aux Function et BiFunction des interfaces permettant d’utiliser jusqu’à 8 paramètres. Ca n’est pas très pur fonctionnellement, mais c’est bien pratique.

Composition

Mais bien sûr qu’on peut composer des fonctions avec vavr !

Lifting

Chez les fonctionnalistes, c’est la transformation d’une fonction impure en fonction pure. Et je dois bien dire que c’est assez propre.

Application partielle de fonctions

Là aussi, ça fait rêver tous les haskelliens, et c’est assez simplement réalisé. Mais mon voisin pose une excellente question : si j’applique ma fonction partielle à 5 paramètres, lequel reste à utiliser ? Le premier ou le dernier ?

Curryfication

A priori, c’est très semblable à l’application partielle, tout en étant subtilement différent.

Mémoisation

Un peu comme un cache, en fait …​ mais sans avoir besoin de réécrire à chaque fois.

Et maintenant, le pattern matching

Alors là ça va être un peu fou …​ Toujours est-il que, vous le savez maintenant, le pattern matching, c’est un switch sous stéroïdes (mais alors beaucoup de stéroïdes). Par contre, comme vavr reste du Java, c’est la syntaxe habituelle qui est utilisée. Et du coup, c’est moins surprenant puisque l’instruction langage est remplacée par des appels de méthode dans tous les sens. Ca n’est pas forcément très lisible à mon sens, mais bon. En revanche, ce qui est vraiment moche, c’est que le cas d’usage typique, c’est pour faire des instanceOf plus propres de la manière suivante :

Number plusOne = Match(obj).of(
    Case($(instanceOf(Integer.class)), i -> i + 1),
    Case($(instanceOf(Double.class)), d -> d + 1),
    Case($(), o -> { throw new NumberFormatException(); })
);

Bon, ben là, franchement, je suis désolé, mais il n’y a aucune espèce de gain par rapport au simple instanceofdans un if (qui est un foutu antipattern).

Par contre, pour filtrer une liste d’adresses entre valides et invalides, c’est pas mal du tout (merci Either).

Property Checking

On parle ici de la génération automatique de cas de tests. Et il y a pour les différents types de variables simples, des générateurs aléatoires inclus. Ca n’est pas inintéressant …​ mais le test est salement pollué par tout le code supplémentaire nécessaire pour gérer ce contenu aléatoire. Il me semble qu’il y a d’autres outils qui fournissent le même genre de services

Conclusion

La présentation était intéressante et Guillaume sacrément motivé pour nous convaincre (et ça, c’est très cool). Néanmoins, je ne peux pas m’ôter de la tête l’idée que vavr est avant tout un exercice de style, une espèce de figure imposée éventuellement utilisable sur un projet, mais dont ça n’est pas le but premier. Pour moi, le but de ce genre de librairie (comme ça a pu l’être quand j’ai écrit gaedo), c’est avant tout de tester la faisabilité d’un concept. Après, pour l’utiliser, il faut quand même avoir des besoins qui sont à mon sens particulier (surtout si quelqu’un veut sortir de Java 8 pour utiliser vavr). L’idée est néanmoins intéressante, et certains aspects m’ont paru particulièrement séduisants.

Java 9, tu modules ?

Rémi ?

Vous connaissez Rémi ? Il est maître de conférences à Marne la Vallée, développeur d’ASM (j’ignorais ça), d’OpenJDK, expert pour le JCP (et clairement spécialiste des évolutions du langage, pas la partie JavaEE) où il a ajouté invokedynamic, et a travaillé sur les lambda, Jigsaw.

Java 9

Java 9 est la dernière grosse release, parce que c’est pénible. Ca va être remplacé par une release tous les six mois …​ c’est un peu plus rapide. Et donc, dans six mois, on verra var apparaître dans le langage Java.

Les modules

Historiquement, ça devait arriver en Java 6 ou 7 …​ Ca a pris un peu de retard.

Qui veut modulariser le JDK ?

Il y a d’une part des développeurs du JDK qui veulent le faire évoluer, mais ne peuvent pas à cause de toutes les APIs qui utilisent des classes « cachées » du JDK. Et d’autre part …​ des experts de sécurité qui considèrent les failles de la plateforme.

D’autres problèmes

L’enfer du CLASSPATH

Sans outil pour gérer automatiquement le CLASSPATH (genre maven, gradle et autres), c’est l’enfer. Par ailleurs, quand le ClassLoader charge une classe, il scanne linéairement tous les jars et charge la première classe qu’il trouve. Du coup, c’est long. Mais aussi super merdique quand on a deux versions du même artefact. Du coup, on aimerait bien que ce soit géré par la JVM. De la même manière, éviter que les NoClassDefFoundError ne sortent qu’à l’exécution serait une bonne idée pour les développeurs.

Alléger Java

Ce serait sympa de pouvoir faire tourner le code Java sur des plateformes plus légères (typiquement pour l’IoT). Personellement, ça meparaît vaguement possible avec des outils comme ProGuard de limiter la taille des dépendances, mais ce serait mieux d’avoir ça directement dans le JDK. Par exemple, rt.jar contient encore des parseurs XML (qui n’ont pas à être des classes privilégiées) ou CORBA (qui s’en sert encore ?).

Comment modulariser ?

standards
Aucun rapport avec Java, bien sûr

Il y a déja des standards de modules en Java

  • maven
  • gradle
  • JBoss
  • OSGi
  • JavaEE

Du coup, Jigsaw doit fournir un système de modules compatible avec tous ces systèmes prééexistants. Ce qui implique tristement qu’il ne peuvent pas contenir de numéros de version. Et ça, c’est moche.

Modularisons donc !

C’est quoi un module ?

Dans un module, il y a

  • des dépendances
  • et des classes qu’on veut garder invisibles aux autres packages

Par exemple, java se est constitué d’un paquet de modules …​ dont java.compiler. Ce qui signifie qu’il n’y a plus de distinctions JRE/JDK.

Tout ça est décrit dans un module-info.java qui va être compilé comme le reste du code. Ca empêche les modifications ultérieures, ce qui est bien.
A la compilation, il ne peut donc plus y avoir de « split-packages » (des packages déclarés dans deux JARS), ou de dépendances cycliques.

Qu’est-ce qu’on perd ?
  • les « split-package »
  • setAccessible(true) sur une classe d’un module fermé

Au passage, ça permettra enfin à Rémi d’optimiser les champs final (un truc dont j’étais sûr qu’il existait déja pourtant)

Enfin, sauf si on active le flag --illegal-access=permit (qui est parfaitement clair). Notez qu’il y a d’autres valeurs possibles (warn, debug). Bon, un jour, il disparaîtra.

En bonus, les classes JavaEE cachés dans JavaSE (java.xml.bind, java.xml.ws et autres) ne seront plus dans le JDK. Notez que ces classes sont toujours accessibles via des dépendances Maven.
Les classes sun. et com.sun. qui ne sont pas utilisées sont également supprimées.

A noter : L’annotation @ForRemovall (je ne suis pas sûr de l’orthographe) indique les classes qui seront supprimées dans la prochaine version du JDK. C’est une version étendue du classique @Deprecated.

Comment trouver les dépendances

Avec jdeps, par exemple, on peut facilement trouver toutes les dépendances d’un module. Notez que cet outil existe déja dans Java8.

Oui, mais comment je transforme mon application pour Java9 ?

Pour ça, Rémi a développé son application de test : ModuleTools qui lui permet de lire et d’écrire un fichier module-info.java ou module-info.class.Et dans son application, il déclare quelques modules, qui dépendent tous de java.base (comme les classes Java dépendent de java.lang.Object).

Contrairement à maven, un require de module n’est pas transitif (contrairement à maven). Du coup, il y a un require transitif, mais qui est limité à un niveau.

En terme d’organisation de fichiers, il est possible d’avoir plusieurs organisations, dont par exemple plusieurs modules dans le même dossier (ce qui n’est supporté ni par maven, ni par gradle, ni par les IDE).

Il est possible de stocker la version du JAR dans le module-info, et de la réutiliser depuis le code, mais le système de modules n’utilise pas la version.

Comment utiliser un JAR non modulaire ?

Typiquement, une dépendance de Maven Central.On ne peut évidement pas ajouter simplement le JAR dans le CLASSPATH. Il suffit en fait de mettre le jar dans le module-path, et Java va générer automatiquement un module-info, qui permet en plus à ce module automatique de dépendre de JARs du CLASSPATH.Et comment s’appelle ce module ? Soit le nom fourni dans le MANIFEST.MF comme Automatic-Module-Name, soit le nom du JAR sans le numéro de version. Du coup, en ce moment, sur GitHub, c’est la fête à l’Automatic-Module-Name. Au passage, il est possible d’utiliser les JARs modulaires comme des JARs classiques, en les mettant dans le CLASSPATH !

Comment limiter la visibilité ?

Si on veut faire un module interne, il est possible de limiter sa visibilité à certains modules spécifiques, et pas au monde entier. C’est très chouette pour mieux gérer la sécurité. C’est ce qu’ont fait les développeurs du JDK pour la tristement célèbre sun.misc.Unsafe dont une partie a été déplacée dans jdk.unsupported, et une autre partie dans un jdk.internal.misc. Du coup, la réflexion n’est plus possible sur les champs privés (dommage pour gaedo). C’aurait été pénible pour JPA …​ mais les fournisseurs d’implémentation JPA utilisent des agents qui changent le code avant son exécution. Cela dit, la réflexion reste possible grâce à open sur un package ou sur le module.Par ailleurs, le très moche ServiceLoader a été étendu pour fonctionner avec les modules …​ La différence, c’est que la description est un peu propre et intégrée au module-info. Et la description à la fois des fournisseurs d’implémentation et de l’utilisation de ces implémentations doit être fournie. C’est super cool, parce que ça permet de tester les injections de dépendances.

Packager ?

Avec jlink, on peut créer une image à la volée de l’application et de ses dépendances. Pratique pour l’embarqué, évidement, mais aussi pour Docker. Hélas, il ne peut pas y avoir de modules automatiques, parce qu’avec ces modules automatiques, il faut embarquer tout le JDK, et ça limite en plus énormément les possibilités d’optimisation de jlink. C’est un peu la carotte de Jigsaw. Ca permet aussi d’utiliser une JVM minimale de 3 Mo !. Bon, sans JIT, mais sur un Raspberry, c’est pas vraiment utile. Ca permet aussi de garantir la durée d’exécution …​ avec jaotc, qui génère du code natif (qui pourra même se passer de JIT). Le but à terme de cette expérience est (accrochez-vous) de réécrire la JVM en Java (pour se débarasser du code C++).

Evidement jlink (et l’ahead-of-time-compiling) est chouette pour les performances. Et pour l’espace disque.

Conclusion

Modulariser, ça n’est pas si facile, à la fois pour les implémenteurs et les utilisateurs. A ce sujet, la conclusion de Rémi est parfaitement claire : si vous avez une application non-modulaire, n’essayez pas de la modulariser. C’est à la fois long, complexe, et sans valeur ajoutée. En revanche, si vous vous lancez dans une nouvelle application, la modulariser dès le départ peut être utile.

Et merci à nos amis du chtijug pour une soirée sans faille, dans une super salle, et avec une vue vraiment chouette !

Hop, hop, hop, un chtijug !

Un nouveau site (les terrains de Décathlon), pour deux présentations …​ au chaud !

Dix méthodes pour garder les développeurs

J’ai vu les slides de cette présentation, j’en ai donc une idée assez précise (malheureusement, je n’arrive pas à les retrouver dans la timeline de Cyril …​).

Les 3/4 des développeurs sont plus on moins à la recherche d’un nouvel emploi.

Bien recruter

D’abord, il faut s’arranger pour que le recrutement se passe bien, avec un peu de transparence : vision, cadre, salaire, code, soft skills (beuark, quelle frangliche dilbertien), tout ça doit être présenté.

En bonus, il y a certaines pratiques à éviter :

  • L’offre d’emploi impossible
  • Le code sur tableau blanc, remplacé par exemple par Codingame (mais ça n’est pas encore le vrai environnement …​)

Et d’autres à favoriser

  • Code review
  • Pair programming (eh, mais c’est ce que j’ai fait avec Youssef !)

Proposer une carrière

Oui, alors ça, bon, je connais. Et franchement, les développeurs non motivés, au bout de 3 ans, ils sont devenus chefs de projet.

Quant au graphique produit par N. Martignole sur la carrière d’un déveeloppeur, il est adapté à la vision moderne de l’informatique. Expert au bout de 5 ans, c’est vraiment de la blague.

Travailler sur l’environnement

Avant tout, arrêter l’open-space. Et c’est vrai que mes expériences récentes, dans des bureaux de moins de 15 personnes, m’ont réconcilié avec les gens : quelqu’un qui vient me voir vient réellement me voir, et ça change tout, à la fois en qualité de communication et en amour-propre.

En parallèle, avoir des machines de qualité, et avec de bons écrans, c’est évidement bénéfique. Et de la même manière, avoir de bons logiciels, ça ne coûte pas bien cher par rapport à la valeur ajoutée.

Hey, mais ils citent le test de Joël ! Cool. Bon, en fait, je suis sûr que ce test reprend tout le contenu de cette présentation, mais je ne vais pas râler.

Organiser son travail

Mettre en place des features teams, ça peut être bien. Ou les pizzas teams. En tout cas éviter les équipes façon galère de 50 personnes.

Software craftmanship

Bon, je vous encourage à relire le manifeste …​

Du coup, pour ça, il faut avant tout vouloir s’améliorer. En passant, pour vouloir s’améliorer, il faut avoir un sens du temps long …​ Autrement dit, envisager sa vie professionnelle sur plus de deux ans …​ voire plus de dix.

Tech radar

Bon, là, j’ avoue, j’ai soupiré. Mettre en place un tech radar, ça a un côté tellement poussiéreux, tellement pénible. J’ai vraiment du mal à voir en quoi ça peut présenter un intérêt, parce que ça met en place des comités théodule internes. Je comprend le besoin d’urbanisme, mais franchement, j’ai l’impression que c’est la très mauvaise méthode pour suivre l’évolution technologique.

Ne pas se mentir

Dire qu’on est cool, avec des consoles, du café gratuit, des nerfs (eh, mais on a tout ça, et en plus une Wii U), ça ne vaut pas le travail intéressant (mais on a aussi ça, ça va).

Autrement dit, il ne faut pas remplacer le sens de l’entreprise par tout un tas de bonu qui, eux, ne font pas sens. Cela dit, c’est raccord avec le premier point : lorsqu’on recrute un développeur, on l’intègre au sens de l’entreprise, à ses valeurs.

S’organiser en communautés

Alors théoriquement, c’est censé permettre de la transversalité, encore faut-il que les communautés soient vivantes. Et pour ça, il leur faut des moyens matériels : du temps, de l’espace, des outils de collaboration.

Malheureusement, dans le seul contexte où j’ai vu des gens essayer, ça n’a rien apporté.

Contribuer à l’open-source

Bon, alors là, ça tombe bien, parce que ce matin, un collègue a lancé un recensement des participations de l’entreprise à l’open-source. Et ce soir, il y a déja 60 contributions recensées ! Evidement, parfois, c’est difficile, parce que la sécurfité, tout ça. Dans ces cas-là, monter un Gitlab est une très chouette idée.

Participer aux événements

brown bag lunch, conférences, jugs (ou meetups, ça dépend comment vous voulez appeler ça), tout ça, c’est assez plaisant effectivement. Et puis ça permet à des speakers débutants de prendre l’habitude de parler en public.

Conclusion

Je crois que le message est bon. Mais je crois aussi que le public n’est pas le bon : faire cette conf à l’USI, ou dans un truc de décideurs, bien sûr. Mais je ne suis pas sûr de l’intérêt pour les développeurs.

Vert.x et Kubernetes

Eh, mais c’est Clément Escoffier ! Sans vouloir dire de mal de Cyril et Romain, j’étais surtout venu voir Vert.x et des conteneurs, et je n’ai pas été déçu. Donc Clément va développer des microservices Vert.x et les déployer dans un cloud …​ local (parce que le wifi ne semble pas très bien marcher). Le tout sans aucun slide.

Et il va packager son application avec le vert.x-maven-plugin pour générer des fat-jars. Ca n’est pas indispensable, mais c’est plus facile. Et ça permet à Clément de montrer un peu de maven-fu (parce qu’honnêtement, il en a sacrément, du maven-fu).

Donc en Java, un vert.x, c’est une sous-classe de AbstractVerticle …​ mais ça marche aussi dans la plupart des langages supportés par la JVM (Scala, Groovy, Ruby, Clojure sans doute, …​). Et dans une application Vert.x, pour chaque requête, on appelle la méthode de réponse …​ dans le même thread. Et c’est cool, parce qu’il n’y a pas de context switch.

Mais d’un autre côté, il ne faut pas bloquer ce thread. Sinon c’est la merde.

Pour continuer à développer, avec vertx-web, on peut créer un routeur, et assigner à chaque route une méthode correspondante. Ensuite, Clément nous implémente le serveur tranquillement, avec un mode d’écriture qui m’a beaucoup amusé, et qui est à mon avis la marque des développeurs expérimentés.Par contre, IntelliJ IDEA qui s’amuse à mettre le nom du paramètre en gris devant la valeur, ça me trouble un peu, même si je dois bien reconnaitre que c’est une sacrément bonne idée.

Et maintenant, comment déployer ça sur le cloud ? Et en particulier Kubernetes ? Et en particulier OpenShift ? Donc Kubernetes, ça permet de gérer facilement des gros paquets de conteneurs (logs, sécurité, volumes, …​). Et avec OpenShift, on a une interface, et c’est cool. Et en bonus, on a du build automatisé, ce qui est encore plus pratique. Et avec tout ça, en tant que dév, Clément va pousser son code, qui sera buildé et déployé par OpenShift, et les requêtes seront exécutées dessus.

Et quand ça marche, ça fournit des trucs assez spectaculaires, comme par exemple du déploiement avec rolling updates ! Ou par exemple le déploiement de nouveaux pods avec un simple click. Du coup, comment partager les données ? Avec Redis, par exemple. Et évidement, il faut du service discovery …​ qui est vraiment très facile à implémenter avec vertx.

Et maintenant, depuis notre service, on va en appeler un autre …​ en parallèle …​ à travers RxJava ! Et d’un coup, l’une des propriétés intéressantes de http prend tout son sens : comme c’est un protocole chunked, il n’est pas éncessaire de calculer toute la réponse avant de l’envoyer. Du coup, la mémoire est moins utilisée, l’application coûte moins cher. Et en bonus, le garbage collector n’a pas besoin d’arrêter l’application pour passer. Et ça, c’est vraiment beaucoup plus efficace.

Et c’est le petit moment gênant où la démo s’enraye…​ Mais elle revient sur les rails assez rapidement.

Et maintenant, Clément ajoute des circuit breakers, au cas où le pricer soit un peu trop lent. Et là aussi, comme on fait du vertx, il faut un circuit breaker qui n’utilise pas de threads (donc pas hystrix, par exemple).

Et pour finir la démo, Clément pousse le code sur GitHub en live !

Conclusion

Le talk est à la fois parfaitement spectaculaire dans la forme et vraiment intéressant dans le fond : Vertx permet de développer très facilement des services HTTP (oui, on peut faire autre chose, mais à quoi bon ?), et l’intégration avec OpenShift était aussi poussée que propre.

Compiler ou tester ?

En fin de cette semaine, une controverse intéressante a eu lieu sur twitter, habilement lancée par cet article de « Uncle » Bob Martin : The Dark Path.

Regardez par exemple cette sélection de tweets sur le sujet :

On peut dire que les avis sont contrastés.

Et comme la meilleure façon de ne laisser aucun doute sur le sujet de ma propre stupidité, laissez-moi vous donner mon avis sur le sujet.

A quoi sert le compilateur ?

Il y a des années, à l’époque du C, le compilateur servait à transformer le code source écrit dans un langage quasiment humainement compréhensible (le C) en un code exécutable par la machine.

Aujourd’hui, quelque soit la plateforme d’exécution (la JVM, la CLR de Microsoft, ou le résultat d’une transformation LLVM), le compilateur fait beaucoup plus de choses : il simplifie, optimise, vérifie le code d’une façon très poussée.

Et à mon avis, c’est l’argument de Romain Guy : si le système de type est suffisamment poussé, il est impossible d’écrire un code qui ne corresponde pas précisément aux souhaits du développeur. Mais à quel prix ? C’est ce que cherche à évaluer Uncle Bob.

Laisser le compilateur vérifier ? ou Tester

Parce que l’avantage des tests sur le compilateur, c’est quand même de laisser le développeur vérifier les choses à sa façon : il écrit l’histoire dans son test, et vérifie les choses comme il le souhaite. Qui plus est, le compilateur est certes capable de vérifier certaines erreurs bas niveau, mais il y a tout un tas de trucs que le compilateur ne sera absolument pas capable de vérifier, et qui nécessiteront des tests.

Alors quoi ?

Heureusement, aujourd’hui, la question est en fait tranchée : les développeurs utilisent les deux, en fonction à la fois du coût d’implémentation de chacun, et de l’expressivité de chacun. Cela dit, je continue à trouver cette question intéressante. Et, à titre personnel, je comprend parfaitement la décision de Kotlin et Swift d’empêcher les références null, même si je préfère une solution plus simple : je n’utilise autant que possible jamais de nullen Java. Et du coup, si NullPointerException il y a, c’est forcément un bug qui nécessitera un test. Est-ce que c’est plus coûteux que d’avoir des <Object>? à déréférencer ? Je n’ai pas l’impression. Mais bien sûr, je peux me tromper.

Le quidditch, c’est pas si facile

Pour la troisième fois, je me suis lancé dans un des contests de Codingame.

Cette fois-ci, Harry Potter était à l’honneur.

fantastic_bits_ld-compressor-1
Oui, enfin, sans les combats de sorciers

En particulier, le quidditch. Enfin, un quidditch un peu simplifié : seulement deux sorciers, et, surtout, pas de vif d’or. Et heureusement, parce que j’en ai bien bavé pour en arriver à résultat un peu meilleur que la dernière fois, et surtout, cette fois-ci, raisonnablement satisfaisant, vu le temps investi :

score
707 sur 1950 qui passent atteignent le bronze, c’est bien. Mais s’arrêter à la porte de la ligue gold, ça craint.

EDIT : heureusement, une heure plus tard

score_1

Alors, comme la dernière fois, il y a des leçons à retenir.

D’abord, mon code magique qui produit des tests directement à partir du jeu est très pratique, mais nécessite que le code n’utilise jamais de random() sous quelque forme que ce soit. Cela dit, c’est tellement pratique ! regardez mes classes InGameTest, comme par exemple ici

test_generator
Et n’oubliez pas que ça vient directement de l’état du jeu à instant donné !

Ensuite, utiliser le bug tracker de GitHub comme bloc-note, c’est évidement pratique pour me garantir que j’ai traité le sujet efficacement. Et je m’en resservirai.

Enfin, j’avais une première implémentation que je savais boiteuse dès jeudi. Mais, si vous regardez mon historique de commit, vous verrez … rien ! En effet, le code que j’ai mis en place samedi et dimanche n’a pas été placé dans GitHub avant ce soir … Le fameux rush de fin de projet :-). Cela dit, il n’y avait peut-être pas de commits, mais il y avait des tests ! Et j’utilise toujours avec autant de profit mon plugin Maven qui génère le fichier source de codingame. Mais pour en revenir à cette histoire d’implémentation, clairement, il va falloir que je passe plus rapidement à la deuxième implémentation. Je dis deuxième, mais pour Hypersonic, j’ai enchaîné 4 implémentations différentes, donc n’en avoir que deux, c’est déja un net progrès. D’ailleurs, je pense ne pas vraiment pouvoir en faire moins. En effet, mis à part pour les super-tueurs genre Magus, il paraît difficile d’obtenir une première version qui soit à la fois correcte conceptuellement et efficace.

Mais bon, tout ça ne doit pas ôter quelque chose que j’ai écrit sur hypersonic : coder une de ces satanées IA est un sacré challenge, mais aussi un sacré fun. Et pour le coup, le quidditch était très bien grâce à sa complexité.

PS : Bravo à nmahoude :

Je ne suis pas hypersonic sur CodinGame

Depuis deux ou trois semaines, avec quelques collègues, nous avons créé des comptes CodinGame et nous nous sommes lancé dans les jeux pour programmeurs qui y sont disponibles.

Au début, c’était simple …

Et puis, il y a deux semaines, ils ont lancé Hypersonic. Bon, hypersonic, c’est pas bien compliqué, c’est faire jouer un bot que vous programmez. Donc j’en ai codé un.

Et j’ai fini quasi-dernier en bronze.

Vous pouvez lire le post-mortem que j’ai écrit pour y voir les leçons techniques.

J’en tire quelques autres leçons, plus humaines.

La première est assez logique, pour un jeu : c’est la chose la plus amusante, la plus prenante, et la plus crispante que j’ai codé au moins cette année, et facilement ces cinq dernières années. Imaginer un algorithme, le développer, et le voir se comporter, même mal, a ce côté démiurgique qui est la raison pour laquelle je suis développeur. Le corollaire évident, c’est que je ne comprend toujours pas pourquoi mon métier est aussi chiant actuellement : j’ai utilisé des méthodes professionnelles pour développer ce bot : tests, profiling, … Et c’était vraiment amusant. J’en tire la conclusion que notre métier est chiant parce que les demandes des clients sont, la plupart du temps, d’un ennui mortel.

La seconde est assez logique, mais moins drôle : j’ai fini 1675ème ! C’est médiocre, évidement. Et c’est vrai que mon bot était médiocre : des timeouts partout, et des décisions suicidaires encore plus souvent. J’ai pris une vraie leçon d’humilité, ce qui est toujours bien.

La dernière est plus intéressante : on est certes loin du fameux « meilleur programmeur de France », mais il y a beaucoup de monde, et qui semble prêt à y passer beaucoup de temps. Autrement dit, les développeurs sont des gens passionnés, prêts à se confronter les uns aux autres, et à communiquer autour de leurs succès ou de leurs échecs.