Ingénierie

Pourquoi les applications de terrain échouent : concevoir en hors-ligne d'abord

Une application démontrée sur le wifi du bureau et utilisée à l'entrée d'un chantier avec une barre de 4G sont deux logiciels différents. Un seul des deux a été testé.

Lecture : 9 min Plexowave

Le schéma est assez constant pour être prévisible. Une application de terrain est commandée, construite avec compétence, démontrée avec succès et déployée. En deux mois l'équipe de terrain est revenue au papier, et la raison invoquée est qu'« elle perdait les données ». En général elle n'a pas perdu les données — elle ne les a pas reçues, puis n'a rien dit, et la différence est invisible pour celui qui se tient à l'entrée.

Une fois que le personnel de terrain cesse de faire confiance à une application, il cesse de l'utiliser, et aucune fonctionnalité ne le ramène. La confiance est le véritable produit, et elle se décide à ce que l'application fait sur une mauvaise connexion.

Pourquoi les hypothèses de connectivité s'effondrent

Le wifi du bureau n'est pas une version affaiblie d'une connexion de chantier — c'est autre chose. Un chantier a une couverture intermittente et non lente : plein signal près du bureau, rien derrière la structure, et un portail captif dans le bâtiment du client qui renvoie HTTP 200 à chaque requête avec une page de connexion dans le corps.

Ce dernier cas mérite qu'on s'y arrête, car il met en défaut les vérifications naïves de connectivité. L'appareil signale un réseau. La requête aboutit. La réponse n'est pas celle demandée. Une application qui teste « y a-t-il une connexion » plutôt que « le serveur a-t-il vraiment répondu » croira volontiers avoir enregistré quelque chose qu'elle n'a pas enregistré.

L'autre hypothèse qui s'effondre est celle de la durée. Une application qui s'attend à se synchroniser en quelques minutes se comporte autrement qu'une application devant gérer un appareil hors ligne trois jours, pendant lesquels les données du serveur ont évolué. Le second cas est la norme sur le terrain et il change la conception.

Hors ligne d'abord signifie que le stockage local fait foi

La distinction n'est pas « nous mettons certaines choses en cache ». Dans une conception hors-ligne d'abord, la base locale est là où vit le travail de l'utilisateur, les écritures se terminent localement et reviennent immédiatement, et la synchronisation est un processus d'arrière-plan qui se réconcilie plus tard avec le serveur. Le réseau est une amélioration, pas une condition préalable.

Cela inverse la gestion d'erreur habituelle. Dans une application en ligne d'abord, l'absence de réseau est une erreur dont l'utilisateur doit s'occuper. Dans une application hors-ligne d'abord, l'absence de réseau est un état normal et l'interface le reflète — l'enregistrement est sauvegardé, il est en file, il partira quand il le pourra, et l'utilisateur peut voir que c'est vrai.

Choisir le modèle de conflit délibérément

Deux personnes modifient le même enregistrement, toutes deux hors ligne. Ce qui se passe ensuite est votre modèle de conflit, et si personne n'en a choisi un, le code l'a choisi par accident — généralement la dernière écriture gagne, en silence, et le travail de l'un disparaît.

Il n'existe que quelques options viables, et la bonne dépend des données.

  • La dernière écriture gagne est acceptable pour des champs réellement indépendants et pour des données qu'un léger retard ne gêne pas. C'est le mauvais choix pour tout ce qui est financier ou pour tout ce sur quoi un litige pourrait porter.
  • Les journaux d'événements en ajout seul évitent totalement les conflits en ne modifiant jamais sur place. Les pointages de présence, les sorties de matière, les mouvements de stock et les relevés d'inspection s'y prêtent naturellement — deux chefs d'équipe qui pointent ajoutent des faits, ils ne se disputent pas l'écrasement d'un même enregistrement.
  • La fusion au niveau des champs fonctionne quand un enregistrement a des sections relevant de rôles différents, si bien que deux modifications ne touchent en général pas les mêmes champs.
  • La résolution explicite — montrer les deux versions et demander — est correcte là où la donnée compte assez pour mériter l'attention de quelqu'un, et intolérable si cela arrive souvent. Si votre modèle en a besoin fréquemment, le modèle est mauvais.

Dans les opérations de terrain, l'ajout seul convient à bien plus de cas que les équipes ne l'imaginent, et il supprime par construction la classe de bugs la plus difficile. Cela vaut la peine de restructurer les données pour y parvenir.

La file d'attente est la partie qui doit être à toute épreuve

Tout ce qu'un utilisateur fait hors ligne entre dans une file, et c'est là que vivent tous les modes de défaillance sérieux. La file doit survivre à l'arrêt de l'application par le système d'exploitation, au redémarrage du téléphone, et à la mise à jour vers une version dont la forme des données a changé.

Trois règles le rendent fiable. Chaque opération en file porte un identifiant généré par le client pour que le serveur reconnaisse un doublon — les réessais sont certains, et sans idempotence ils créent des enregistrements en double. Les opérations s'appliquent dans l'ordre là où l'ordre compte, car une création qui arrive après sa propre mise à jour est un bug de perte de données. Et un élément définitivement en échec doit être mis en quarantaine et signalé plutôt que réessayé indéfiniment, sinon un mauvais enregistrement bloque tous les bons derrière lui.

