Skip to content

Blog


Remise à niveau de la plate-forme FAQ de Dasher grâce à un contenu piloté par le serveur et à une vue Web

24 octobre 2023

|
Kent Lee

Kent Lee

Jason Prasad

Jason Prasad

Siddharth Utgikar

Siddharth Utgikar

Josephine Chen

Josephine Chen

Chez DoorDash, le " dashing " dépend fortement du processus. Les Dashers doivent maîtriser le processus de livraison de bout en bout pour compléter les commandes avec succès - et gagner de l'argent. La première itération du contenu d'assistance de DoorDash a fait plus pour expliquer comment faire du dashing, gérer les problèmes de livraison courants et les pièges, et maximiser l'expérience du dashing que les itérations suivantes n'ont fait.

Notre récent effort pour réorganiser le centre d'assistance Dasher a permis de mettre en place un système qui permet aux Dashers de trouver des réponses de manière indépendante, ce qui réduit les coûts d'assistance. Cependant, les ressources disponibles pour répondre aux questions courantes des Dashers étaient.. : 

  1. Difficile à trouver parce que les ressources sont réparties sur de nombreux canaux et sources ; les Dashers doivent déjà savoir exactement où chercher s'ils espèrent trouver des réponses, et 
  2. Parfois, le contenu n'aidait pas à résoudre le problème d'un Dasher, ce qui entraînait de longs appels avec un agent en chair et en os pour parvenir à une solution.

Dans le cadre de la refonte, les articles de la FAQ devaient être concis et permettre des changements rapides, comme l'ajout de captures d'écran de l'application ou de modifications des lignes directrices à venir. La mise en place d'un système permettant cette flexibilité sur chaque plateforme mobile nécessiterait un double développement et exigerait également que chaque application native soit mise à jour lorsque la structure du contenu est modifiée. Le déploiement des applications natives peut prendre des semaines, alors que les déploiements sur le web peuvent être réalisés immédiatement. Nous avons donc décidé de créer une application web commune qui peut être hébergée dans l'application native afin de fournir une expérience in-app tout en offrant la flexibilité nécessaire pour effectuer des changements rapides.

Architecture

Figure 1 : La première itération de notre solution (MVP) suivait cette architecture : Mobile (iOS/Android) ⇒ WebView ⇒ Backend for Frontend (BFF) ⇒ Backend Service.

Comme le montre la figure 1 ci-dessus, les deux clients mobiles utilisent WebView, ce qui garantit une expérience identique d'un client à l'autre. Certains menus, comme la prise en charge du chat dans l'application, déploient des rappels spécifiques au client mobile pour faire le lien entre l'application native et l'affichage Web. 

Le BFF est utilisé pour transformer le format de la demande de HTTP en gRPC afin de permettre au service dorsal de l'analyser. La réponse est également transformée de gRPC en HTTP. Aucune logique commerciale n'est gérée au niveau de la couche BFF ; elle existe uniquement pour formater les données provenant du service dorsal dans un format adapté à l'application web.

Le service d'arrière-plan stocke et sert le contenu. Le contenu des articles est stocké sous forme de JSON qui contient non seulement le contenu mais aussi la mise en forme de l'interface utilisateur. Dans MVP, tous les articles, catégories, menus et snippets sont stockés sous forme de fixtures JSON. Chaque modification de l'article nécessite un formatage manuel de l'article, la mise à jour de la fixation JSON et le déploiement du service backend.

Champ d'application du MVP

Pour minimiser la portée du MVP, nous avons limité le contenu au marché américain. Un contenu localisé avec un contenu spécifique à la langue et au pays ou à la région aurait compliqué le lancement initial ; bien que nous ayons gardé ces fonctionnalités à l'esprit, nous ne les avons pas implémentées dans le MVP.

