Vertx, c’est vachement bien !

J’ai commencé récemment un projet lisant des messages d’AciveMQ pour les écrire dans Kafka. J’aurais pu utiliser Kafka Connect, mais les condtions de licence sont peu favorables au type de déploiement envisagé. Et comme le client m’a laissé libre de la technologie, j’ai choisi de créer une petite application Vertx (parce que ça faisait longtemps que je voulais tester). En fait, j’ai déja utilisé Vertx, mais indirectement, à travers … Wisdom Framework (que j’ai beaucoup aimé utiliser). Donc Vertx c’est bien, parce que c’est simple, rapide asynchrone, tout ça.

Et je voudrais vraiment que vous reteniez ça, parce que je vais rentrer dans les petits détails qui grattent.

D’abord, vertx, comme Wisdom, justement, a un chouette plugin maven pour faire du rechargement à chaud. C’est super efficace pour l’expérience du développeur. Mais, à la différence de Vertx, le plugin ne détecte que les changements dans le code, et pas dans le pom.xml. Il y a un ticket dans GitHub à ce sujet … mais sans avancement visible (et c’est d’autant plus dommage que Clément Escoffier a bossé sur le plugin Wisdom pour maven). C’est un peu gênant, mais moins que l’autre problème …

L’autre problème, c’est celui de la ligne de commande de Windows et sa limite à 8192 caractères, comme je l’ai indiqué sur Github. Le meilleur contournement que j’ai trouvé a été d’utiliser subst (évidement à travers une interface graphique) pour mapper mon repository maven sur le disque virtuel M:.

A part ça, le build se passe assez bien … Du moment qu’on a bien codé.

Et là, les surprises sont nombreuses.

D’abord, il m’a fallu un peu de temps avant de comprendre que, pour utiliser le plugin Maven dans de bonnes conditions, il ne faut pas créer un main(...), mais plutôt un « MainVerticle » dans lequel on met le code d’initialisation avant de mettre le code applicatif dans d’autres verticles que ce MainVerticle charge. C’est un mode de déploiement comme un autre … sauf quand j’ai voulu ajouter les métriques avec micrometer. Parce que micrometer demande à manipuler les VertxOptions. Et ça, ça se fait dans un Launcher comme celui-ci

Comme vous le voyez, l’implémentation n’est pas forcément immédiate, puisqu’il faut surcharger à la fois le main et la méthode de construction des options.

Vertx, c’est bien, mais il n’y a pas d’injection de dépendances … sauf avec vertx-weld. Et j’aime bien CDI, le modèle est bon, il y a des possibilités intéressantes d’événements et de cycle de vie … sauf que parfois, ça coince !

Si vous suivez le fil, vous verrez que Clément me propose une solution. Merci Clément ! Malheureusement, la bonne solution n’est pas celle-là. Sans vraiment rentrer dans les détails, WeldWebVerticle déclare une extension Weld : RouteExtension, laquelle fournit l’injection de l’objet Vertx. Je me suis donc dit qu’il était possible d’étendre WeldWebVerticle pour permettre de rendre le routeur injectable.

Il y a deux problèmes avec ça.

  1. La méthode WeldWebVerticle#createRouter() retourne toujours une nouvelle instance de routeur, ce qui met un sacré bazar.
  2. Pour rendre le routeur injectable, il faut créer un Bean (au sens Weld) produisant cet objet.

Pour le premier point, ça n’est pas compliqué, il suffit de surcharger createRouter pour retourner toujours la même instance. Par contre, pour le second … je ne voulais pas réimplémenter toute l’interface Bean, j’ai donc cherché à réutiliser une implémentation existante, comme VertxBean par exemple. Malheureusement, VertxBean est une classe privée, j’ai donc dû la recopier … et ça, je n’aime pas trop.

Une fois que ces petits soucis de style ont été résolus, développer avec Vertx est vraiment une expérience hautement recommandable. En fait, j’ai l’air de râler, mais j’ai passé un bon moment à développer avec Vertx, et j’espère que ça n’est pas fini !

Publicités

Introduction à JHipster au chtijug

Julien Dubois vient donc nous présenter le framework qu’il a créé …​ JHipster. Spring, c’est un gros paquet d’ingrédients. Spring Boot, c’est la même chose bien packagée. Et JHipster, c’est ce package encore décoré. Autrement dit, c’est un générateur d’application full stack avec Spring Boot en back-end, Angular et React en front-end (mais Vuejs arrive bientôt), et préconfiguré pour être facilement déployable.

A noter que JHipster est 100% open-source sous licence Apache (la meilleure pour le commerce, quand même).

C’est un projet qui réussit : 22 développeurs dans la core team, 500 contributeurs, des conférences, des étoiles GitHub, …​

