22 février 2020

La JVM, Java, ses haters, son écosystème

Comme beaucoup de monde, j’ai eu l’occasion de travailler professionnellement avec Java (et je continue de le faire ponctuellement aujourd’hui). Dans cet article, je parlerais de la JVM, de Java, et de la (mauvaise) réputation que ce langage se traîne. Mais cette dernière est-elle vraiment justifiée ?

La JVM

Je pense que la JVM est probablement la plateforme la plus mature pour développer des applications aujourd’hui. Elle a en effet plusieurs avantages :

  • De très très bonnes performances.

  • Un écosystème énorme. Il existe des clients écrits en Java pour à peu près toutes les bases de données, broker de messages etc…​ disponibles sur le marché. Ces clients sont de plus généralement des clients officiels maintenus par les développeurs du produit. La librairie standard est également très riche.

  • De nombreux langages disponibles (Java, Clojure, Scala, Kotlin, Groovy…​), tous bénéficiant de l’écosystème et des performances de la JVM.

  • Il est très facile aujourd’hui de déployer des applications sur la JVM. Généralement installer la JVM puis lancer un java -jar app.jar suffit, des outils comme jlink permettent même d’optimiser cela.

  • De nombreuses innovations apparaissent sur la JVM ou dans son écosystème, comme par exemple GraalVM, les différents types de garbage collector etc…​

Je développe depuis plusieurs années sur la JVM (en Clojure majoritairement aujourd’hui), et j’en ai toujours été très content.

Et pourtant…​ quand je présente un de mes projets open source écrit en Clojure, j’ai régulièrement droit à des :

  • Pourquoi ça tourne sur la JVM ?

  • Jamais je ne déployerais des application sur la JVM de nouveau, c’est l’enfer à gérer !

  • Mon expérience de la JVM a toujours été désastreuse !

  • Tourner sur la JVM n’est pas un avantage.

  • …​

C’est aussi quelque chose qu’on voit souvent sur les réseaux sociaux. La JVM serait lente, consommerait beaucoup de RAM, les applications seraient difficiles à maintenir…​
Pourtant, comme dit précédemment, il est facile de faire des applications très performantes consommant quelques centaines de Mo de RAM. On voit d’ailleurs bien que la JVM est performante vu la quantité d’outils "Big Data" tournant dessus.

Mais alors, comment en est-on arrivé là ?

Le langage Java

Certains diront que la faute revient à Java. Ce langage serait lourd, verbeux, et ce serait un calvaire de développer des applications en Java.

C’est vrai, Java a des inconvénients mais ce n’est pas le pire langage du monde. De plus, le langage s’améliore, certes lentement mais ça va dans le bon sens.

Le langage n’a pas un système de type digne des langages fortements typés comme OCaml ou Rust, le "tout objet" du langage est parfois lourd, l’immuabilité n’est pas vraiment encouragé, la programmation fonctionnelle est limitée…​ Mais on peut largement travailler avec.

D’ailleurs, la critique de Java est encore plus savoureuse quand elle vient de développeurs Golang (je développe aussi en Golang, vous pouvez retrouver mon avis sur le langage ici). Java n’a rien à envier à Golang en terme de fonctionnalité.

Mais alors, quel est le problème ?

Les serveurs d’applications

Une génération complète de développeurs et de sysadmin ont été traumatisé par les Glassfish, JBoss et autres WebSphere.

J’ai eu la chance de commencer ma carrière vers la fin de leurs utilisations (bon il y en a toujours qui tournent en prod aujourd’hui mais ça se voit moins on va dire), et en effet j’ai quelques souvenirs de fichiers domain.xml bien violents (le tout branché sur de l’Oracle sur AIX histoire de bien faire les choses).

Mais comme dit précédemment, aujourd’hui un java -jar suffit généralement pour lancer une application. Mais cette époque a fait du dégât, généralement les remarques du type "OMG Java en prod plus jamais!" viennent de gens ayant connu cette époque.

Les frameworks Java

Pour moi, la mauvaise réputation de Java aujourd’hui vient principalement de ses frameworks et non du langage, j’insiste sur ce point.
Il y a une culture de l’usine à gaz difficile à comprendre dans la communauté Java, et c’est quelque chose que je n’ai jamais vu dans d’autres langages ou communautés. L’expression "pourquoi faire simple quand on peut faire compliqué" prend ici tout son sens. L’exemple type de cette culture étant Spring.

C’est quoi le problème avec Spring ?

J’ai eu l’occasion de travailler professionnellement sur des projets Spring dans plusieurs contextes. Je parle ici de projets avec du Spring "modernes": Spring Boot, Spring Cloud, "microservices"…​ Et c’était à peu près ma réaction quand j’arrivais sur les projets:

what the fuck

On me disait : "tu verras, c’est des stacks modernes".
Quel naïf j’étais ! Essayons de résumer un projet Java/Spring comme ils sont faits aujourd’hui.

Génération du projet

Les développeurs adorent générer les projets via des archetypes Maven, ou si vous n’avez pas de chance avec JHipster.
Vous n’avez pas commencé à coder, mais vous avez déjà 25 classes et un pom.xml de 2000 lignes. Soyons franc, la seule chose à faire après ça devrait être rm -rf * et repartir sur des bases saines, mais bon, c’est comme ça qu’il faut faire il paraît.

Annotation

tout doit être annoté

Le projet devient un jeu où le but est simple, le mot-clé new est interdit. Plus il y a de magie, plus il y a d’annotations, mieux c’est.

Métriques ? Annotations.
Mocks ? Annotations.
Dépendances ? Annotations.
Getters ? Annotation (coucou lombok).
Routes HTTP ? Annotations.
Cache de la DB ? Annotation.
Mapping entre objets ? Annotations.

