Les furets sont au chtijug !

Tout ce qui est présenté est disponible sur GitHub, avec d’autres choses …

Donc, les furets font de l’assurance. Et ils ont fait d’abord différents sites au look … discutable.

En terme de code, actuellement, il y a 450K lignes de code, et un déploiement en prod par jour. Ca change pas mal des clients avec un déploiement par semestre …

DomainModel.stream()

A quoi ça sert

La classe Person, chez eux, est … grosse, et riche d’héritage. Normal pour un projet qui a 7 ans. Et évidement, ce modèle circule dans l’entreprise, du client Javascript à la base backoffice qui reprend les données sous des formats différents.

Evidement, comme c’est compliqué, ils ont cherché à simplifier tout ça, en utilisant globalement des domaines orientés colonne :

  • clé/valeur dans le client
  • modèle colonnes dans Cassandra
  • Vecteurs de données dans la base analytics

Du coup, utiliser un dictionnaire de clé partout devrait simplifier les choses.

Pour la suite, on va travailler sur un modèle simplifié.

Premier souci : comment ça se passe quand on a des relations 1-n ? A priori, « ca marche » … personnellement, je n’y crois pas trop, mais admettons.

Il y a aussi le souci des champs accessibles par plusieurs chemin, qui cette fois-ci sont dupliqués dans les données disponibles.

Et passons tout de suite au livecoding.

Manipulation simple du modèle

Donc, on a d’un côté notre modèle objet traditionnel, et de l’autre, grâce à un wrapper généré par l’outil des furets. Dans ce wrapper orienté clé/valeur, écrire des données se fait avec un simple

SampleModelWrapper.set(FieldId, value)

lire des données se fait simplement avec un

SampleModelWrapper.get(FieldId)

Et en terme d’implémentation, ça se fait facilement en générant des lambdas (évidement, avant Java8, ça aurait marché tout aussi bien avec des classes internes, mais bon, les lambdas sont à la mode). Et, en plus, l’inférence de type semble assez faible, puisque le type contenu dans le FieldId ne suffit pas à contraindre suffisament le type de sortie.

Manipulation avec des streams

Donc évidement, faire SampleModelWrapper#stream() retourne toutes les valeurs du modèle. Et on peut transformer les valeurs en map en traversant le même stream. Pratique, mais pas exceptionnel. Même si le speaker n’est pas de cet avis. Et en un sens, je le comprend, puisque le mapping est traditionnellement un problème compliqué : regardez Jackson/JAXB et autres qui galèrent pour bien faire les mêmes choses.

Petit bonus : on peut filtrer les éléments du wrapper selon différents tags, qui sont définis librement dans le wrapper. De cette manière, par exemple, on peut séparer les données du User et de l’Account du modèle d’exemple. Evidement, ça peut aussi servir à affiner l’accès à certaines interfaces selon les droits des utilisateurs.

A noter que le site des furets a environ 1000 clés dans les différents enums défnissant les modèles utilisés.

Introspection

Typiquement, quand on veut mettre les données dans une base Cassandra ou autre, il faut d’abord créer les colonnes typées associées, pour lesquelles on va naviguer le modèle, et pour chaque élément du modèle, on va créer une colonne dont le type sera obtenu grâce au FieldInfo correspondant à chaque champ.

Evidement, pour moi, il est assez curieux de voir des gens réinventer des notions analogues aux BeanInfo / PropertyDescriptor disponible dans le vieux monde des JavaBeans. Mais dans l’ensemble, ça fait le job assez (peut-être trop, en fait) simplement. Trop simplement, parce que par exemple, les annotations portées par le modèle initial ne sont pas transportées à travers le wrapper.

Conclusion partielle

Je comprend tout à fait l’intérêt de mapper un modèle de beans vers des séries de colonnes. En revanche, l’implémentation, certes joliment Java8, manque quand même de certains aspects clés : la gestion des collections fait vraiment peur, l’introspection est fichtrement limitée.

Cela dit, ça doit clairement bien limiter la complexité de la sérialisation, de la création d’interface graphique, ou même de persistence (non relationnelle, évidement, parce que sinon, c’est évidement merdique).

Il y a en fait là-dedans une idée très intéressante qui a sans doute un rapport certain avec le NoSQL. je m’explique. Lorsqu’on codait il y a dix/quinze ans avec une base de données relationnelle, avoir du code proprement décomposé était une bonne idée. Maintenant que la plupart des outils de stockage sont dénormalisés, à typage faible, bref, merdiques en terme de support des liens sémantiques, avoir un modèle riche est presque une gêne. Et c’est dans ce cadre que ce système de wrapper de modèle présente un intérêt.