Pourquoi ?

JHipster fait gagner du temps au démarrage (avec des études qui montrent un gain de 6 semaines au démarrage), au build (comme le projet est très utilisé, le code généré est stable et fiable), et au run (le monitoring est déja prévu).

Stack technique

front-end

JHipster propose une stack complète, avec des front bien connus. Mais attention : tous les widgets de l’univers ne sont pas supportés. Parce que ça fait trop de code d’une part, mais aussi parce que ce sont des technologies qui évoluent très rapidement.

back-end

Evidement, le back est intégralement Spring .. il y a toutefois quelques choix qui sont faits (par exemple, JHipster utilise toujours DropWizard Metrics, parce que la communauté n’est pas fan des nouvelles métriques Spring).

Outillage

Les tests de base sont déja préparés. En revanche, les tests Gatling ou Protactor sont optionnels …​ mais ajoutables facilement.

Par ailleurs, JHipster suppose que vous utilisez Docker …​ parce que c’est pratique et c’est quasiment un standard. Cependant, si vous voulez utilisez MySQL ou Elastic, il faudra en passer par là.

Le déploiement est préparé pour heroku (beaucoup utilisé parce que gratuit), Kubernetes, OpenShift, …​

Architecture

JHipster permet de générer une architecture microservice, en créant un module par service de back-end et un ou plusieurs modules de front-end.

Il peut aussi utiliser une service registry (celle de Netflix ou de Consul sont intégrées) ou une API gateway.

L’objectif étant d’avoir la même expérience développeur que pour un monolithe. Ca paraît séduisant sur le papier …​

Et enfin, il est possible de créer des microservices réactifs utilisant Spring Boot 2 …​ et les composants Spring réactifs, évidement.

Toutefois, sur le sujet des microservices, je me dois d’avouer qu’à mon ens on est déja là dans une démarche dépassée : aujourd’hui, le marché semble converger vers les service meshs (Istio, Consul Connect, ou Conduit). Et franchement, continuer à implémenter la couche de service dans l’application … c’est lourd, c’est peu efficace, et ça rajoute de la complexité au mauvais endroit.

Démo

Et c’est parti pour la démo à la cool !

Installation

On peut installer JHipster avec npm (wat ?), Homebrew, Chocolatey, …​

Mais on peut aussi, comme pour Spring boot (ou Wisdom Framework), créer le projet depuis le site web de JHipster. Donc on se connecte, et grâce à l’intégration GitHub/GitLab, le projet est directement créé dans le gestionnaire de source (comme avec Jenkins-X).

Les options de création de projet sont assez nombreuses (monitoring, authentification, base de données, …​). C’est l’un des intérêts de JHipster : gérer des sujets auxquels Spring Boot ne s’intéresse pas. L’impact évident, c’est que le démarrage est plus lent que Spring Boot …​ qui n’est déja pas fameux sur ce sujet. Parce que JHipster démarre un EHCache, un hibernate, …​

En dehors de ça, JHipster peut gérer l’accès à ElasticSearch, ou Kafka. Dans le cas d’Elastic, ça a un impact sur le front et sur le back (parce que JHipster génère les outils d’accès, et l’interface de recherche). Qui plus est, le docker-compose.yaml sera également généré pour le développement avec Elastic.

Et lorsqu’on clique sur « generate on GitHub », le projet est automatiquement généré avec les bons *ignore.

Ouverture du projet

Donc le projet a été généré avec un pom.xml pour maven, et un package.xml pour générer le front-end. Et au lancement, on démarre node et maven, le front et le back se lance (pour le front, après avoir copié 1/2 Go de dépendances Node). Ca me trouble un chouaï, parce que je ne vois pas pourquoi il faut lancer deux outils de build différents … pour produire les deux morceaux de la même application.

Spring-boot et webpack sont configurés pour faire du rechargement à chaud, ce qui fait qu’il n’y a pas besoin de relancer les outils de build. C’est quelque chose qu’on a aussi dans Spring-Boot, dans Wisdom Framework – mais bien mieux (désolé, mais c’est vrai) ou même en Rust avec cargo watch.

L’application est générée avec de l’authentification, ce qui est cool, mais aussi tout un tas d’écrans d’administration. On peut voir les métriques, les niveaux de logs (et les changer), …​ C’est franchement pratique.

Il y a également un browser-sync, qui permet d’avoir plusieurs clients synchronisés (c’est typiquement pratique pour le développement web avec des écrans réactifs).

Et encore la configuration Docker …​ avec le fichier de configuration permettant l’accès à la base de données.

