Chez ExpertItLab, on a cédé à la grande promesse des microservices sur une plateforme transactionnelle en pleine croissance. On avait alors une équipe de quatorze développeurs, un dépôt de code unique qui commençait à grossir, et des temps de validation de pipeline qui atteignaient douze minutes. Les articles d'ingénierie des géants du web et les discussions de salon nous répétaient tous le même refrain : découpez vos applications, libérez vos sous-équipes, gagnez en agilité et déployez dix fois par jour de manière totalement indépendante.
On s'est donc lancés avec enthousiasme. En l'espace de huit mois, notre monolithe historique a été scindé en huit services distincts : authentification, gestion des utilisateurs, catalogue, facturation, notifications, passerelle de paiement, inventaire et logs d'audit. Chacun avait son propre dépôt Git, sa base de données isolée et son pipeline de livraison.
Huit mois plus tard, le réveil a été brutal. Ce qui prenait auparavant deux heures de travail sur une branche locale réclamait désormais quatre pull requests synchronisées, deux réunions de coordination inter-services et une demi-journée d'angoisse en préproduction pour vérifier qu'un changement de schéma n'avait pas cassé un contrat d'API en cascade. Un simple bug de validation de commande nous forçait à ouvrir cinq terminaux, à faire tourner des conteneurs gourmands en mémoire et à traquer des traces distribuées pour comprendre quel microservice avait lâché une réponse en timeout.
C'est là qu'on a dû se regarder en face et poser la question centrale : pourquoi découpe-t-on des architectures logicielles en dizaines de microservices pour résoudre des frictions d'organisation qu'on finit en réalité par décupler en production ? Le problème n'était pas technique, il était structurel.
Le piège de l'autonomie feinte et la tyrannie des contrats d'API
La justification numéro un des microservices repose toujours sur l'indépendance des équipes. On nous disait : l'équipe paiement avance à son rythme, l'équipe catalogue avance au sien, plus personne ne bloque la branche principale. C'est une belle promesse théorique, mais elle s'effondre dès que le modèle métier évolue rapidement.
Dans une entreprise en croissance, les fonctionnalités ne respectent presque jamais les frontières arbitraires qu'on a tracées dans un schéma d'architecture un mardi après-midi. Lorsque nos clients ont demandé l'ajout d'une remise promotionnelle dynamique liée au profil de fidélité et au mode de règlement, la réalité nous a rattrapés. Il a fallu modifier le service utilisateur pour exposer le statut du compte, mettre à jour le service catalogue pour calculer la remise, adapter le service commande pour stocker l'historique et ajuster le service de facturation pour ventiler la TVA.
Au lieu d'avoir des développeurs autonomes, on a créé un réseau de dépendances invisibles. Une équipe ne pouvait plus livrer sans attendre que deux autres équipes aient déployé leurs versions respectives d'API en staging. On a troqué un verrou de fusion de code dans Git, facilement gérable avec une bonne revue de code, contre un couplage réseau dynamique distribué en production. Pire encore, la gestion de la compatibilité ascendante est devenue un fardeau quotidien. On passait plus de temps à rédiger des adaptateurs de schémas JSON et à déprécier d'anciens endpoints qu'à concevoir des fonctionnalités métier réelles.
L'environnement local impossible et la perte de visibilité directe
Avant cette scission, un nouveau développeur rejoignant ExpertItLab clonait un dépôt unique, lançait une commande d'installation, et disposait en moins de vingt minutes d'un environnement complet fonctionnel sur son poste de travail. Les tests d'intégration tournaient en local en moins de trois minutes, avec une base de données PostgreSQL de test et des bouchons simples.
Avec les microservices, faire tourner la plateforme en local est devenu une épreuve digne d'un parcours du combattant. Il fallait faire tourner huit conteneurs Docker, orchestrés par des scripts locaux complexes, consommant plus de 18 gigaoctets de mémoire vive sur les machines de développement. Les ordinateurs portables soufflaient en permanence, les ports réseau entraient en conflit, et les développeurs passaient leurs lundis matin à réparer des environnements cassés plutôt qu'à coder.
Résultat inévitable : l'équipe a renoncé à tester les interactions complètes en local. On a commencé à pousser du code sur des environnements de préproduction partagés pour voir si l'ensemble tenait debout. Mais quand six développeurs poussent simultanément sur un staging partagé composé de microservices asynchrones, isoler l'origine d'une régression devient un travail d'archéologue. Un message d'erreur "502 Bad Gateway" pouvait provenir d'un crash de pod, d'une rupture de contrat de sérialisation ou simplement d'une latence anormale sur le broker de messages. On a perdu la boucle de rétroaction rapide qui fait toute la productivité d'une équipe de développement.
Le coût caché du réseau : latence, transactions distribuées et pannes partielles
Dans un monolithe, un appel entre deux modules métier est un simple appel de fonction en mémoire. Cela prend quelques nanosecondes, ne consomme pas de bande passante réseau et réussit de manière déterministe. Dans une architecture distribuée, chaque appel devient une traversée de pile TCP, une sérialisation JSON ou Protobuf, un routage réseau et une désérialisation.
On a mesuré l'impact sur nos temps de réponse : pour charger une simple vue récapitulative de commande, notre passerelle effectuait jusqu'à quatorze requêtes HTTP internes entre services. Même avec des connexions mutualisées et des délais courts, le temps de réponse total est passé de 85 millisecondes sur notre ancien système à plus de 420 millisecondes sur la nouvelle architecture.
Mais la latence n'était encore que la partie émergée du problème. Le vrai drame concernait la cohérence des données. Dans un système transactionnel bancaire ou e-commerce, on ne peut pas se permettre d'avoir un débit validé sans que la réservation de stock soit garantie. Dans notre monolithe, une transaction SQL ACID classique réglait la question en quatre lignes. Avec huit bases de données séparées, on s'est retrouvés confrontés au problème des transactions distribuées. On nous a conseillé de mettre en place des sagas asynchrones, des compensations automatiques et de la cohérence à terme. En pratique, lorsqu'un paiement réussissait mais que le service d'inventaire tombait en panne au même instant, notre équipe passait des soirées entières à réconcilier les données à la main dans des scripts SQL d'urgence. On avait importé les problématiques d'ingénierie d'une multinationale traitant un million de requêtes par seconde, alors qu'on servait quelques dizaines de milliers d'utilisateurs actifs.
La renaissance du monolithe modulaire et le pragmatisme architectural
Il existe bien sûr des cas légitimes pour les microservices : quand une organisation compte trois cents ingénieurs répartis sur plusieurs continents, ou quand un composant très spécifique réclame une mise à l'échelle matérielle disproportionnée, comme du transcodage vidéo intensif ou de l'inférence de modèles d'intelligence artificielle. Même Amazon Prime Video a fait les gros titres en 2023 en abandonnant une architecture serverless et microservices coûteuse pour revenir à un monolithe consolidé, réduisant leurs coûts d'infrastructure de 90%.
Pour une équipe de dix à cinquante personnes, la séparation physique prématurée est une erreur stratégique majeure. La bonne réponse aux difficultés de maintenance d'une base de code n'est pas le découpage réseau, mais la rigueur modulaire au sein d'un même artefact déployable. En appliquant une séparation stricte par domaines métier, avec des interfaces internes claires et des contrôles d'accès au niveau des modules de compilation, on obtient tous les bénéfices de la séparation sans payer la taxe exorbitante de l'infrastructure distribuée.
Chez ExpertItLab, on a changé d'avis sur ce qu'est une architecture élégante. On a arrêté de considérer le mot monolithe comme une insulte technique ou un vestige du passé. Deux ans après notre folle aventure dans les microservices, on a rapatrié six de nos huit services au sein d'un monolithe modulaire bien structuré en Go et PostgreSQL.
Le constat chiffré a été sans appel : nos temps de cycle pour livrer une fonctionnalité métier complète ont chuté de 65%. La facture d'infrastructure a diminué de près de la moitié, car on n'avait plus besoin d'entretenir une nuée de clusters Kubernetes, de routeurs de maillage et d'outils d'observabilité lourds pour chaque micro-dépôt. Mais surtout, le niveau de sérénité de l'équipe a bondi. Les développeurs peuvent à nouveau faire tourner l'intégralité du projet sur leur ordinateur portable sans que leur machine ne surchauffe, poser des points d'arrêt dans un débogueur local et comprendre le flux d'exécution d'un bout à l'autre sans consulter un graphe distribué.
Voici la question qu'on pose aujourd'hui à chaque équipe technique tentée par le grand saut distribué : cherchez-vous à découper votre système parce que vos contraintes matérielles l'exigent absolument, ou fuyez-vous simplement le travail difficile d'organiser proprement votre code et vos frontières de communication interne ? Avant de déployer un réseau de services, demandez-vous si vous êtes prêts à payer le prix de la complexité distribuée tous les jours ouvrés.
