Serverless est souvent présenté comme une évidence.
Pas d’infrastructure à gérer, scaling automatique, facturation à l’usage. Sur le papier, c’est difficile de faire plus séduisant. Dans la pratique, les choses sont un peu différentes.
Serverless est extrêmement efficace dans certains contextes, mais dès que l’on sort du cas simple — démonstration, API peu sollicitée, workload ponctuel — des contraintes apparaissent, et elles ne sont pas toujours visibles au départ.
Le point de départ
Partons d’un cas assez classique : une API backend conçue pour être simple, stateless, avec une charge variable. Le choix se porte naturellement sur une architecture AWS Lambda avec API Gateway.
Les premiers résultats sont convaincants : le déploiement est rapide, l’infrastructure inexistante à gérer, la latence reste correcte et les coûts sont faibles.
Tout fonctionne bien au départ. Puis, progressivement, le trafic augmente. Pas de manière brutale, mais suffisamment pour passer d’un usage ponctuel à un usage régulier.
Le premier signal : la latence devient instable
En environnement de test, tout reste fluide. En production, le comportement devient moins prévisible.
Sur un même endpoint, sans aucun changement fonctionnel, on observe des écarts significatifs :
p50: 90ms
p95: 300ms
p99: 900ms+
Dans un cas similaire, le passage vers un service persistent (ECS) a permis de stabiliser les temps de réponse :
p95: 300ms → ~120ms stable
Ce n’était pas plus rapide en moyenne, mais beaucoup plus prévisible.
La base de données n’est pas en cause, le réseau non plus.
Le problème vient du runtime.
Les cold starts, en conditions réelles
Le concept est connu, mais son impact réel est souvent sous-estimé.
Dans un environnement PHP (via Bref) ou Node, un cold start implique plusieurs étapes : initialisation du runtime, chargement du code, bootstrap du framework, ouverture des connexions. Chacune est légitime, mais leur accumulation a un coût.
Selon le contexte, on se retrouve typiquement avec :
Cold start: 200ms → 800ms
Warm execution: 20ms → 80ms
Le problème n’est pas le cold start en lui-même, mais sa distribution. Sur un trafic irrégulier ou bursty, il devient fréquent, et surtout imprévisible.
Tentatives classiques (et leurs limites)
Plusieurs stratégies sont généralement mises en place pour limiter ces effets.
La première consiste à maintenir les fonctions “chaudes”, en les déclenchant régulièrement via CloudWatch ou un scheduler. Dans les faits, cela permet de réduire une partie des cold starts, mais l’amélioration reste partielle. Surtout, on introduit un coût non négligeable, ainsi qu’une complexité supplémentaire dans l’exploitation.
Une autre approche classique consiste à augmenter la mémoire allouée aux fonctions.
128MB → 512MB → 1024MB
Sur Lambda, cela a un effet direct : le CPU augmente proportionnellement, ce qui réduit les temps d’exécution. Les performances s’améliorent, parfois de manière significative.
En revanche, le coût augmente lui aussi rapidement, et surtout, cela ne règle pas le problème de fond : les cold starts restent présents.
Le coût réel (et non celui annoncé)
Le modèle “pay-per-use” est séduisant, du moins tant que le volume reste modéré.
Sur des charges faibles ou irrégulières, il tient parfaitement ses promesses. Mais dès que le trafic devient plus constant, l’équation change.
Un exemple réel, à titre indicatif :
~2M requêtes / mois
Lambda + API Gateway:
≈ 120–180$
Equivalent ECS Fargate (always on):
≈ 70–100$
L’écart ne vient pas d’un élément unique, mais d’une accumulation de coûts souvent sous-estimés : API Gateway en tête, puis les invocations Lambda, et enfin les logs.
Pris individuellement, ces coûts restent raisonnables. Mais ensemble, ils finissent par dépasser ce que l’on aurait avec une infrastructure plus classique.
Le coût n’est pas nécessairement problématique.
En revanche, il n’est pas toujours optimal non plus.
Le vrai problème : l’observabilité
C’est probablement l’un des points les plus problématiques en pratique.
Dans une architecture plus classique, l’observabilité est relativement maîtrisée : les logs sont centralisés, les traces continues, et le profiling reste accessible. On dispose d’une vision assez claire de ce qui se passe.
En serverless, la situation est différente.
Les logs sont fragmentés, les exécutions éphémères, et la corrélation entre les différentes étapes devient rapidement complexe.
Un cas simple permet de l’illustrer : une requête traverse plusieurs fonctions — trois Lambdas par exemple. Chacune log de son côté, dans son propre contexte d’exécution. Reconstituer le parcours complet de la requête demande alors un effort supplémentaire, qui n’est pas toujours trivial.
Des outils comme X-Ray ou OpenTelemetry permettent d’améliorer la situation, mais au prix d’une complexité qui augmente vite, notamment dès que les flux se multiplient.
Les effets de bord
Certains problèmes n’apparaissent réellement qu’à l’échelle.
C’est notamment le cas des connexions à la base de données.
Dans un modèle serverless, chaque Lambda peut ouvrir sa propre connexion. En situation de concurrence, on se retrouve rapidement avec quelque chose du type :
100 Lambdas concurrentes → 100 connexions DB
Sur une base classique, cela suffit à provoquer une saturation.
Plusieurs solutions existent — pool externe, RDS Proxy, limitation de la concurrence — mais aucune n’est gratuite. Chacune introduit une couche supplémentaire, et donc une complexité opérationnelle accrue.
Temps d’exécution et timeouts
Certaines opérations deviennent également plus complexes à mettre en œuvre.
Les traitements longs, les agrégations ou encore les exports s’intègrent difficilement dans un modèle conçu pour des exécutions courtes et stateless. Des solutions existent — Step Functions, files de messages — mais elles introduisent rapidement une orchestration plus lourde.
On s’éloigne alors du modèle initial, simple et direct, qui faisait l’intérêt du serverless au départ.
Ce décalage se retrouve aussi côté développement : reproduire un comportement localement devient plus difficile, et certaines latences ou effets de bord n’apparaissent qu’en production.
Où serverless fonctionne très bien
Malgré ces contraintes, le serverless reste extrêmement pertinent dans certains contextes.
Sur des workloads sporadiques, des traitements asynchrones, de l’ingestion d’événements ou des opérations courtes et isolées, il apporte une réelle valeur. Dans ces situations, la simplicité est tangible, le coût reste maîtrisé, et le scaling automatique joue pleinement son rôle.
Où ça devient plus discutable
Dès que le contexte évolue vers un trafic soutenu, une latence critique, un besoin fort en observabilité ou une logique applicative plus riche, les limites apparaissent plus nettement.
Dans ces situations, une approche plus classique peut redevenir pertinente. Des architectures basées sur des conteneurs (ECS, Kubernetes), des services persistants, ou simplement un meilleur contrôle du runtime permettent de retrouver de la prévisibilité et de la maîtrise.
Conclusion
Serverless n’est pas une mauvaise solution.
Mais ce n’est pas une solution universelle.
Ce qui fonctionne très bien en démonstration peut devenir contraignant en production, dès que les contraintes réelles apparaissent : latence, observabilité, coût.
Serverless ne supprime pas la complexité. Il la déplace.
Comme souvent, le sujet n’est pas tant la technologie elle-même que le contexte dans lequel elle est utilisée.
TL;DR
- Serverless fonctionne très bien… dans certains contextes
- Les cold starts sont imprévisibles en production
- Le coût réel dépend fortement du trafic
- L’observabilité est plus complexe
- Les effets de bord apparaissent à l’échelle