Et beaucoup de configuration Java, pour optimiser les performances de Jackson, par exemple, ou pour corriger des implémentations pas optimales, ou pour lier le front-end et le back-end. Une librairie équivalente existe évidement côté Javascript, encore une fois pour faciliter la vie du développeur.

Et enfin les bases d’une application : un utilisateur, ses droits, les repository Spring Data, les contrôleurs web permettant d’y accéder, et même les messages internationalisés.

Ecrire du code

JHipster fournit des sous-générateurs. Donc pour créer une nouvelle entité, il suffit de taper jhipster entity Foo par exemple.

JHipster Studio

Avec ce studio, on peut décrire facilement des entités dans un DSL …​ qui ressemble furieusement à un descripteur UML. Ce studio est utilisable dans une interface web, ou via un plugin Eclipse (cool). Et le modèle peut être appliqué sur une application préexistante. L’avantage du studio web, c’est qu’il génère une pull request automatiquement, ce qui est cool. Ce qui l’est moins, c’est que 110 fichiers ont été générés pour 4 entités. 110 fichiers ! C’est pas un peu beaucoup ?

Si, mais il y a quand même un front-end, un back-end, et toute la connexion entre les deux. C’est malgré tout impressionnant.

Même si ça ne marche pas.

Méta-modèle

Julien rencontre à ce moment-là une difficulté qui l’oblige à tout régénérer …​. y compris le code Java. Pour ça, JHipster maintient un méta-modèle avec lequel il génère le code Java et le code Javascript.

Ca ne marche pas mieux après, mais c’est néanmoins impressionnant.

Surtout quand je me rends compte qu’en fait, JHipster est un outil de génération à base de modèle UML. Modèles qui sont bien cachés, c’est vrai, mais qui se révèlent bien pratiques.

Différence dev-prod

Pour aller plus loin, Julien nous montre que l’application, quand elle se lance en mode production, démarre plus lentement, parce que les comportements sont différents :

  • Le code Angular est minifié
  • Les stratégies de cache côté serveur sont plus aggressives
  • Les descripteurs Swagger peuvent (ou pas) être générés

Tests

Sur cette application, 160 tests sont générés pour le back et 152 pour le front. Et c’est là qu’on voit tout ce que cache ce générateur …​. Il y a 5 entités. Et ça mérite 300 tests ? J’imagine que oui, bien sûr.

Mise à jour

La ligne de commande contient une commande jhipster update qui permet de mettre à jour le projet par rapport à une version suivante, via un merge git. C’est conceptuellement vraiment, mais alors vraiment balaize. Et ça facilite évidement énormément les mises à jour de version.

Mise en prod

Pour finir, il existe des sous-générateurs permettant de déployer l’application dans les infrastructures de prod. Julien prend donc l’exemple de jhipster heroku …​ qui déploie chez heroku (dingue).

Et les différents cloudeurs ont produit des optimisations spécifiques. Par exemple, Google App Engine déploie le front-end sur son CDN, et les requêtes qui arrivent sur votre « serveur » sont celles qui concernent le back uniquement.

Conclusion

Julien vend particulièrement bien son outil. Et honnêtement, il a raison : JHipster est bien fichu, et fournit un certain nombre de services spectaculairement pratiques. L’un des plus importants, mais des moins visibles, est la mise en place d’une approche MDA pragmatique et efficace. En revanche, ayant utilisé pendant un certain temps Wisdom Framework, je trouve le choix de se baser sur Spring pour construire un générateur d’applications magnifié me paraît …​ discutable : je trouve la quantité d’éléments générés hallucinante, pour un résultat qui est raisonnablement joli, mais pas non plus fascinant (typiquement, les démos de Julien n’ont montré que Twitter Bootstrap (aucun des éléments visuels ne nécessitait Angular ou React). Malheureusement, je pense qu’il est de moins en moins pertinent de « gâcher » du CPU en fournissant un échafaudage de projet – aussi poussé soit-il. En effet, on voit maintenant émerger des approches – type « Function as a service » – qui interdisent totalement ce genre d’outillage.

Alors certes ça n’est pas la cible.

Mais quand même. Quelle application déployer avec JHipster ? Un outil « léger » non critique ? Quel genre d’outil peut encore avoir besoin de la « puissance » de la JVM, en étant toutefois assez simple pour être développable en PHP ?

Autrement dit, simplifier Spring, pour moi, est une « solution » qui ne me paraît pas, malgré tous les exemples de mise en oeuvre présentés – avec brio, je dois dire – par Julien, être applicable à un cas « utile ». Quelque part, c’est avant tout un outil de commodisation du développement, applicable dans les cas où le logiciel ne doit pas être un obstacle à une tâche, mais ne doit pas non plus être un facteur de succès.

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).

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.