Ajouter du cache avant d'avoir un problème de charge : le raccourci qui vous coûte des bugs invisibles

Le cache passe pour une optimisation gratuite. En réalité, c'est une décision d'architecture qui introduit une source de vérité parallèle — et des bugs que vous ne verrez jamais en développement. Quand cacher, quoi cacher, et comment ne pas s'y brûler.

Published: August 28, 2026

Une page qui met deux secondes à charger, un dashboard qui rame quand un client a trop de données, une requête qui tape la base à chaque affichage. La réaction réflexe d’une équipe tech pressée : « on met du cache ». C’est rapide à brancher, ça marche immédiatement en démo, et le fondateur voit le chiffre passer de 2 secondes à 200 millisecondes. Tout le monde est content.

Sauf que vous venez d’introduire, sans le nommer, une deuxième source de vérité dans votre produit. Et les problèmes qu’elle crée n’apparaîtront pas cette semaine. Ils apparaîtront dans trois mois, chez un client, sur des données que vous ne pouvez pas reproduire — au pire moment.

Le cache n’est pas une optimisation, c’est un compromis

Il faut être clair sur ce qu’est vraiment le cache : une copie de données stockée quelque part pour éviter de recalculer ou de re-récupérer l’original. Tant que la copie est identique à l’original, tout va bien. Le problème, c’est le moment où l’original change et que la copie ne suit pas. C’est là que naissent les bugs les plus insidieux d’un produit : un client qui modifie son profil et voit l’ancienne version pendant dix minutes, un prix mis à jour dans l’admin mais toujours affiché à l’ancien tarif au checkout, un utilisateur désactivé qui garde son accès parce que la vérification de permissions passe par une valeur cachée.

Ces bugs ont une caractéristique commune, et c’est ce qui les rend dangereux : ils sont invisibles en développement. Chez vous, vous testez sur une base fraîche, vous rechargez, le cache est vide ou cohérent. Vous ne verrez jamais l’effet d’une invalidation ratée avant que ça arrive en production, sur des données réelles, dans des conditions de concurrence que votre environnement de test ne reproduit pas. Phil Karlton, ingénieur chez Netscape, a résumé ça par une formule devenue célèbre : les deux choses difficiles en informatique sont l’invalidation de cache et le nommage des variables. Ce n’est pas une blague de développeur. C’est un avertissement.

La vraie question : avez-vous un problème de charge, ou un problème de code ?

Avant de cacher quoi que ce soit, il faut savoir pourquoi c’est lent. Et dans l’immense majorité des cas early-stage, la lenteur ne vient pas d’un manque de cache. Elle vient d’un code qui fait mal les choses.

Le coupable numéro un, celui qu’on retrouve dans presque toutes les startups qui ont grandi vite, c’est le problème dit « N+1 » : votre page affiche une liste de cinquante clients, et pour chacun elle refait une requête pour récupérer son abonnement. Cinquante-et-une requêtes là où une seule bien écrite suffirait. Ajouter du cache par-dessus ce code, c’est masquer le symptôme sans traiter la cause. Vous cachez une inefficacité au lieu de la corriger. Le jour où le cache expire ou où un nouveau client arrive, la lenteur revient — sauf que maintenant vous avez aussi la complexité du cache à gérer.

Le deuxième coupable, c’est l’absence d’index en base de données. Une requête qui balaye une table de cent mille lignes parce qu’il manque un index prendra plusieurs centaines de millisecondes. Ajouter l’index la fait passer sous les cinq millisecondes. C’est gratuit, ça ne crée aucune source de vérité parallèle, et ça résout durablement le problème. Beaucoup de startups mettent une couche de cache Redis pour éviter d’avoir à comprendre pourquoi leur requête était lente — alors qu’un EXPLAIN ANALYZE de trente secondes aurait donné la réponse.

La règle pratique, à ce stade : mesurez avant de cacher. Regardez vos requêtes lentes, comprenez d’où vient la lenteur, corrigez le code et les index. Dans neuf cas sur dix, le besoin de cache disparaît. C’est moins glamour que « on a mis en place une architecture de cache distribué », mais c’est ce qui fait tourner un produit sans dette.

Quand le cache devient légitime — et lequel choisir

Il arrive un moment où le cache est la bonne réponse. C’est le cas quand la donnée est coûteuse à produire et qu’elle change peu. Le résultat d’un calcul lourd — un rapport analytique agrégé sur des millions de lignes, une recommandation générée par un modèle, une réponse d’API externe que vous payez à l’appel. Là, recalculer à chaque affichage n’a aucun sens, et le risque d’incohérence est faible parce que la donnée est stable par nature.

Encore faut-il choisir le bon type de cache, et ils ne se valent pas en termes de complexité introduite.