Au départ, nous avons conservé le contenu dans des montages JSON plutôt que d'investir dans une base de données complète. Les fixtures JSON nous ont permis d'itérer rapidement et de manière répétée à travers le format de données. L'inconvénient est que nous n'avons pas pu utiliser une base de données plus robuste : Nous n'avons pas pu utiliser un système de recherche plus robuste. Au lieu de cela, nous avons simplifié l'utilisation d'une recherche de base uniquement sur les titres d'articles en utilisant la distance de Levenshtein pour tenir compte des fautes d'orthographe. Tous les tags, catégories, menus et contenus d'articles figurant dans la FAQ ont dû être formatés manuellement en JSON et téléchargés.

Protocole de communication

De nombreux messages sont envoyés pour permettre des transitions transparentes entre les applications natives et la fonction FAQ exécutée dans une WebView. Par exemple, la WebView peut avoir besoin du contexte Dasher ou une interaction avec l'interface utilisateur peut nécessiter de revenir à une vue native.

Android et iOS disposent tous deux de méthodes pour envoyer des messages vers et depuis leurs WebViews. Pour envoyer des messages de la WebView à l'application native, le côté natif doit enregistrer des gestionnaires ou utiliser la délégation. Par exemple, le côté natif peut enregistrer un gestionnaire appelé "WebViewModuleCallbacks". Du côté de la WebView, il s'agira d'une propriété de la fenêtre de messages. 

// For Android
WebViewModuleCallbacks?.postMessage(...)
// For iOS
webkit?.messageHandlers?.WebViewModuleCallbacks?.postMessage({...})

Pour envoyer des messages du côté natif au WebView, JavaScript doit être évalué sous la forme d'une chaîne. Par exemple, sur iOS :

WebView.evaluateJavaScript("WebViewModule.completeCall(1234, { payload: \"somePayload\" }")

Il convient de noter que le code JavaScript évalué doit faire référence à quelque chose qui est déjà disponible au moment de l'exécution dans l'application web. C'est pourquoi une instance globale capable de répondre aux messages a été mise en place pour la fonction FAQ (c'est-à-dire "WebViewModule").

Nous avons rencontré quelques difficultés avec le modèle de messagerie suivant :

  1. Par défaut, le destinataire du message n'accuse pas réception et ne renvoie pas de résultat en réponse.
  2.  Il fallait trouver un moyen de gérer la complexité des différents types de messages au fur et à mesure de l'évolution des besoins.
  3. Les fonctionnalités et les versions du client changent constamment ; un message qui a pu être introduit ne sera pas disponible dans les versions antérieures de l'application.

Un modèle basé sur les promesses JavaScript a été mis en œuvre pour résoudre le premier défi identifié. Pour ce faire, une instance de l'application web a conservé une collection de promesses de messages en cours. Ces promesses de message peuvent être référencées ultérieurement par un identifiant. Lorsque l'application web se prépare à envoyer un message à un natif, elle crée une nouvelle promesse dans la collection avec un identifiant. L'identifiant est ensuite transmis avec le contenu du message de l'application web à l'application native. Pour compléter le message, l'application native évalue le JavaScript et renvoie l'identifiant et les résultats éventuels, remplissant ainsi la promesse.

Pour résoudre le second problème, un contrat de messages typés est établi pour gérer la complexité de la gestion de plusieurs types de messages :

export type ScriptMessage = {
   name: string
   payload?: Record<string, unknown>
}

Le type "ScriptMessage" porte le nom qui identifie l'intention du message. Ce nom permet aux applications natives et web de changer le traitement spécifique du message. Le destinataire du message doit savoir comment décompresser le contenu de la charge utile.

Nous nous efforçons toujours de résoudre le troisième problème - la cible mouvante des versions des clients - par des changements incrémentaux. Si l'on considère le modèle basé sur les promesses décrit précédemment, si l'application web envoie un message qui n'est pas pris en charge par le côté natif, cela pourrait bloquer l'application pendant qu'elle attend la réalisation de la promesse. Pour éviter cela, les applications natives transmettent actuellement leur version dans un message de configuration initial. L'application web conserve les versions connues des clients natifs et les compare lors du traitement des messages.

