Le chatbot interne marchait bien. Trop bien.
C'était une interface simple : un champ de texte, des réponses générées par notre base de connaissances interne. RAG, embeddings, le pipeline classique. Les consultants l'utilisaient pour retrouver des procédures, des templates, des informations client.
Puis les demandes ont commencé. "Ce serait bien sur mobile." "Je voudrais l'utiliser dans le métro." "Tu peux faire une app ?"
On aurait pu créer une app React Native. Ou Flutter. Gérer deux codebases. Passer par les stores. Attendre les validations.
Ou alors transformer l'existant en PWA.
Pourquoi la PWA plutôt que le natif
Pour un outil interne, les stores sont un cauchemar. Apple demande des justifications pour les apps "privées". Google a ses propres contraintes. Les mises à jour prennent des jours à se propager.
Une PWA, c'est :
- Installation instantanée depuis le navigateur
- Mises à jour automatiques à chaque visite
- Une seule codebase pour desktop et mobile
- Pas de validation store pour un outil interne
Le compromis ? Pas d'accès aux APIs natives avancées (NFC, Bluetooth). Pour un chatbot, on n'en avait pas besoin.
L'implémentation en trois étapes
Le manifest
Le fichier manifest.json définit comment l'app apparaît une fois installée. Nom, icônes, couleurs, mode d'affichage.
{
"name": "Assistant Interne",
"short_name": "Assistant",
"start_url": "/chat",
"display": "standalone",
"background_color": "#1a1a2e",
"theme_color": "#4a90d9",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
display: standalone est crucial. Ça fait disparaître la barre d'URL du navigateur. L'app ressemble à une app native.
Le service worker
Le service worker gère le cache et les requêtes. La stratégie choisie était simple : mettre en cache l'UI, pas les données.
// sw.js
const CACHE_NAME = 'assistant-v1';
const STATIC_ASSETS = [
'/',
'/chat',
'/css/app.css',
'/js/app.js',
'/icons/icon-192.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(STATIC_ASSETS))
);
});
self.addEventListener('fetch', event => {
// Cache-first pour les assets statiques
// Network-first pour les appels API
if (event.request.url.includes('/api/')) {
event.respondWith(fetch(event.request));
} else {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
}
});
Les réponses du chatbot viennent toujours du serveur (network-first). L'interface se charge depuis le cache (cache-first). Résultat : l'app démarre instantanément même sur connexion lente.
Le prompt d'installation
Le navigateur peut afficher un prompt d'installation, mais seulement si on le capture correctement.
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
showInstallButton();
});
function installApp() {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((result) => {
if (result.outcome === 'accepted') {
hideInstallButton();
}
deferredPrompt = null;
});
}
}
Un bouton "Installer l'app" a été ajouté dans le menu. Discret mais visible. Les utilisateurs curieux cliquent. Les autres continuent sur le web.
Les notifications push
La plateforme avait aussi un forum interne où les consultants pouvaient poser des questions et taguer des collègues ou des experts sur des sujets précis. Avant, les mentions généraient un email pour ramener l'utilisateur vers l'app. Mais un email noyé dans une boîte de réception saturée, ça peut attendre des heures.
Avec la PWA, les mentions déclenchent maintenant des notifications push. Sur desktop, c'était des notifications navigateur classiques. Sur mobile installé, ça devient de vraies notifications système.
// Demander la permission
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
subscribeUserToPush();
}
});
// Recevoir dans le service worker
self.addEventListener('push', event => {
const data = event.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192.png',
badge: '/icons/badge.png'
});
});
Le forum est devenu beaucoup plus dynamique. Quelqu'un tague un expert, l'expert reçoit une notif sur son téléphone, ouvre l'app, répond dans les minutes qui suivent. Le temps de réponse moyen est passé de plusieurs heures à quelques minutes. Le taux d'engagement a explosé — les notifications push ont un taux d'ouverture 3x supérieur aux emails.
Ce qu'on n'a pas fait
Pas de mode offline complet. Le chatbot a besoin du serveur pour répondre. Pas de réponses en cache. Juste un message "Connexion requise" propre quand le réseau est indisponible.
Pas de synchronisation en arrière-plan. Pour un chatbot, ça n'a pas de sens. Les messages sont synchrones. S'il y avait eu des formulaires à soumettre offline, on aurait implémenté Background Sync.
Pas d'App Store. Même si les PWA peuvent être publiées sur les stores maintenant, ça n'avait pas de sens pour un outil interne. L'installation directe suffit.
Les résultats
Une semaine après le déploiement :
- Plus de 100 installations sur les téléphones des employés
- Usage mobile passé de 5% à 35% du trafic total
- Temps de réponse moyen en déplacement : identique au desktop
- Zéro ticket support lié à l'installation
Le plus surprenant : les utilisateurs ne savent pas que c'est une PWA. Ils disent "l'app". Ça ressemble à une app. Ça se comporte comme une app. Pour eux, c'est une app.
Ce qu'on en retient
Pendant des années, on a cru que "mobile" signifiait "app native". Que les PWA étaient un compromis pour les projets sans budget.
On avait tort. Pour un outil interne, une PWA est souvent le meilleur choix. Installation instantanée. Mises à jour transparentes. Une seule codebase.
Les équipes accèdent maintenant au chatbot depuis leur téléphone, dans le métro, entre deux réunions. Exactement ce qu'elles voulaient. Sans passer par l'App Store. Sans attendre de validation. En une semaine au lieu de deux mois.