Le plus simple, et souvent le plus sous-estimé, c’est le cache HTTP : demander au navigateur ou à un CDN de garder une ressource statique — vos assets, vos images, une page qui ne change pas par utilisateur. Ça se pilote avec des en-têtes, ça ne touche pas votre base, ça ne crée pas de source de vérité parallèle dans votre logique métier. Pour une startup, un CDN devant votre application (Cloudflare, Fastly, ou l’offre CDN de votre hébergeur) résout une bonne partie des problèmes de performance perçue sans une ligne de code à cacher côté serveur.

Ensuite vient le cache applicatif en mémoire, du type Redis ou Memcached. C’est le cran au-dessus en puissance, mais aussi en responsabilité : vous devenez propriétaire de la cohérence. Chaque donnée que vous y mettez, vous devez décider quand elle expire et comment elle est invalidée. C’est là que la plupart des équipes se brûlent, parce qu’elles pensent à écrire dans le cache mais oublient tous les chemins par lesquels la donnée d’origine peut changer.

La stratégie d’invalidation la plus robuste pour une petite équipe n’est d’ailleurs pas l’invalidation fine et intelligente — trop facile à rater — mais l’expiration par le temps, le fameux TTL. Vous acceptez que la donnée cachée puisse être périmée de trente secondes, ou cinq minutes, et vous la laissez expirer d’elle-même. C’est imparfait, mais c’est prévisible, et la prévisibilité vaut de l’or dans un système que trois personnes maintiennent. La question à se poser pour chaque donnée cachée devient alors très concrète : « si un utilisateur voit une version périmée de X pendant deux minutes, est-ce que c’est grave ? » Pour l’affichage d’un tableau de bord marketing, non. Pour un solde de compte, un prix au checkout, ou un droit d’accès, la réponse est oui — et ces données-là ne doivent jamais reposer sur un cache à expiration.

Le coût caché, celui qui touche votre runway

On parle du cache comme d’un gain de performance, rarement comme d’un coût. Pourtant il en a plusieurs, et ils sont réels pour une startup qui compte ses ressources.

Il y a d’abord le coût d’infrastructure direct : un Redis managé chez votre hébergeur, c’est une ligne de plus sur la facture cloud chaque mois, pour un service qui, à votre échelle, ne vous apporte peut-être rien qu’un bon index n’aurait apporté gratuitement. Il y a ensuite le coût cognitif, plus insidieux : chaque nouveau développeur qui arrive doit désormais comprendre non seulement votre base de données, mais aussi votre couche de cache, ses règles d’invalidation, ses pièges. Vous avez ajouté une surface à raisonner sur chaque bug. Et il y a enfin le coût du débogage : les bugs de cache sont non déterministes, difficiles à reproduire, chronophages. Une journée d’ingénieur passée à traquer une incohérence de cache, à votre stade, c’est une journée qui ne construit pas de valeur.

C’est exactement le même arbitrage que sur le reste de votre architecture : la question n’est pas « est-ce que ça rend le produit plus rapide » mais « est-ce que le gain justifie la complexité permanente que j’introduis ». En pré-seed et en seed, la réponse par défaut devrait être non. Corrigez le code, ajoutez les index, mettez un CDN devant. Réservez le cache applicatif au moment où vous avez une preuve mesurée qu’il est nécessaire — et où vous avez les épaules pour en assumer l’invalidation.

Ce qu’il faut vraiment faire

Commencez par mesurer, pas par cacher. Identifiez vos requêtes lentes, comprenez pourquoi elles le sont, et traitez la cause : requêtes N+1, index manquants, code inefficace. C’est là que se trouvent 80 % de vos gains, sans aucune dette.

Quand une lenteur résiste après ce ménage, posez-vous la question de la nature de la donnée avant de choisir un cache. Statique et partagée par tous ? Un CDN et des en-têtes HTTP suffisent, et c’est le chemin le moins risqué. Coûteuse à produire et rarement changée ? Un cache applicatif à TTL court est justifié — à condition d’assumer que la donnée puisse être périmée quelques minutes.

Ne cachez jamais ce qui doit être exact en temps réel : soldes, prix de transaction, permissions, quotas. Pour ces données, la lenteur est un moindre mal comparé à un bug d’incohérence qui peut vous coûter un client ou de l’argent réel.

Enfin, chaque fois que vous ajoutez du cache, écrivez noir sur blanc — dans le code, dans un commentaire, dans votre doc — quelle est sa durée de vie et par quels chemins il est invalidé. Un cache dont personne ne sait quand il expire est une bombe à retardement dans votre produit.

Le cache est un excellent outil au bon moment. Le problème n’est presque jamais le cache lui-même : c’est de l’ajouter avant d’avoir compris ce qui était vraiment lent, et de le traiter comme une optimisation gratuite alors que c’est un engagement à maintenir de la cohérence pour toujours. Avant de brancher votre prochain Redis, posez-vous une seule question : est-ce que je résous un problème que j’ai mesuré, ou est-ce que je masque un problème que je n’ai pas voulu comprendre ?

cache, performance, architecture, scalabilité, dette technique, runway