WebView

Pour un ingénieur web, une WebView est simplement une autre application avec quelques protocoles intégrés qui permettent une communication bidirectionnelle entre les couches mobile et web.

Pour la pile technologique WebView, nous nous appuyons sur les cadres et bibliothèques existants de DoorDash, qui comprennent React, React Query et notre système de conception interne. Nous avons choisi d'utiliser React Query pour la récupération des données et la gestion de l'état parce qu'il a des capacités de cache et d'invalidation de cache dès le départ et qu'il fonctionne bien avec notre BFF par le biais d'appels API REST (representational state transfer).

Points d'entrée et de sortie du WebView 

Notre application expose plusieurs points d'entrée basés sur l'origine de l'utilisateur dans l'application Dasher, tels que les paramètres du compte → le contenu pré-filtré de WebView à la FAQ du compte. Il n'est donc plus nécessaire de parcourir l'ensemble du contenu. Le protocole permettant d'établir un lien profond entre le mobile et le WebView se présente comme suit :

// The entry point will be specified by the callee of the FAQ module
enum EntryPoint {
    case dashing
    case earnings
    /* ... */
}
// Based on the above entry point the filter query param will be populated
webView?.load(
    URL(string: "https://<app-base-url>?filter=\(entryPoint.rawValue)")!
)

Nous utilisons également un protocole de messagerie pour minimiser un article. Ainsi, lorsqu'un utilisateur décide de naviguer dans l'application mobile après avoir consulté le contenu de l'article et joué avec les fonctionnalités, il ne perd pas l'accès à l'article qu'il était en train de consulter. Les gestionnaires sont attachés à une icône ; au moment du clic, ils envoient un message au client mobile natif pour qu'il écoute l'événement.

Définition du gestionnaire de messages

const postMessageToNative = (args) => {
  if (webViewHost === 'iOS') {
    setTimeout(() => {
      webkit?.messageHandlers?.webViewModuleCallbacks?.postMessage(args)
    }, 0)
  } else if (webViewHost === 'Android') {
    webViewModuleCallbacks?.postMessage(JSON.stringify(args))
  } else {
    // Web code: do nothing
  }
}

Contenu de l'interface utilisateur piloté par le serveur

Pour soutenir un système plus robuste basé sur le contenu, nous avons ajouté tout le contenu de nos articles de FAQ dans le backend. Nous récupérons ensuite les données via WebView. Cela nous permet d'avoir une seule source de vérité et facilite les mises à jour grâce à un outil de configuration. En outre, si nous décidons un jour d'exposer cette application en tant qu'application web autonome, nous disposerons toujours du contenu.

À partir de ce projet, nous avons développé un prototype d'outil de configuration du contenu qui crée le contenu de l'article plutôt que de l'ajouter sur le client en tant qu'élément fixe. Le JSON complexe décrit la structure de l'article de la FAQ. Cela permet d'étendre à l'avenir la collection de bases de connaissances sans effort de développement. 

Voici un exemple de la structure du contenu. L'anatomie de la page de l'article est partiellement pilotée par le serveur et partiellement par le client :

Figure 2 : Mise en page d'un article, en partie pilotée par le client et en partie pilotée par le serveur

Cet outil nous a permis de construire une bibliothèque de composants qui mappe l'objet de la réponse en composants de nœuds de texte et exporte une fonction utilitaire de rendu pour que le Dasher WebView puisse l'utiliser.

Nous pensons que cet outil sera très utile à l'avenir lorsque nous devrons étendre ses capacités à d'autres initiatives d'interface utilisateur pilotées par le serveur, telles qu'un constructeur d'écran pour les mises en page.

Défis liés au développement et au déploiement initial