Continuous delivery

Bon, si vous ne savez pas ce qu’est le continous delivery, c’est assez simple : c’est l’idée de pouvoir envoyer du code en prod au plus tôt. Et actuellement, les furets font plusieurs mises en prod par jour.

Historique

D’une façon amusante, en 2012, ils faisaient une MEP par mois, parce que leurs sprints SCRUM duraient un mois. Les tests étaient joués en fin de sprint et pouvaient entraîner des retards de MEP du fait de tests à corriger. Pas très pratique …

En 2013, ils ont tenté d’accélérer les choses avec des sprints « bonus » d’une semaine. Le premier impact est que le temps de build est passé de 15 mn à 3 mn.

En 2014/15, passage à Kanban avec une release chaque jour : les fonctionnalités sont marquées finies dans Kanban quand elles sont en prod. L’impact, évidement, c’est que la release est passée d’une journée entière à 2/3 heures. L’autre impact, c’est que les tests ont tous été automatisés. Histoire de se rassurer, les fonctionnalités passent quand même dans un environnement de staging pendant une journée pour vérifier que tout va bien.

Pour le futur, l’objectif est de livrer encore plus vite : les fonctionnalités devraient apparaître presque une par une en prod.

Curieusement, les MEP apportent toujours autant de code, voire même plus, parce qu’il y a beaucoup plus de fonctionnalités dedans.

Quelques aspects techniques

En-dehors des classiques environnements, il y a quelques points notables.

Les tests sont joués en parallèle sur un grid selenium, qui tourne sur une machine avec un RAMFS (128 Go de RAM chez OVH pour 300 €/mois).

Ils développé Zeno qui va sur une URL pour faire une capture d’écran. Et qui fait la différence entre la prod et la préprod. De cette façon, on peut vérifier que les différences visibles sont bien liées aux fonctionnalités développées (et pas à des bugs, par exemple). Cet outil pourrait par exemple être utilisé pour faire de la veille concurrentielle.

Evidement, le développement se fait en mode blue/green. Avec un double cluster.

Continuous delivery en détail

Il y a en gros 3 modèles de développement

  • trunk based : tout ce qui est dans le trunk part en prod. Ca implique le feature flipping qui est du code en trop.
  • feature branching : tout les dévs sont fait dans des branches, qui sont réintégrées dans le trunk au moment de la MEP. Ca pose évidement des problèmes d’intégration continue.
  • Et pour finir, le modèle github où le code est développé dans des branches, et réintégré dans le trunk via des pull requests, qui passeront dans l’intégration continue.

La grande astuce des furets pour simplifier tout ça est de tester les merge rapidement. Grâce à ce qu’ils appellent le continuous merge : lorsqu’un développeur a fini un développement, il est poussé sur un repository qui contient toutes les nouvelles features branches, et qui tente de les merger et de déployer le code sur l’environnement de staging.

git octopus

Chez eux, les branches en cours de dev s’appellent studies/ et celles qui sont finies s’appellent features/. Pour merger tout ça, ils ont un script appelé git octopus disponible sur github. Le truc magique, c’est qu’il peut détecter les conflits entre les branches à merger avant le merge final.

Pour résoudre les problèmes qui arrivent … ben là, essentiellement, c’est du git-fu (et vous savez ce que j’en pense). En fait, c’est pas vrai : tout ce qui est dit est également valable avec Subversion … et j’ai bien l’impression que c’est ce que gigomerge (mince, j’aurais bien mis le lien, mais je ne le retrouve plus) permet d’une certaine façon.

Pour faciliter la gestion des conflits, ils ont également créé un outil de gestion des conflits … qui en fait se contente de créer un workflow d’escalade des exceptions.

Conclusion

Deux présentations très intéressantes, faites par des gens qui se soucient manifestement beaucoup de ce qu’ils livrent, et de la façon dont ils le livrent. Il y a là-dedans des choses vraiment très intéressantes (git octopus, conceptuellement, est très bien, et cette histoire de repository définissant le niveau de maturité d’une feature est également très chouette) D’autres sont en revanche plus discutable (la résolution de conflit me paraît plus de l’ordre de l’astuce que de la solution vraiment propre).

Mais surtout, ce qui apparaît, c’est que, contrairement à bien des boîtes, ils ont l’ambition de se voir comme une boîte de développement logiciel, ce qui implique de produire de la qualité et de la visibilité. C’est sans doute ce qui leur permet de recruter des pointures, puisque nos deux intervenants paraissaient particulièrement compétents.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s