06 novembre 2015

Learning Go (from Java)

Ca fait un moment que je me dis qu'il faut que j'apprenne à coder en Go.


Pourquoi ?

Déjà, pourquoi apprendre un autre langage ? 15 ans à faire du Java pour en comprendre les rouages et l'écosystème, pourquoi changer ? J'ai par exemple suivi le cours Scala sur coursera, qui m'a permis plus de comprendre les concepts fonctionnels que d'apprendre à faire du Scala, que je n'ai jamais pratiqué ensuite. Et voilà que Java 8 vient saupoudrer un peu de fonctionnel dans un langage objet impératif. Donc oui, connaître un autre langage aide à mieux appréhender les nouveautés et les différences entre langages, et donc ce qui et bien ou ce qui manque à notre langage de prédilection.

Pourquoi Go ? Go est à la mode, surtout depuis que le monde sait que Docker est fait avec. Mais ce n'est pas qu'un effet de mode. Go est un langage assez bas niveau, sans pour autant être aussi pénible que du C. Typiquement, il a un garbage collector. Il mixe donc des concepts plutôt bas niveau avec une approche moderne. Il n'a pas d'héritage mais uniquement de la composition et des interfaces. Go est donc un langage très opinionated et c'est ça qui m'intéresse. Les choix qui sont fait dans Go imposent une façon de concevoir le code, et c'est découvrir ce que cela implique qui m'intéresse.

Par contre, pour être significatif il faut avoir un projet sous le coude. Professionnellement ce n'est pas évident. Si on parle de Jenkins 2.0 il n'est pas pour le moment question de tout recoder en Go. Du coup j'ai pris un petit projet, le backend du système de vote du BreizhCamp, soit un serveur REST avec 5 endpoints. Le recoder en Go est un exercice concret sans nécessiter des jours de travail pour avoir un résultat.

Premiers pas

Commencez bien sur par "a tour of go", le tuto officiel de Go. Ca donne les bases et de premier petits exercices pour se faire à la syntaxe. 

Bon alors soyons honnête, quand on vient du monde Java, l'éditeur de texte vi on se sent un peu à poil avec. Donc pour la suite prenez tout de suite IDEA avec le plugin Go, ça vous simplifiera la vie et ça vous évitera des prises de tête.

Niveau littérature, il existe aussi plusieurs bouquins - dont des gratuits - qui sont très bien.

Première chose à noter dans Go, le langage vient avec un outillage assez complet : gestion de dépendance (même si elle est naïve), build, test, packaging. Pas besoin donc de réfléchir à quel outil de build utiliser. Ca permet de démarrer vite et de prendre pied rapidement dans un projet existant.

Deuxième chose que je note, Go se débarrasse de nombreux problèmes de formatage. D'une part gofmt gère l'indentation du code pour vous (et de manière intelligente), ensuite le compilateur ne vous laisse pas le choix et refuse certaines mises en formes (comme mettre l'accolade sur la ligne suivante). Les structures sont aussi prévues pour éviter les délimiteurs qui font merder vos commits : typiquement, en Java, quand on ajoute un élément à une liste, on modifie la ligne d'au dessus pour y ajouter un ','. En Go, soit il n'y a pas de délimiteur de fin de ligne, soit ce caractère doit être présent sur chaque ligne, même la dernière, supprimant ce "défaut". 

text := LinesOfText{
 []byte("Now is the time"),
 []byte("for all good gophers"),
 []byte("to bring some fun to the party."),
}

Le compilo fait aussi de l'inférence de type et tout ce genre de choses qui font de Go un langage peu verbeux comparé à Java, sans pour autant être un langage dynamique : la compilation vous rappellera à l'ordre, pas besoin d'attendre un crash en production. Et du coup IDEA vous propose également la complétion du code, et ça moi j'aime.

Enfin, la compilation Go est super rapide. Un cas d'usage très simple de Go, c'est pour le scripting : la compilation est tellement rapide qu'on peut écrire ses scripts en go et les lancer avec un go run truc.go. Ca permet de s'y mettre tranquillement, et potentiellement ce code pourra être regroupé et compilé dans un binaire portable qui vous servira de SDK interne.

Le compilo TGV c'est entre autre la raison pour laquelle la gestion des dépendances repose sur un bête git clone, recompillant donc tout à chaque fois (comme en Erlang d'ailleurs). Ca marche bien, mais on récupère donc le HEAD de chaque dépendance, pas une version bien identifiée. En termes Maven, tout est en SNAPSHOT :) On trouve donc des outils alternatifs, mais bon pour démarrer ce n'est pas un gros soucis, au pire vous aurez l'impression de faire du NodeJS... Go 1.6 devrait gérer des objets binaire par package, ce qui ouvre la voie pour une gestion un peu plus stricte.