La création d'une fonctionnalité dans une application native présentée dans une WebView pose des défis supplémentaires en matière de développement, de débogage et de validation. Pour comprendre si quelque chose fonctionne vraiment - et si ce n'est pas le cas, où cela s'est cassé - il faut connaître les piles natives et web.

Par exemple, lors du développement de la fonction FAQ, un défaut est apparu dans le protocole de communication basé sur les promesses. Pour déboguer correctement, nous devions exécuter l'application web localement, émettre une carte source pour le TypeScript transpilé, autoriser et diriger l'application native vers l'application web exécutée localement, marquer la WebView comme inspectable dans iOS ou Android, et s'attacher à la WebView en cours d'exécution à partir de Safari ou de Chrome. Cela peut s'avérer difficile pour un développeur web ou un développeur natif isolé, c'est pourquoi nous pratiquons souvent le couplage.

Résultats et travaux futurs

Le développement de la plate-forme d'assistance Dasher en tant que WebView a présenté de nombreux avantages notables, notamment

  • Déploiements rapides: Les déploiements sur le web sont rapides et simples, sans friction pour l'adoption par les utilisateurs. En revanche, le déploiement d'une application mobile prend environ une semaine, et il faut compter environ deux semaines pour obtenir des pourcentages d'adoption significatifs. Les changements de WebView nécessitent moins d'une journée.
  • Mises à jour du contenu: Le contenu étant stocké sur le serveur, il peut être mis à jour rapidement et fréquemment, en envoyant la version la plus récente au client sans qu'il soit nécessaire de procéder à des mises à jour mobiles.

Cela nous a également permis de créer des projets supplémentaires qui contribueront à la création d'outils d'assistance pour nos clients et nos agents et qui seront étendus, notamment :

  • Outil de configuration du contenu: Nous pouvons développer cet outil pour configurer d'autres flux web, réduisant ainsi les efforts de développement pour créer des flux similaires dans nos produits mobiles et web.
  • Bibliothèque de composants pour le contenu de l'interface utilisateur pilotée par le serveur: Ces composants gèrent le rendu du JSON complexe et peuvent également être étendus pour gérer différentes mises en page.

Conclusion / Résumé

La création d'un centre d'assistance Dasher amélioré dans l'application était cruciale pour offrir aux Dashers un meilleur accès à l'information. Les Dashers ont besoin d'un contenu clair, concis et facilement accessible qui apporte des solutions aux problèmes rapidement, sans avoir à passer par des agents en chair et en os. Les informations doivent également pouvoir être facilement mises à jour, ce qui nécessite une solution unique pour éviter les longs délais de déploiement ou le travail en double sur Android et iOS. La création d'une application web commune nous a permis de contourner les difficultés liées à la mise à jour des applications natives, de réduire considérablement le temps de déploiement et de donner aux Dashers l'accès aux informations les plus récentes.

Restez informé grâce aux mises à jour hebdomadaires

Abonnez-vous à notre blog d'ingénierie pour recevoir régulièrement des informations sur les projets les plus intéressants sur lesquels notre équipe travaille.

About the Authors

  • Kent Lee

    Kent is a Software Engineer on the Customer Enablement Team at DoorDash. His focus is on Web Engineering.

  • Jason Prasad

    Jason is a Software Engineer on the Customer Enablement Team at DoorDash. His focus is on iOS engineering.

  • Siddharth Utgikar

    Siddharth Utgikar is a Software Engineer on Customer Enablement Team at DoorDash. His focus is Android Engineering.

  • Josephine Chen

    Josephine is a Software Engineer on the Customer Enablement Team at DoorDash. Her focus is on fullstack engineering.

Emplois connexes

Localisation
Oakland, CA ; San Francisco, CA
Département
Ingénierie
Localisation
Oakland, CA ; San Francisco, CA
Département
Ingénierie
Job ID: 2980899
Localisation
San Francisco, CA ; Sunnyvale, CA
Département
Ingénierie
Localisation
Pune, Inde
Département
Ingénierie
Localisation
Pune, Inde
Département
Ingénierie