Dans cet article, je vous propose de découvrir ce qu’est un monolithe, ainsi que l’organisation d’une entreprise avec plus de 300 développeurs. Au moment où j’écris ces lignes, nous sommes presque 500 personnes, dans le département « Tech&Product », derrière la conception du logiciel et des services opérés par Doctolib.
Qu’est-ce qu’un monolithe ?
On désigne par monolithe, un logiciel informatique unique, qui est capable de proposer plusieurs services, dans plusieurs langues et pour différents utilisateurs finaux. Concernant Doctolib, il existe le site grand public (www.doctolib.fr), ainsi que le site pour les professionnels de la santé (pro.doctolib.fr), et d’autres sites secondaires, qui permettent par exemple l’inter-connexion avec les systèmes hospitaliers. Sachez que l’ensemble de ces sites est servi par un seul logiciel, installé sur des milliers de serveurs. Selon le traffic, il est ainsi possible d’équilibrer les requêtes webs entrantes. C’est une seule unité à déployer, ce qui est un avantage et qui permet par exemple, chez Doctolib, de faire des mises en production trois fois par jour.
Lorsque votre service web dépend de plusieurs services, il est alors nécessaire d’assurer une coordination et de ne rien casser. A contrario ici, on déploie une seule grosse unité. Puis le système active telle ou telle partie du code, pour servir le site public ou le site professionnel.
Un monolithe fait appel à une seule technologie et un seul langage de programmation pour la partie côté serveur (ou le « backend »). En 2023, chez Doctolib c’est du Ruby avec Rails. Le monolithe embarque plusieurs parties pour pouvoir interagir avec votre navigateur web, mais aussi avec votre téléphone mobile. Le même monolithe est capable de servir plusieurs types de périphérique, avec un seul « backend ». Nous utilisons pour cela React avec TypeScript (et encore pas mal de JavaScript).
Les points forts
En premier lieu et c’est vraiment le point fort : c’est simple. Que ce soit lorsque vous développez, lorsque vous devez mettre à jour ou lorsque vous opérez un monolithe : c’est vraiment (VRAIMENT) plus simple que de coordonner plusieurs serveurs.
Prenez par exemple le suivi de la performance. Il n’y a pas de trace à coordonner entre plusieurs services, et donc l’utilisation d’un APM (Application Performance Monitoring) est plus simple. La mise à jour aussi est facile. Il suffit que les équipes se coordonnent pour livrer le code, vous prenez une photo, vous la testez, et vous pouvez alors déployer sans risque.
Doctolib fait 3 mises en production par jour. Pour cela, un train part à 11h, à 14h et à 17h. Si vous voulez livrer une fonctionnalité en production, elle doit être prête et testée avant le départ presque automatique en production. Nous utilisons aussi beaucoup le principe des « feature switches/feature flags » pour activer/désactiver une nouvelle fonction. La mise en production du code est dissociée de son activation pour les utilisateurs finaux.
Vous pouvez aussi revenir en arrière (rollback) lorsqu’une version ne fonctionne pas correctement.
Techniquement, il y a une seule stack technique à connaître. Vous pouvez intervenir sur l’ensemble du logiciel (en théorie). Un autre avantage aussi, c’est la possibilité de mettre à jour les librairies et les frameworks utilisés par le logiciel très facilement. Vous pouvez ainsi utiliser la dernière version du langage, et les dernières librairies sans aucuns problèmes.
Par sa caractéristique, le monolithe est forcément sans état du côté serveur. Trois requêtes webs pourront être traitées par la même machine ou par trois machines différentes, cela n’a aucune incidence sur l’expérience utilisateur.
Lors de la vague de la vaccination lié à la COVID-19, nous avons pu aussi géré sans trop de problèmes la charge énorme de visiteurs. Le 12 juillet 2021, pendant 3 heures, la plate-forme a tourné avec environ 2500 machines, des dizaines de milliers de Pods, le tout pour servir presque 10 millions de requêtes par minute en pic. Plus de 926 000 rendez-vous réservés pour la vaccination en moins de 3h, et 1.4 millions le lendemain matin… Ce soir-là, la simplicité du moteur a très certainement évité que le site ne tombe en panne. Donc un monolithe peut très bien tenir la charge, et il serait incorrect d’affirmer le contraire.
Je reviendrai plus loin sur les inconvénients ou les risques, restez jusqu’à la fin.
La vie d’un Monolithe lorsque l’entreprise croît fortement
C’est une approche qui tient dans le temps, puisqu’elle est aujourd’hui encore au coeur de l’architecture de Doctolib. D’autres entreprises autour de Rails fonctionnent sur le même principe. Aircall ou Shopify par exemple sont 2 entreprises qui ont un monolithe, et des services secondaires. Chez Aircall, il y a environ 150 ingénieurs, et une équipe dédiée à travailler autour du monolithe. Chez Shopify, le nombre de développeurs n’est pas publié, mais il y a 10 000 employés (contre 3000 chez Doctolib fin 2022). Il y a une centaine d’équipes, et toujours un « core » chez Shopify, même une dizaine d’années après le lancement.
Pourquoi je parle de « nombre de développeurs » ? Car l’architecture d’un logiciel est fortement lié aussi à l’organisation de l’entreprise (Loi de Conway)
« les organisations qui conçoivent des systèmes […]tendent inévitablement à produire des designs qui sont des copies de la structure de communication de leur organisation. »
Un monolithe peut donc être développé par 5, 10 ou 300 personnes.
Cela fonctionne avec une organisation en Feature Team. Vous pouvez organiser par produit, puis ensuite décliner par fonctionnalités pour chaque client, en y associant un Product Manager par exemple. Avec presque 77 features teams pour ce qui est de Doctolib, l’organisation coordonne ensuite finalement assez peu le travail entre les équipes.
C’est un point qui présente un avantage compétitif pour développer plus rapidement. Dès lors que chaque équipe est presque autonome de A à Z sur le développement d’une fonctionnalité, elle peut avancer sans devoir attendre une autre équipe. C’est donc une excellente tactique pour développer rapidement, et ne pas payer de surcoût de synchronisation très élevé. Il n’y a pas d’équipe « front-end » et d’équipe « backend ». Une équipe de 6 à 8 personnes est constituée avec toutes les spécialités. Et donc, elle est autonome pour pouvoir construire ou améliorer une fonctionnalité.
Le monolithe simplifie aussi l’intégration et l’assurance qualité. Lorsque l’équipe a terminé son développement, une série de tests passe sur son code. Puis ensuite, plus de 40 000 tests « end-to-end » passent et valident la modification. A chaque modification, nous passons une recette complète. Elle prend à peine 20 minutes. Et elle couvre l’ensemble du logiciel.
Les « axes d’améliorations » du monolithe
Les coûts de tests et de construction augmentent
Lorsque le nombre d’équipe a commencé à fortement augmenter, nous avons vu que le système fonctionnait… mais qu’il avait un coût de construction important.
Vous passez de 200 à 300 développeurs en un an. Vous passez de 40 à 77 features teams. Quelques signaux faibles commencent à remonter à la surface. L’intégration continue d’abord commence à montrer quelques signes de fatigue. Enfin elle va bien… mais elle coûte un bras. Chaque batterie de test est lancé sur 1000 machines en parallèle. 300 développeurs, une centaine de Pull-request mergées chaque jour, des mises à jour rapide et désynchronisée… Tout cela commence à couter cher.
En janvier 2022, nous calculons le prix unitaire pour faire passer les quelques 40 000 tests sur un commit.. et nous communiquons ce prix à l’ensemble des développeurs. Peu importe le montant, c’est le fait que ce prix augmente fortement et rapidement qui va nous forcer à revoir quelques points dans la façon de construire le logiciel.
Alors nous avons évidemment optimisé la façon de lancer les tests, de tourner rapidement sur un cluster kubernetes l’ensemble des tests, et nous avons consacré beaucoup d’énergie à corriger par exemple la flakiness… Ce travail de fond est indispensable et il continue à être fait.
Là où se pose la question du monolithe, c’est que l’ensemble des équipes travaillent progressivement de plus en plus sur des produits différents. La bascule et l’intérêt de commencer à envisager l’ère post-monolithique (crédit Grégoire Cacheux pour ce terme) se font de plus en plus sentir.
Autour de la CI qui mérite un article à part entière, nous avons des équipes dédiées, qui sont positionnées comme des équipes « Plateforme », avec des KPIs facile à suivre : nombre de build, coût unitaire d’un build, utilisation de métrics DORA, nous avons optimisé autant que possible la partie intégration continue et tests du moteur.
La propriété du code tend à se diluer au cours du temps
Un autre problème que nous observons, c’est la propriété du code au fil des années. Les « feature teams » sont volatiles et peuvent apparaître/disparaître et se retrouver ensuite assignées sur d’autres sujets. Au fil du temps, cela va créer une fragmentation de la responsabilité et des connaissances. Je pense qu’un monolithe amplifie ce phénomène. Le fait de ne pas devoir opérer le « build&run » permet aux équipes de se décharger rapidement de la propriété du code. Elles ont certes quelques KPIs et elles doivent surveiller un périmètre de code. Mais ce code, dans un monolithe, sera peut-être complété ou modifié par une autre équipes, des semaines ou des mois après la première version.
Ceux qui ont utilisé la commande defrag sur ms-dos 6.22 savent de quoi je parle…
L’ère Post-Monolithe
Nous sommes maintenant convaincu que nous devons entrer dans l’après-monolithe.
Plusieurs signaux nous indiquent que nous arrivons à la fin d’une période.
Ces signaux sont :
- notre capacité à créer un nouveau produit en réutilisant un socle commun
- avoir la possibilité de racheter des produits complémentaires ou concurrents, afin de les intégrer ensuite dans la suite Doctolib
- l’envie de pouvoir rationaliser et améliorer nos coûts selon le type de produits à construire
- la possibilité d’utiliser d’autres technologies, d’autres langages de programmation ou un autre socle technique lorsqu’il est plus adapté au marché (exemple : support de FHIR en Allemagne)
- le coût de construction et de tests du monolithe
- d’autres trucs que j’ai oublié, mais que je ne manquerai pas de lister ici
Nous savons aussi que cela va entraîner de nouveaux challenges :
- l’obligation de préparer une software factory, et de proposer un socle pour écrire et connecter ces services
- un passage obligé par plus d’isolation, afin d’améliorer la modularisation de notre code
- les équipes seront invitées à être plus proches de la production, et à suivre chacune leurs services
- il va être nécessaire de former les développeurs sur l’orchestration de services, la sécurisation des échanges inter-service, et sur les notions d’architecture synchrone/asynchrone
- la construction d’un produit composé de plusieurs ‘capabilities‘ devra être coordonnée et reposera sur des versions de services à composer
- il faudra isoler le domaine, restructurer la base principale et revoir les bounded context de chaque produit
- il y aura forcément des échecs et des projets qui n’y arriveront pas
Quelle est la bonne stratégie ?
On peut démarrer d’abord par chercher d’autres entreprises qui ont fait le passage d’un monolithe à « autre chose« . Pour cela, nous avons échangé avec différentes entreprises, et nous avons aussi identifié les besoins de formation en interne.
Nous avons démarré ce projet par un énorme travail de documentation et de mapping, afin de vulgariser et d’expliquer les différents composants actuels du monolithe. Il s’avère qu’il y a en fait une quinzaine de « grands modules » et plus de 189 fonctionnalités. Tout ceci fait « Doctolib ». Enfin, plus exactement, cet ensemble fait Doctolib Médecin, Doctolib Kiné, la Téléconsultation, la prise de rendez-vous en Italie, en Allemagne, la facturation en France, la synchro avec les hopitaux… En fait, c’est énorme. Et nous l’avons représenté graphiquement avec des codes couleurs simples, pour montrer à tout le monde « ça, c’est Doctolib« .
Ce travail nous a ouvert les portes (je dirai même les bras) des équipes Produits. En attaquant ce projet non pas via la voie technique, mais via la voie produit, nous avons obtenu une oreille attentive.
Vraiment, ce qui fait que pour l’instant « ça marche« , c’est que nous avons réussi à embarquer les équipes Produits. Et même, plus fort, à embarquer notre CEO. Si le taulier n’est pas informé des « petits » travaux que nous comptons faire dans sa maison, ce projet court à la catastrophe. C’est aussi un investissement important, sur 2 ans, pour se préparer à aller ensuite plus vite.
Chaque chose que nous faisons est piloté autour de 2 missions
- améliorer le quotidien des personnels soignants avec des outils efficaces, sécurisés et agréables à utiliser
- améliorer la santé de tous, en protégeant les données des patients et des médecins, en collaborant avec nos utilisateurs et en proposant des solutions innovantes (comme la téléconsultation)
Premier enseignement de ces derniers mois : il est indispensable de convaincre et de travailler d’abord avec les équipes Produits, avant d’aller voir ensuite l’Engineering.
Pour nous, c’est passé par une compréhension des « capabilities » par les équipes produits. Elles doivent s’approprier et comprendre quels sont les différents briques « Lego » dont elles disposent.
Ensuite, il est important de définir un plan et de démarrer avec une stratégie. Est-ce que vous souhaitez créer un nouveau produit ? Est-ce que vous venez d’acheter une entreprise, et vous devez l’intégrer dans votre monolithe ? Je pense que la stratégie et les objectifs doivent être clairs et partagés par tout le monde.
L’organisation a alors créé une équipe ad-hoc avec un Product manager, et a démarré une phase assez longue pour reconfigurer la façon dont on pense et on construit nos fonctionnalités. Désormais, chaque équipe qui souhaite construire ou intervenir sur une des « capabilities » doit penser qu’elle est une petite startup.
Imaginons que je souhaite créer un service de stockage de documents médicaux chiffrés de bout-en-bout, dans le but de permettre à des médecins de partager des analyses médicales. Là je vous sens un peu perdu. Imaginez plutôt que je vous demande d’associer une capability de partage de document (comme Dropbox) avec un système de chiffrement de bout-en-bout (via Tanker). Et voilà, vous êtes chef de produit et vous venez de composer 2 capabilities.
Votre système de partage de document étant tellement bien, que je vous souhaite un jour de pouvoir offrir ce service à d’autres éditeurs de logiciels dans le monde médical. Imaginez sinon un Amazon S3 sécurisé et HDS…
En fait, si Doctolib arrivait à proposer des services en mode SaaS, elle deviendrait alors un fournisseur de service, comme l’a fait Amazon Web Service en son temps. Pourquoi ne pas envisager cela dans quelques années ?
Mais avant d’en arriver là, reprenons l’histoire d’AWS qui est intéressante. Je ne sais pas si vous aviez cette information, mais Amazon a réalisé 24 Milliards de C.A en 2020, et AWS 17.4 Milliards, soit 74% du revenue d’Amazon. 74% du CA est généré par AWS. Sachant que l’on estime que seul 10% du marché du cloud a migré, AWS est devant une montagne encore plus impressionnante. En 2010 j’écrivais qu’AWS ne représentait « que » 4.28% du CA pour l’année 2009.
Bref, imaginer qu’un acteur dans le monde de la santé soit capable de construire et d’opérer des services, c’est pas forcément une ‘mauvaise idée’.
Avant d’en arriver là, il faut d’abord rendre le monolithe actuel complètement modulaire. Et il ne s’agit pas de « casser » ce qui marche très bien.
Je crois que le plus dur c’est de savoir par quelle pièce commencer. Est-ce que l’on refait la cuisine, puis la chambre et le salon ? Ou alors est-ce que l’on change le sol de toute la maison, avant de s’attaquer à l’électricité ? Vous me voyez venir avec mes grosses images qui collent ?
Voilà où nous en sommes.
J’essaye de vous partager une expérience, et je retourne défragmenter les équipes…
Crédits images : images générées avec Midjourney
Bonjour,
Article très intéressant. Cependant, il comporte une erreur dans le paragraphe final : vous confondez Chiffre d’Affaires (CA) et bénéfice (« profit »).