Au fil des heures

Premier projet en Go pour moi donc, et j'ai pu plier ça en deux soirées, content du résultat. Les librairies standard de go sont bien foutues, ce qui me fait constater que le plus gros du travail d'un développeur Java est de trouver la lib qui marchera là ou le standard java.xx.yy est juste bon à jeter. Qui utilise java.util.logging - à part Jenkins, mais ça compte pas ? Ne soyons pas aveugle, c'est l'avantage d'un langage jeune, et avec le temps les même travers risquent d'apparaître. Cependant, le principe de Go - produire un binaire unique et autonome - permet de dégager du code obsolète, ce que tente de faire JigSaw depuis des années.

Bref, après quelques heures on a un truc qui fonctionne, on a donc atteint le niveau "je suis ok avec la syntaxe et les API de base". Parce que la syntaxe est pas complètement naturelle pour un dev Java, entre autre je bute encore régulièrement sur l'inversion de l'orde paramètre : type

J'ai donc un truc qui marche.

J'ai ensuite repris mon code morceau par morceau en utilisant des libs plus adaptées (drone/routes par exemple pour les services REST plutôt que du net/http standard). Il n'y a pas encore pléthore de librairies, tout en ayant du choix, c'est donc le bon moment pour s'y mettre sans se noyer dans la masse.

Là on découvre l'écosystème, on regarde le code des librairies qui nous apprennent plein de trucs qu'on ne connaissait pas. Clairement, une fois la barrière syntaxique franchie, lire le code des autres est un excellent moyen d'appréhender le langage. Au final mon code fait rapidement un "divisé par deux" et au passage plusieurs reprises structurelles pour corriger des erreurs de jeunesse. Autrement dis, je fais des progrès.



Enfin, j'ai voulu faire mon David Gageot en écrivant des tests (oui, le vrai David écrit ses tests en premier, mais bon hein ho). Là aussi, on a peu de librairies disponibles mais tout de même de quoi rendre les choses sympathiques.

Tester, c'est ici surtout l'occasion de découvrir les patterns Go pour rendre le code testable, dans un contexte ou nous n'avons ni programmation orientée objet, ni injection de dépendance, ni librairie de mock dynamiques. Et bien ça se fait plutôt bien :P Là la littérature disponible et divers blogs m'ont bien aidé pour expérimenter et comprendre les approches possibles. Et là ça fait du bien d'avoir un peu de bouteille parce qu'on reconnait tout de suite certains patterns d'autres langages/frameworks, et on connait leurs limites, donc on peut comparer/confirmer/se faire rapidement une opinion.



Objectif atteint donc à 100%
Note: oui, je met des images de beauf sur ce billet, vous plaignez pas j'aurais aussi bien pu poser en bikini.

Et donc ?

Bref, passer à Go n'a pas été très difficile, bien moins que mes heures de Scala, en rien à voir avec mes déprimantes tentatives en Ruby (sic). Evidemment j'ai un niveau Padawan 101, mais c'est déjà intéressant et ça m'encourage à pratiquer plus.

Vous l'aurez compris, si vous avez quelques heures de libre, je vous conseille de vous y mettre !









1 commentaires:

Unknown a dit…

Il manque quand meme cruellement : les génériques (que j'ai personnellement compensé avec de la génération de code via github.com/cheekybits/genny), un bon debugger (je bricole avec github.com/mailgun/godebug), une solution pour le versionning des packages (google n'a pas ce genre de pb vu qu'ils sont tjrs sur master si j'ai bien compris, mais nous autres pauvre mortel...).
Apres je regrette quelque lourdeur et incoherance dans la syntaxe. Le coup du mot clé "range" pour itérer sur les slice/map, j'avoue que j'ai eu du mal a m'y habituer. Pas d'opérateur ternaire... pas notion d'immutabilité, la declaration de fonction anonyme un peu lourde etc. Bref... ;)

Sinon il y a de bons cotés, comme la vitesse de compilation et de bootstrap qui du coup facilitent le feedback immédiat pour le dev et les tests.