Je pourrais continuer comme ça longtemps…​

La plupart des projets pourraient avoir une class Main qui serait plus ou moins :

Cache cache = new cache(cacheConfig);
Database database = new Database(databaseConfig);
HTTPServer server = new HTTPServer(cache, database);

database et cache implémentent une interface (pour écrire facilement un mock).
Bien sûr, comme dit précédemment utiliser new est interdit, donc vous ne verrez jamais ça. J’ai eu des discussions hallucinantes où les devs ne pouvaient même pas envisager ne pas utiliser @Inject, comme si on leur demandait de coder avec ed à la place de leur IDE.

On préfère avoir une mega usine à gaz qui explose au runtime. Bah oui, c’est super utile d’avoir du typage statique quand derrière on a du bean not found au démarrage du projet (démarrage qui met 2 plombes au passage généralement).

Utilisez les libs les plus bloated de l’écosystème

Rule 34: IF IT EXISTS, THERE IS A SPRING STARTER FOR IT.

Vous ne pensiez quand même pas pouvoir utiliser les clients officiels Kafka, Elasticsearch etc…​ quand même ?

Non, il faut utiliser Spring Kafka, Spring Elasticsearch, Spring Redis…​ ces libs cachent les implémentations officiels, sont très peu flexibles, et sont surtout totalement inutiles. Bah oui, faire KafkaConsumer consumer = new KafkaConsumer(config) c’est trop compliqué. Mais bon, le but de ces frameworks est de cocher des cases, pour pouvoir dire "Oui c’est supporté !!" dès qu’une techno est mentionnée en réunion.

JPA, AspectJ, Hibernate…​ Il y aurait tellement de choses à dire sur ces technos sorties de l’enfer.

Spring Data ? True story, j’arrive sur un projet : "gnagna microservices, gnagna stack moderne, gnagna on est le meilleur projet de la ville…​" j’ouvre un fichier et je tombe sur une tonne de fonctions ressemblant à :

findByLibelleIgnoreCaseContainingOrOuvrageIgnoreCaseContainingOrCreatedByIgnoreCaseContaining

WTF ? C’est un vrai nom de fonction qui était dans le projet, hein, et que j’ai conservé précieusement pour la ressortir. C’est un peu à ce moment où je me suis dis que je devais soit repasser sysadmin, soit trouver une boîte qui ne fait pas du Java (j’ai fait l’un puis l’autre d’ailleurs). Et ces fonctions ne choquaient PERSONNE sur le projet, c’était "business as usual".

Design pattern

Une classe == une interface, c’est la règle ! Chaque requête HTTP faisant un accès à une base de données devra aussi au moins créer :

  • un DAO,

  • un DTO,

  • un VO.

Pourquoi ? Car ça fait 20 ans qu’on cargo cult le Gang of Four.

Hiérarchie de pom complexe

Avec les starters Spring et compagnie, votre pom.xml fait maintenant 3500 lignes. Sauf qu’en fait le pom a aussi un pom "parent", un "BOM"…​ chaque montée de version d’un de ces trucs vous coûtera des jours de travail.

Montées de versions

D’ailleurs, parlons en des montées de versions. Chaque montée de version de Spring et de ses starters amènera son lot de bugs bien louches, et beaucoup de projets/libs n’ont même pas de changelog donc amusez-vous bien lors du débugging.

Faites ch*er les ops au maximum

La stack Spring Cloud Netflix (Ribbon & co) est un enfer.

Votre projet est maintenant un load balancer bugué, pas flexible, exposant très difficilement ses métriques. C’est toujours cool quand on on se rend compte que N % des requêtes partent dans les limbes sans avoir aucune métrique et aucun moyen de débuguer le truc.

De toute façon, personne ne comprend comment ça marche

J’ai travaillé avec de très bons développeurs Java, certains "connus". Des devs qui en font "depuis 199X". Des gens qui font du Spring depuis le tout début. Même eux ne pourront pas vous aider, le framework est juste trop complexe. Et quand ton annotation marche pas car un truc rentre en conflit avec un autre, va trouver la source du problème…​

Je me rappelle d’une montée de version de Spring où mon CTO de l’époque avait tweeté : "Quelle douleur les montées de version Spring et ces dépendances. A la limite de l’abandon mais toujours en cours". Oui, les montées de versions étaient dingues, de mémoire celle là commençait à se compter en mois.

Mais pourtant…​

Malgré cette complexité ENORME, il ne faut surtout pas changer ! Incompréhensible.

Tu passes 2 semaines sur un bug Ribbon, tu mets 1 mois à monter de version Spring, tu mets 3 jours à monter de version ton pom parent, ton "microservice" avec 10 endpoints REST a 60 classes, 10 000 lignes de code et a un pom de 3000 lignes…​ Mais tout le monde est content et continue dans sa lancée !

Sur un des projets où je suis intervenu, quelques développeurs (généralement ceux qui avaient codés avec autre chose que Java dans leurs carrières) voulaient lancer un microservice avec Vert.x pour tester (certains l’avaient déjà utilisés en prod, d’ailleurs j’en ai également entendu de bons retours).
Bien sûr les autres développeurs étaient contre (fallait faire du microservice mais avec Spring), et d’ailleurs les décideurs étaient aussi contre car : "Quand je demande à la SSII du coin des développeurs Spring ils en ont en stock". OK.

Un nouvel espoir ?

Mais de nouveaux frameworks Java apparaissent, amenant simplicité avec eux…​ Ah non, on me souffle dans l’oreillette que les Quarkus, Micronaut & co vous feront aussi faire de l’annotation driven development.

Et c’est pourquoi j’ai arrêté de coder en Java.

Tags: programming
Top of page