05 - Les 7 erreurs classiques (et comment les éviter)
Ce que tu vas apprendre
- Les 7 pièges les plus courants en architecture microservices
- Comment les détecter dans ton propre système
- Les solutions concrètes pour chacun
Prerequisites
Erreur 1 : Decouper trop tot
Ce que ca ressemble. L'équipe décidé des le jour 1 que l'application sera en microservices. Avant meme d'avoir un utilisateur. On créé 8 repos, 8 pipelines CI/CD, un cluster Kubernetes, et on passe les deux premiers mois a faire marcher l'infra au lieu de livrer des features.
Pourquoi ca arrive. La peur du monolithe. "Si on commence monolithe, on ne pourra jamais migrer." C'est faux. Un monolithe modulaire avec des frontieres propres se decoupe bien. Un code spaghetti aussi, mais ca prend juste plus de temps.
Comment l'éviter. Commence par un monolithe. Toujours. Comme je l'explique dans l'introduction de cette serie, les microservices se justifient quand la douleur est réelle, pas en prevention. Sur paltemps.fr, le monolithe est le bon choix depuis le premier jour.
Erreur 2 : Les nano-services
Ce que ca ressemble. Un service par endpoint. Un service "user-email", un service "user-address", un service "user-phone". 40 services pour ce qui devrait etre un service "user".
Pourquoi ca arrive. Une mauvaise interprétation du "micro" dans microservice. Micro, ca veut dire "une capacité métier", pas "le moins de code possible".
Comment l'éviter. Un microservice doit pouvoir etre développé et maintenu par une petite équipe (2-8 personnes). Si ton service a 50 lignes de code et une seule route, c'est probablement un endpoint, pas un service. Regroupe par bounded context, pas par endpoint.
Erreur 3 : La base de donnees partagee
Ce que ca ressemble. Cinq services qui font des requêtes sur les memes tables PostgreSQL. Le service "commandes" fait un JOIN avec la table users du service "auth". Les migrations de schema deviennent un cauchemar parce que chaque changement peut casser 4 autres services.
Pourquoi ca arrive. C'est la migration la plus facile : tu separes le code mais tu gardes la base. Ca marche au début. Et puis le premier ALTER TABLE casse tout.
Comment l'éviter. Chaque service a sa propre base de donnees. Si le service "commandes" a besoin du nom de l'utilisateur, il le demande au service "auth" via une API (REST ou gRPC) ou il garde une copie locale synchronisee par événements. Oui, ca duplique des donnees. C'est le prix de l'independance.
Erreur 4 : Tout en synchrone
Ce que ca ressemble. Chaque requête utilisateur déclenché une cascade d'appels synchrones. Le service A appelle B, qui appelle C, qui appelle D. Si D met 3 secondes a répondre, toute la chaîne met 3+ secondes. Si D tombe, tout tombe.
Pourquoi ca arrive. Le synchrone est plus facile a comprendre. Tu fais un appel, tu attends la réponse, tu continues. Le code ressemble a ce qu'on ecrirait dans un monolithe.
Comment l'éviter. Pose-toi la question : "est-ce que j'ai besoin de la réponse maintenant ?". Si la commande est créée et que tu dois envoyer un email de confirmation, c'est un message asynchrone. L'email peut partir 2 secondes plus tard, l'utilisateur ne verra pas la différence. L'article sur la communication détaillé les trois modes et quand les utiliser.
Erreur 5 : Pas de logs centralises
Ce que ca ressemble. Chaque service écrit ses logs dans stdout. Pour debugger un problème, tu fais docker logs sur 8 containers différents en essayant de reconstituer la chronologie a la main. Bonne chance.
Pourquoi ca arrive. "On ajoutera le monitoring plus tard." Plus tard n'arrive jamais, ou il arrive apres la première panne en prod que personne ne comprend.
Comment l'éviter. Jour 1 de tes microservices : logs JSON avec un request ID propage entre services, un collecteur (Loki, ELK, Datadog), et un dashboard pour chercher. C'est non negociable. Si tu utilises gRPC, les interceptors sont l'endroit ideal pour logger chaque appel.
Erreur 6 : Pas de versioning d'API
Ce que ca ressemble. Le service "catalogue" change le format de sa réponse. Le champ price devient price_cents. Le service "commandes" plante en production parce qu'il attendait price. Personne n'avait prevenu.
Pourquoi ca arrive. Quand c'est la meme équipe qui gere tout, on oublie que les services sont des clients les uns des autres. On change l'API comme on changerait une fonction interne.
Comment l'éviter. Trois regles :
- Backward compatibility : ajouter des champs, jamais en supprimer (dans un premier temps)
- Versioning explicite :
/v1/products,/v2/productsou versioning dans les headers - Contrats types : Protocol Buffers rendent les changements cassants visibles a la compilation. C'est un des gros avantages de gRPC sur REST pour le trafic interne.
Erreur 7 : Le monolithe distribue
Ce que ca ressemble. Tu as 10 "microservices" mais :
- Tu ne peux pas déployer un service sans déployer les autres
- Un changement dans le service A nécessité un changement dans B et C
- Tous les services partagent la meme base de donnees
- Les tests d'intégration necessitent de lancer tous les services
C'est un monolithe avec du réseau en plus. Le pire des deux mondes.
Pourquoi ca arrive. On decoupe par couche technique (un service "controllers", un service "repositories") au lieu de par capacité métier. Ou on decoupe trop finement sans définir des frontieres propres. L'article sur la decoupe explique comment faire correctement.
Comment l'éviter.
- Chaque service a sa propre base de donnees
- Chaque service peut etre déployé seul
- Les contrats d'API sont explicites et versionnes
- Si tu dois coordonner le déploiement de 3 services, tes frontieres sont mauvaises
Le test decisif : coupe le réseau entre le service A et le service B. Si A ne peut plus du tout fonctionner, le couplage est trop fort.
Le test des 5 questions
Avant de mettre en prod, pose-toi ces 5 questions :
- Est-ce que chaque service peut etre déployé indépendamment ? Si non, c'est un monolithe distribue.
- Est-ce que je peux debugger un problème sans lire les logs de 10 services ? Si non, il manque du tracing.
- Est-ce que la chaîne d'appels synchrones fait plus de 3 niveaux ? Si oui, il faut du messaging.
- Est-ce que changer l'API d'un service casse les autres sans prevenir ? Si oui, il manque des contrats types.
- Est-ce que l'équipe passe plus de temps sur l'infra que sur le produit ? Si oui, tu as probablement decoupe trop tot.
Si tu reponds "oui" a plus de 2 questions, il y a du travail. Mais c'est reparable. Le pire serait de ne pas poser les questions.
Résumé
- Decoupe trop tot = temps perdu sur l'infra au lieu du produit
- Nano-services = complexité sans benefice, regroupe par bounded context
- Base partagee = pas de vrais microservices, chaque service a sa propre base
- Tout synchrone = cascades de defaillances, utilise du messaging
- Pas de logs = aveugle en prod, c'est non negociable
- Pas de versioning = casse en cascade, utilise des contrats types
- Monolithe distribue = le pire des deux mondes, vérifié tes frontieres
Article précédent : 04 - L'infra
Suivant : 06 - Glossaire
Serie liee : gRPC : communication inter-services -- pour approfondir la communication typee entre services.