Conception d'API pour l'autonomie mobile : réduire la bavardise
Conception d'API pour l'autonomie mobile : apprenez le batching, les en‑têtes de cache et l'allégement des payloads pour réduire les réveils radio, accélérer les écrans et diminuer la consommation.

Pourquoi les API bavardes épuisent la batterie sur mobile
Une API « bavarde » fait qu'une app déclenche de nombreuses petites requêtes pour construire un seul écran. Chaque requête paraît bon marché sur le papier, mais sur un téléphone elles s'additionnent vite.
Le coup le plus lourd vient souvent de la radio réseau. Les puces cellulaires et Wi‑Fi passent en états haute consommation pour envoyer et recevoir des données. Quand une app envoie beaucoup de requêtes proches dans le temps, la radio se réveille souvent et reste active plus longtemps. Même si chaque réponse est minuscule, ces réveils répétés coûtent de l'énergie réelle.
Il y a aussi du travail CPU. Chaque requête signifie construire des en‑têtes, faire du TLS, parser du JSON, mettre à jour des caches et exécuter du code applicatif pour fusionner les résultats. Si les connexions tombent, le travail d'initialisation se répète.
La bavardise rend aussi l'UI plus lente. Au lieu d'un chargement prévisible, l'écran attend une chaîne d'appels. On obtient des loaders plus longs, des rendus partiels qui sautent, et plus de timeouts quand le réseau est faible. Le rafraîchissement en arrière‑plan s'aggrave de la même manière : plus de retries, plus de réveils, plus de consommation.
Une façon pratique de penser à la conception d'API pour l'autonomie mobile est simple : afficher la même UI avec moins d'allers‑retours, moins d'octets et moins de travail en arrière‑plan.
Vous pouvez suivre le succès avec quelques changements concrets :
- Moins d'appels API par chargement d'écran
- Moins d'octets téléchargés par écran
- Meilleur temps médian et pire cas pour être interactif sur réseau cellulaire
- Moins de récupérations en arrière‑plan et de retries pour obtenir le même résultat
Quand ces métriques s'améliorent, la réactivité et l'autonomie s'améliorent généralement ensemble.
Ce qu'il faut mesurer avant de changer quoi que ce soit
Avant de regrouper les requêtes ou d'ajuster le caching, dressez un inventaire clair de ce que fait l'app aujourd'hui. Les gains rapides viennent souvent de corriger quelques coupables récurrents, mais on ne les trouve que si l'on mesure de vrais écrans et parcours (pas seulement un test en parcours heureux).
Pour une poignée d'écrans à fort trafic, enregistrez l'essentiel : combien de requêtes par chargement, quels endpoints sont appelés, octets envoyés et reçus (en‑têtes plus corps), taux de retry et de timeout, et combien de temps avant que l'UI paraisse utilisable. Si possible, séparez les taux d'erreur par type de réseau (Wi‑Fi vs cellulaire). Ces découpages révèlent souvent des problèmes invisibles sur une connexion stable.
Séparez le trafic au premier plan du trafic en arrière‑plan. Un écran peut sembler silencieux, mais le téléphone peut être occupé par un refresh en arrière‑plan, des synchronisations déclenchées par push, des uploads d'analytics, ou des appels de prélecture « au cas où ». Suivez-les séparément pour ne pas optimiser la mauvaise chose.
Les points chauds se concentrent souvent autour de quelques moments : lancement d'app, écran d'accueil/feed, écrans de détail qui se ramifient en nombreux appels, et pull‑to‑refresh. Choisissez l'un d'eux et mesurez de bout en bout.
Fixez un budget de référence pour que l'équipe s'accorde sur ce qu'est "bien". Par exemple : « Un cold load de l'écran de suivi de commande ne doit pas effectuer plus de 6 requêtes et ne pas télécharger plus de 250 KB avant d'afficher le statut. »
Si vous voulez un couple de KPI simple pour commencer, utilisez (1) le nombre de requêtes par chargement d'écran et (2) le taux de retry. Réduire les retries économise souvent plus de batterie qu'on ne l'attend, parce que les retries répétés maintiennent la radio active plus longtemps.
Étape par étape : réduire les requêtes avec le batching
Les API bavardes se créent facilement par accident. Chaque widget charge « ses » données, et un écran déclenche une douzaine d'appels. La correction n'est généralement pas compliquée : identifiez ce qui se charge toujours ensemble et renvoyez‑le en moins d'appels.
Commencez par cartographier un écran à fort trafic (accueil, boîte de réception, liste de commandes). Notez ce qui apparaît au‑dessus de la ligne de flottaison, puis remontez chaque élément d'UI jusqu'à la requête qui l'alimente. Vous trouverez souvent des duplications (le même profil utilisateur récupéré deux fois) et des appels qui voyagent toujours ensemble (profil + permissions + compteur de non‑lus).
Ensuite, regroupez les appels qui se produisent systématiquement ensemble. Vous avez généralement deux options :
- Créer un endpoint dédié pour cet écran (souvent le plus stable)
- Ajouter un endpoint de batch qui accepte une petite liste prévisible de ressources
Gardez les batchs centrés sur l'écran et bornés pour qu'ils soient faciles à mettre en cache, surveiller et déboguer.
Quelques règles évitent que le batching ne devienne un cauchemar. Ne renvoyez que ce dont l'écran a besoin maintenant, pas des objets complets « au cas où ». Si certains éléments sont optionnels, rendez‑le explicite pour que l'UI puisse rendre les parties importantes rapidement. Concevez aussi la réponse pour que des échecs partiels n'obligent pas à tout retenter. Il coûte bien moins cher de ne retenter que les parties échouées que de renvoyer tout le lot.
En‑têtes de cache qui économisent la batterie (pas seulement la bande passante)
Le caching est une fonctionnalité d'autonomie. Chaque requête réveille la radio, sollicite le CPU et déclenche du parsing et de la logique d'app. De bons en‑têtes de cache transforment de nombreux refreshs en vérifications légères.
Le plus grand gain vient des requêtes conditionnelles. Au lieu de retélécharger les mêmes données, le client demande « Est‑ce que ça a changé ? ». Si non, le serveur répond par un minuscule 304 Not Modified.
Utilisez ETag sur les réponses qui représentent une version d'une ressource, et faites en sorte que le client envoie If-None-Match au prochain fetch. Si générer un ETag est difficile, Last-Modified avec If-Modified-Since fonctionne bien pour les ressources simples basées sur un « updated at ».
Pour les données qui changent rarement, rendez la décision de cache explicite. Cache-Control doit refléter la réalité. Un max-age court pour les profils utilisateur peut convenir, tandis que la configuration de l'app, les listes de référence et les feature flags peuvent souvent durer plus longtemps.
Côté app, traitez 304 comme un chemin rapide. Ne parsez pas de JSON (il n'y en a pas), ne reconstruisez pas les modèles et ne faites pas clignoter l'UI. Conservez ce qui est déjà à l'écran et mettez juste à jour les indicateurs.
Exemple : un écran de suivi de commande interroge le statut toutes les 15 secondes. Si la réponse inclut un ETag, la plupart des vérifications peuvent retourner 304. L'app conserve le dernier statut et ne fait un vrai travail que lorsque le statut change (par exemple « Emballé » → « Expédié »).
Soyez intentionnel avec les données sensibles. Le cache n'est pas automatiquement dangereux, mais il nécessite des règles claires. Évitez de mettre en cache des réponses contenant des informations personnelles ou des jetons sauf si les exigences produit l'autorisent. Utilisez des durées courtes pour les données utilisateur et empêchez les caches partagés de stocker des réponses privées (utilisez Cache-Control: private quand nécessaire).
Payloads plus intelligents : envoyer moins, parser moins
Chaque octet coûte plus que de la bande passante sur mobile. Il garde la radio éveillée plus longtemps et fait dépenser du CPU à l'app pour décoder du JSON et mettre à jour des modèles. Si vous essayez de réduire la bavardise des API, alléger les payloads est souvent la victoire la plus rapide.
Commencez par auditer les payloads par écran. Choisissez un écran (par exemple le feed d'accueil), enregistrez les tailles de réponse et regardez champ par champ : le client affiche‑t‑il ceci, ou l'utilise‑t‑il pour décider quoi montrer ? Si non, retirez‑le de cet endpoint.
Pour les listes, une petite forme « résumé » suffit généralement. Une ligne de liste a souvent besoin d'un id, d'un titre, d'un statut et d'un timestamp. Elle n'a pas besoin de descriptions complètes, de longues notes ou d'objets profondément imbriqués. Récupérez les détails seulement quand l'utilisateur ouvre un élément.
Quelques changements coupent souvent les payloads rapidement :
- Préférez des ids ou des codes enum courts plutôt que des chaînes longues répétées
- Ne renvoyez pas des valeurs par défaut évidentes que le client peut supposer
- Évitez de répéter le même objet imbriqué dans chaque élément d'une liste
- Gardez les noms et types de champs stables pour que le client n'ait pas à gérer des branches et à reparser
La compression peut aider, surtout sur des réseaux lents, mais testez le compromis CPU sur des appareils réels. Gzip ou Brotli réduisent souvent beaucoup la taille transférée, mais les téléphones anciens peuvent passer un temps notable à décompresser de très grosses réponses. La meilleure économie reste d'envoyer moins de données dès le départ.
Considérez les formes de réponse comme un contrat. Quand noms et types de champs restent constants, l'app a moins de mécanismes de secours et moins de code défensif, ce qui aide à garder l'UI fluide et la consommation d'énergie basse.
Concevoir pour les réseaux faibles et réduire les retries
Une app mobile n'épuise pas la batterie uniquement en envoyant des requêtes. Elle la consomme aussi quand une requête échoue, qu'elle retente, réveille la radio à nouveau et répète du travail. Si une API suppose un Wi‑Fi parfait, les vrais utilisateurs sur un 4G instable en feront les frais.
Facilitez la demande de moins de données. Préférez les filtres côté serveur et la pagination plutôt que « tout télécharger et filtrer sur le téléphone ». Si un écran a besoin des 20 derniers événements d'un utilisateur, supportez précisément cette requête pour que l'app n'ait pas à récupérer des centaines de lignes pour en jeter la plupart.
Supportez la synchronisation delta pour que l'app puisse demander « qu'est‑ce qui a changé depuis ma dernière vérification ? ». Cela peut être aussi simple que retourner un timestamp ou un numéro de version incrémental, puis laisser le client demander seulement les mises à jour et suppressions depuis ce point.
Les retries sont inévitables, alors rendez les mises à jour idempotentes. Un retry ne doit pas facturer deux fois, soumettre deux fois ou créer des doublons. Des clés d'idempotence pour les opérations de création et des sémantiques de mise à jour qui définissent l'état (plutôt que « ajouter 1 ») aident beaucoup.
Quand des retries se produisent, évitez les tempêtes de retries. Utilisez un backoff exponentiel avec jitter pour que des milliers d'appareils ne frappent pas vos serveurs en même temps, et pour éviter que le téléphone ne se réveille chaque seconde.
Retournez des codes d'erreur clairs qui aident le client à décider quoi faire. Un 401 doit déclencher une réauth, un 404 devrait généralement arrêter les retries, un 409 peut nécessiter un rafraîchissement d'état, et 429 ou 503 doivent déclencher un backoff.
Comportements client qui multiplient les coûts API
Même avec une API bien conçue, le client peut multiplier silencieusement le travail réseau. Sur mobile, ces réveils supplémentaires et le temps radio coûtent souvent plus en batterie que les octets eux‑mêmes.
Mettez en cache les données qui changent rarement. Photos de profil, feature flags et données de référence (pays, statuts, catégories) ne doivent pas être récupérés à chaque visite d'écran. Donnez‑leur des durées sensées, stockez‑les sur disque et rafraîchissez‑les seulement quand c'est nécessaire. Si l'API supporte la validation (ETag ou Last-Modified), une rapide vérification est souvent bien moins coûteuse qu'un téléchargement complet.
Un autre problème fréquent est la duplication de requêtes en vol. Deux parties de l'app demandent la même ressource en même temps (par exemple, un header et un écran de réglages demandent le profil). Sans coalescence, vous envoyez deux appels, parsez deux réponses et mettez à jour l'état deux fois. Traitez un appel réseau comme la source de vérité unique et laissez plusieurs consommateurs attendre dessus.
Le rafraîchissement en arrière‑plan doit être intentionnel. S'il ne change pas ce que l'utilisateur voit bientôt, ou ne déclenche pas de notification, il peut généralement attendre. Évitez d'exécuter la logique de refresh à chaque reprise d'app. Ajoutez un cooldown court et vérifiez quand les données ont été mises à jour pour la dernière fois.
Si vous construisez des backends avec AppMaster, il est plus simple de soutenir ces comportements clients en façonnant les endpoints autour des écrans, en gardant des schémas de réponse cohérents et en ajoutant des en‑têtes de cache de manière contrôlée pour que les clients puissent rester silencieux la plupart du temps.
Erreurs courantes avec le batching, le caching et les payloads
L'objectif est moins de réveils radio, moins de travail CPU et moins de retries. Quelques patterns peuvent annuler les économies et même rendre les écrans plus lents.
Quand le batching aggrave les choses
Le batching aide jusqu'à ce que le lot devienne un appel « donne‑moi tout ». Si le serveur doit joindre de nombreuses tables, exécuter de lourds contrôles de permission et construire une réponse énorme, cette unique requête peut prendre plus de temps que plusieurs petites. Sur mobile, une requête lente maintient l'app en attente, garde le réseau actif et augmente le risque de timeout.
Un lot plus sain est taillé pour l'écran : seulement ce qu'une vue a besoin, avec des limites claires. Si vous ne pouvez pas décrire la réponse en une phrase, l'endpoint est probablement trop large.
Un cache qui crée des écrans périmés
Un cache sans stratégie d'invalidation claire mène à une mauvaise boucle : les utilisateurs voient des données anciennes, ils tirent pour rafraîchir, et l'app effectue des rechargements complets supplémentaires. Utilisez des en‑têtes de cache avec un plan pour ce qui met à jour les données (créations/mises à jour, événements serveur ou fenêtres de fraîcheur courtes) pour que le client puisse faire confiance aux réponses mises en cache.
Le polling est un autre piège de batterie. Un timer serré à 5 secondes garde l'appareil occupé même quand rien ne change. Préférez des mises à jour pilotées par le serveur quand c'est possible, ou décroissez agressivement (intervalles plus longs après des polls vides, pause en arrière‑plan).
Les payloads surdimensionnés sont un coût silencieux. Renvoyer d'énormes objets imbriqués « pour plus de commodité » signifie plus d'octets, plus de parsing JSON et plus de churn mémoire. Envoyez seulement ce que l'écran nécessite et chargez les détails à la demande.
Enfin, n'ignorez pas les échecs partiels dans un batch. Si un sous‑résultat échoue et que vous retentez tout le lot, vous doublez le trafic. Concevez les réponses pour que le client puisse retenter uniquement les parties qui ont échoué.
Une checklist mentale rapide : limitez les batchs, définissez la fraîcheur du cache à l'avance, évitez le polling serré, coupez les payloads et supportez le succès partiel.
Checklist rapide avant mise en production
Avant de livrer, faites une passe axée uniquement sur le comportement réseau. La plupart des gains d'autonomie viennent de la suppression des surprises : moins de réveils, moins de parsing et moins de retries en arrière‑plan.
Exécutez ceci sur vos trois écrans principaux :
- Les cold loads se terminent en un nombre réduit et prévisible de requêtes (surveillez les appels cachés comme des lookups par élément).
- Les réponses incluent des règles de cache claires (
ETagouLast-Modifiedquand pertinent) et retournent304 Not Modifiedquand rien n'a changé. - Les endpoints de liste sont bornés : tri stable, pagination et pas de champs inutilisés par défaut.
- La logique de retry utilise un backoff et du jitter, et s'arrête sur des erreurs qui ne se corrigeront pas d'elles‑mêmes ; le temps total de retry est plafonné.
- Les mises à jour en arrière‑plan sont justifiées ; évitez le polling constant sauf si cela modifie clairement ce que voit l'utilisateur.
Un contrôle de réalité simple : chargez un écran une fois, activez le mode avion, puis rouvrez‑le. S'il affiche encore quelque chose d'utile (contenu mis en cache, dernier état connu, placeholders conviviaux), vous avez probablement réduit les appels inutiles et amélioré aussi la vitesse perçue.
Exemple : rendre l'écran de suivi de commande moins coûteux à charger
Un client ouvre une app de suivi de commande en données mobiles avec 20 % de batterie restante. Il veut une seule chose : « Où est mon colis maintenant ? » L'écran paraît simple, mais le trafic API derrière peut être étonnamment coûteux.
Avant, l'app charge l'écran avec une rafale de requêtes. L'UI attend pendant que la radio se réveille encore et encore, et quelques appels timeout sur une connexion faible.
Un schéma « avant » typique ressemble à :
GET /orders/{id}pour le résumé de la commandeGET /orders/{id}/itemspour les lignesGET /orders/{id}/historypour les événements de statutGET /mepour le profil utilisateur et les préférencesGET /settingspour les règles d'affichage (devise, format de date)
Appliquez maintenant trois changements qui ne modifient pas l'UI.
D'abord, ajoutez un endpoint d'écran unique qui renvoie seulement ce que la vue nécessite en un seul aller‑retour : résumé de commande, dernier statut, historique récent et titres des articles. Deuxièmement, mettez le profil en cache : GET /me renvoie un ETag et Cache-Control: private, max-age=86400, de sorte que la plupart des ouvertures deviennent un 304 rapide (ou pas de requête du tout si la copie cache est encore fraîche). Troisièmement, allégerez le payload : la liste d'items envoie seulement id, name, qty et thumbnail_url, pas de descriptions complètes ni de métadonnées inutilisées.
Le résultat est moins d'allers‑retours, moins d'octets et moins de retries quand le réseau cafouille. La radio du téléphone reste plus souvent en sommeil, et c'est là que résident les vraies économies d'autonomie.
Pour l'utilisateur, rien de « nouveau » n'apparaît. L'écran est le même, mais il se charge plus vite, semble plus réactif et continue de fonctionner quand la connexion est capricieuse.
Étapes suivantes : plan de déploiement pratique (et où AppMaster peut aider)
Si vous voulez des gains rapides, commencez petit et prouvez l'impact. Les économies d'autonomie viennent généralement de moins de réveils radio et de moins de parsing, pas d'une grosse réécriture.
Trois changements sûrs, mesurables et faciles à annuler :
- Mesurez un écran de bout en bout (nombre de requêtes, octets totaux, time-to-interactive, taux d'erreur et de retry)
- Regroupez les requêtes de cet écran en un ou deux appels (conservez les anciens endpoints pour compatibilité)
- Ajoutez le support
ETagsur vos GETs les plus à fort trafic pour que les clients puissent envoyerIf-None-Matchet recevoir304 Not Modified
Choisissez une fonctionnalité à usage stable, comme une liste de commandes ou une boîte de réception. Déployez derrière un flag côté serveur si possible, puis comparez les KPI pour l'ancien et le nouveau chemin sur quelques jours. Recherchez moins de requêtes par session, moins de retries et moins d'octets téléchargés par utilisateur actif.
Coordonnez les releases API et app pour que les anciens clients ne cassent pas. Une règle pratique : ajoutez d'abord le nouveau comportement, migrez les clients ensuite, retirez l'ancien comportement en dernier. Si vous changez le comportement de cache, soyez prudent avec les données personnalisées et assurez‑vous que les caches partagés ne mélangent pas les utilisateurs.
Si vous voulez prototyper et livrer les changements backend plus vite, AppMaster (appmaster.io) peut vous aider à modéliser visuellement les données, construire la logique métier avec un éditeur drag‑and‑drop et régénérer du code source prêt pour la production au fur et à mesure que les exigences évoluent.
Essayez d'abord un endpoint groupé plus ETag sur un écran à fort trafic. Si les chiffres s'améliorent, vous saurez exactement où investir plus de temps d'ingénierie.
FAQ
Un bon point de départ est de définir un budget par écran, puis de mesurer des sessions réelles. Beaucoup d'équipes commencent avec quelque chose comme 4–8 requêtes pour un chargement "cold" d'un écran sur réseau cellulaire, puis réduisent ce nombre une fois les pires cas corrigés. Le bon nombre est celui qui atteint de manière fiable votre objectif de time-to-interactive sans déclencher de retries ou de longues périodes d'activité radio.
Le batching aide généralement quand plusieurs appels se produisent toujours ensemble, mais il peut nuire si le lot devient lent ou énorme. Gardez les réponses groupées « taillées pour l'écran » et bornées pour éviter qu'une requête ne devienne un point de défaillance unique. Si un endpoint groupé échoue régulièrement ou renvoie beaucoup de données inutilisées, remettez-le en plusieurs appels ciblés.
Commencez par ETag + requêtes conditionnelles avec If-None-Match, car cela transforme de nombreuses actualisations en petites réponses 304 Not Modified. Ajoutez Cache-Control en accord avec la fréquence réelle de changement des données pour que le client évite des travaux réseau inutiles. Si ETag est difficile à implémenter, Last-Modified avec If-Modified-Since est un bon recours pour les ressources du type « updated at ».
Utilisez ETag lorsque vous voulez une vérification de « version » fiable d'une ressource, notamment quand les changements ne se mappent pas proprement à un horodatage. Utilisez Last-Modified quand le serveur dispose d'une date de mise à jour claire et que la granularité d'horodatage vous suffit. Si vous ne pouvez en faire qu'un seul, ETag est souvent le meilleur choix pour éviter les téléchargements inutiles.
Instrumentez par écran et par session, pas seulement par endpoint. Enregistrez le nombre de requêtes, les octets (en-têtes plus corps), les retries, les timeouts et le temps jusqu'à ce que l'UI soit utilisable, et séparez le trafic au premier plan du trafic en arrière-plan pour ne pas optimiser la mauvaise chose. Vous trouverez généralement que quelques écrans ou flux causent la majorité des réveils répétés de la radio.
Concevez la réponse de lot pour que chaque sous‑résultat puisse réussir ou échouer indépendamment, et fournissez assez de détails d'erreur pour que le client ne retente que les parties échouées. Évitez de forcer le client à redemander tout le lot parce qu'un élément a pété. Cela réduit le trafic dupliqué et empêche des réveils radio supplémentaires en cas de connectivité instable.
Réduisez les réponses à ce que l'écran affiche maintenant et utilisez des formes résumées pour les listes. Déplacez les champs lourds ou rarement utilisés vers un endpoint de détail qui ne se charge que lorsque l'utilisateur ouvre un élément. Cela réduit les octets sur le réseau et diminue la parsing JSON et les mises à jour de modèles, ce qui peut représenter un coût CPU et batterie non négligeable sur les téléphones.
Utilisez un backoff exponentiel avec jitter et plafonnez la fenêtre totale de retry pour que le téléphone ne se réveille pas toutes les quelques secondes. Rendez les opérations d'écriture idempotentes pour qu'un retry n'engendre pas de doublons. Retournez aussi des codes d'état clairs pour que le client cesse de réessayer quand l'erreur ne se résoudra pas d'elle‑même.
Des intervalles de polling serrés maintiennent la radio et le CPU occupés même quand rien ne change. Si vous devez poller, augmentez les intervalles quand les réponses ne changent pas et mettez le polling en pause en arrière-plan. Quand c'est possible, passez à des mises à jour pilotées par événements pour que l'app ne se réveille que lorsqu'il y a du nouveau à afficher.
Dans AppMaster, vous pouvez créer des endpoints orientés écran et garder des schémas de réponse cohérents, ce qui facilite le batching et le façonnage des payloads. Vous pouvez aussi ajouter une logique de versionnement et renvoyer des en-têtes qui supportent les requêtes conditionnelles afin que les clients obtiennent des réponses rapides « pas de changement ». Une approche pratique : commencer par un écran à fort trafic, déployer un endpoint groupé et ajouter ETag sur ses GETs clés, puis mesurer la baisse des requêtes et des retries.