Ce dernier cas est la défaillance la plus courante en production. Un enregistrement que le serveur rejette pour une raison de validation reste en tête de file et se réessaie, pendant que tout ce qui est derrière attend. Le téléphone affiche « synchronisation ». Rien n'a été synchronisé depuis deux jours.

Rendre l'état de synchronisation visible

Les utilisateurs pardonnent à une application de ne pas joindre le serveur. Ils ne lui pardonnent pas de ne pas pouvoir leur dire si elle y est parvenue. Chaque enregistrement devrait indiquer s'il est enregistré localement, en file, synchronisé ou en échec, et il devrait exister un écran indiquant quand a eu lieu la dernière synchronisation réussie et combien d'éléments attendent.

Cela transforme aussi le support. « Ça n'a pas été enregistré » est sans réponse. « Trois éléments en file, dernière synchronisation il y a 2 jours, un échec sur une erreur de validation » est une question qui a une réponse, et cette réponse peut se donner au téléphone.

Un traitement en arrière-plan qui tourne vraiment

L'optimisation de batterie d'Android arrêtera une synchronisation d'arrière-plan naïve, et les surcouches des constructeurs sont plus agressives qu'Android brut — certaines de très loin. Une application qui se synchronise fiablement sur un Pixel peut ne pas se synchroniser du tout sur un téléphone dont le constructeur tue agressivement le travail d'arrière-plan, et ces téléphones sont courants précisément dans la gamme de prix qu'a le personnel de terrain.

Utilisez le planificateur de la plateforme plutôt qu'un minuteur, synchronisez dès que possible quand l'application passe au premier plan, et ne comptez jamais uniquement sur le traitement en arrière-plan pour quelque chose dont l'utilisateur a besoin qu'il ait eu lieu. Testez sur les téléphones bon marché que portent vos utilisateurs, pas sur l'appareil du développeur.

Tester sur le réseau qu'ils ont

  • Mode avion pendant une journée de travail entière, puis reconnexion et vérification que tout arrive exactement une fois.
  • Une connexion bridée et avec pertes plutôt que simplement lente — les modes de défaillance diffèrent, et c'est la perte qui casse les choses.
  • Un portail captif qui renvoie 200 avec une page de connexion, pour confirmer que l'application vérifie la réponse et non la présence d'un réseau.
  • Fermez l'application en pleine synchronisation, redémarrez le téléphone en pleine synchronisation, et vérifiez que la file survit aux deux.
  • Deux appareils modifiant le même enregistrement hors ligne, pour confirmer que le modèle de conflit fait ce que vous avez décidé et non ce que le framework fait par défaut.
  • Une ancienne version de l'application qui se synchronise avec un serveur à jour, car sur le terrain quelqu'un a toujours trois versions de retard.

En résumé

Le hors-ligne n'est pas une fonctionnalité qu'on ajoute vers la fin. Il détermine le modèle de données, les règles de conflit, la conception de la file et l'interface, et l'ajouter après coup revient presque à réécrire. Décidez-le d'abord, faites du stockage local la référence, choisissez le modèle de conflit explicitement, rendez l'état de synchronisation visible, et testez sur une mauvaise connexion plutôt que sur une bonne.

Questions

Questions fréquentes.

Toute application mobile doit-elle être hors ligne d'abord ?

Non. Une application grand public utilisée sur un téléphone avec une connectivité normale peut raisonnablement exiger un réseau. Cela compte quand les utilisateurs sont dans des zones mal couvertes, quand perdre une saisie a de vraies conséquences, ou quand revenir au papier leur est facile — ce qui décrit la plupart du travail de terrain.

Le hors-ligne d'abord rend-il l'application beaucoup plus chère ?

Cela ajoute un coût réel au départ — surtout dans la conception de la synchronisation et des conflits — et en économise bien davantage ensuite, car l'ajouter après coup touche à la fois le modèle de données, l'interface et l'API. Décidé en amont c'est un choix de conception ; décidé plus tard c'est une réécriture.

Et les conflits que l'on ne peut pas résoudre automatiquement ?

Conservez les deux versions, présentez-les à quelqu'un qui peut trancher, et ne supprimez jamais en silence. Si cela arrive souvent, c'est le modèle de données qui est en cause — une restructuration vers des événements en ajout seul supprime en général toute la classe.

Combien de temps les données doivent-elles rester sur l'appareil ?

Assez longtemps pour couvrir une période hors ligne réaliste avec une marge, et pas davantage. Gardez une règle de conservation explicite, car un stockage local sans limite finit par remplir les appareils bon marché et l'application est accusée de ralentir le téléphone.

Vous êtes face à cette décision ?

Décrivez le problème plutôt que la solution. Si la réponse est une plateforme que vous pouvez acheter, ou aucun logiciel du tout, nous le dirons.

Démarrer un projet