Blog
Build in Public
26 mars 20268 min

Sprint qualite : corriger les bugs silencieux qui font desinstaller

Un rappel qui disparaît sans prévenir, un email qui n'arrive jamais, une erreur fantôme au démarrage : ces bugs ne font pas la une d'un changelog, mais ils détruisent la confiance de tes utilisateurs. En une journée de sprint qualité, j'ai corrigé quatre problèmes silencieux dans TAMSIV — mon gestionnaire de tâches vocal pour Android — et l'expérience au quotidien a radicalement changé.

Points clés à retenir :
- Les bugs silencieux (rappels supprimés, emails muets, erreurs flash) causent plus de désinstallations que les crashes visibles.
- Activer push ET email par défaut pour les rappels élimine 90% des tickets "je n'ai pas été notifié".
- Un sprint qualité sans nouvelle feature est l'investissement le plus rentable pour la rétention utilisateur.
- La traduction complète de chaque fonctionnalité (ici les badges en 6 langues) est un signal de respect envers les utilisateurs internationaux.
Développeur travaillant de nuit devant des écrans de code, ambiance lampe de bureau chaleureuse
Les sprints qualité se passent souvent tard le soir, quand on peut enfin se concentrer sur les détails qui comptent.

Pourquoi un sprint qualité est plus important qu'une nouvelle feature ?

Quand tu développes une app en solo, la tentation est permanente : ajouter des fonctionnalités. Un nouveau filtre ici, une intégration là. C'est gratifiant, ça se montre facilement en screenshot, ça fait un beau commit.

Mais la réalité, c'est que les utilisateurs ne désinstallent pas une app parce qu'il manque une feature. Ils désinstallent parce qu'un rappel n'a pas fonctionné. Parce qu'un email n'est jamais arrivé. Parce qu'une erreur incompréhensible est apparue au démarrage.

Selon une étude de UserTesting, 88% des utilisateurs ne reviennent jamais après une mauvaise expérience. Et les bugs silencieux — ceux qui ne font pas crasher l'app mais qui érosent la confiance — sont les plus dangereux. Tu ne reçois pas de rapport de crash. L'utilisateur ne signale rien. Il part, c'est tout.

C'est pour ça que j'ai décidé de consacrer une journée entière à un sprint 100% qualité. Pas de nouvelle fonctionnalité. Pas de refactoring ambitieux. Juste quatre corrections chirurgicales qui, mises bout à bout, changent vraiment l'expérience de TAMSIV au quotidien.

Comment des rappels valides peuvent-ils disparaître en silence ?

Le scénario : tu crées une tâche avec trois rappels — un dans 10 minutes, un demain matin, un vendredi. Sauf que le premier rappel est déjà dans le passé (tu as mis trop de temps à valider). TAMSIV affichait un avertissement : "Ce rappel est dans le passé". Jusque-là, logique.

Le problème ? En fermant cet avertissement, tous les rappels étaient supprimés. Les deux rappels futurs, parfaitement valides, partaient avec. Un nettoyage un peu trop zélé dans le code de validation.

Ce genre de bug est particulièrement vicieux. L'utilisateur ne sait même pas que ses rappels ont disparu. Il attend la notification de demain matin... qui ne viendra jamais. Et il blâmera l'app — à juste titre.

La correction technique

La solution a consisté à séparer clairement les rappels passés des rappels futurs dans la logique de validation. Concrètement :

  • Filtre temporel : seuls les rappels dont la date est antérieure à Date.now() sont marqués comme expirés.
  • Avertissement ciblé : le message d'alerte ne concerne que les rappels effectivement dans le passé, avec leur nombre exact.
  • Conservation garantie : les rappels futurs restent intacts, quoi que l'utilisateur fasse avec l'avertissement.
  • Fuseaux horaires : j'en ai profité pour renforcer la gestion des timezones dans la validation — un cas limite que j'avais sous-estimé.

Le système de rappels et récurrences est un pilier de TAMSIV. Un bug ici, même minime, a un impact disproportionné sur la confiance utilisateur.

Pourquoi les emails de rappel n'arrivaient-ils jamais ?

TAMSIV supporte deux canaux de notification pour les rappels : le push (notification sur le téléphone) et l'email. En théorie. En pratique, quand un utilisateur créait un rappel, seul le canal push était activé par défaut. L'email ? Désactivé. Silencieusement.

Smartphone affichant des icônes de notification email et push sur fond sombre
Push et email : les deux canaux doivent être actifs par défaut pour couvrir tous les contextes d'usage.

Si tu n'allais pas manuellement cocher "email" dans les paramètres du rappel, tu ne recevais jamais rien dans ta boîte mail. Pas d'erreur, pas de message — juste le silence. Et pour un utilisateur qui ne consulte pas systématiquement son téléphone, c'est un rappel complètement raté.

Quel est le bon défaut pour les canaux de notification ?

La question semble triviale, mais elle ne l'est pas. Comme le recommande le Nielsen Norman Group dans ses recherches sur les valeurs par défaut, le défaut doit correspondre à ce que la majorité des utilisateurs attend. Et la majorité attend d'être notifiée — par tous les canaux disponibles.

Maintenant, les deux canaux sont activés par défaut. Tu reçois un push et un email. Si tu veux désactiver l'un des deux, c'est toujours possible, mais le comportement par défaut est celui que tout le monde attend. C'est le principe du sane default — un concept central dans la conception d'applications de productivité.

D'où vient cette erreur "Invalid Refresh Token" au démarrage ?

Celle-là, c'est le genre de bug qui ne casse rien mais qui mine la confiance. Tu ouvres l'app après quelques heures d'inactivité, et pendant une fraction de seconde, une erreur "Invalid Refresh Token" flashe à l'écran. Puis tout fonctionne normalement.

Imagine : tu ouvres TAMSIV le matin pour voir tes tâches du jour, et la première chose que tu vois, c'est une erreur. Tu ne sais pas ce qu'elle signifie. Tu ne sais pas si tes données sont en sécurité. Tu ne sais pas si l'app fonctionne correctement. Même si tout est parfaitement OK en réalité, l'impression est désastreuse.

L'analyse technique du problème

Au démarrage à froid, Supabase Auth tentait de rafraîchir le token d'authentification. Si le token avait expiré (après quelques heures d'inactivité), le SDK levait une erreur avant que le mécanisme de reconnexion automatique n'ait le temps de faire son travail.

L'erreur remontait jusqu'à l'UI alors qu'elle n'avait aucune raison d'y être — la reconnexion finissait toujours par réussir. C'est un pattern classique dans les apps utilisant des tokens JWT avec refresh automatique : le premier appel échoue, le refresh se déclenche, le deuxième appel réussit.

La correction intercepte cette erreur spécifique (AuthApiError avec le code invalid_refresh_token) au bon niveau et la supprime de l'affichage. Le rafraîchissement de session continue de fonctionner exactement comme avant, mais l'utilisateur ne voit plus un message anxiogène qui ne le concerne pas.

Ce type de gestion d'erreur est critique pour toute app utilisant l'authentification Supabase. Le SDK fait bien son travail — il faut juste éviter d'exposer ses erreurs intermédiaires à l'utilisateur.

Comment traduire un système de gamification dans 6 langues ?

TAMSIV est disponible en français, anglais, allemand, espagnol, italien et portugais. Le système de gamification — niveaux, badges, streaks — fait partie des fonctionnalités que les utilisateurs découvrent progressivement. Sauf que le guide explicatif des badges n'existait qu'en français et en anglais.

Globe entouré de drapeaux et symboles représentant six langues différentes
Traduire chaque élément de l'interface est un investissement qui paie sur le long terme.

C'est un problème plus subtil qu'il n'y paraît. Un utilisateur germanophone qui découvre le système de badges et qui tombe sur un texte en français ou en anglais va immédiatement sentir que cette partie de l'app n'est pas finie. C'est un signal négatif très fort, surtout quand le reste de l'interface est correctement traduit.

Le processus d'internationalisation des badges

Les 10 badges de TAMSIV ont chacun un nom, une description et des conditions de déblocage. Ça fait 30 chaînes de texte à traduire dans 4 langues supplémentaires (allemand, espagnol, italien, portugais). Le système d'internationalisation en 6 langues que j'ai mis en place gère ça avec un pipeline de traduction automatique via OpenRouter, suivi d'une relecture manuelle pour les termes de gamification.

Ce n'est pas un fix spectaculaire, mais c'est le genre de détail qui fait qu'un utilisateur lusophone ou germanophone se sent chez lui dans l'app. Et comme l'explique CSA Research, 76% des consommateurs préfèrent acheter des produits dans leur propre langue.

Quel est l'impact réel de ces corrections sur la rétention ?

Ces quatre corrections représentent une journée de travail. Aucune n'aurait fait un titre de changelog excitant. Mais ensemble, elles éliminent des frictions réelles :

  • Rappels perdus : plus aucun rappel valide ne disparaît suite à un avertissement sur un rappel expiré.
  • Emails manqués : les deux canaux de notification sont actifs par défaut, plus de surprise.
  • Erreur au démarrage : le message "Invalid Refresh Token" ne s'affiche plus jamais.
  • Gamification traduite : les 10 badges sont disponibles dans les 6 langues supportées.

En termes de rétention, chacun de ces bugs était un point de friction invisible. L'utilisateur ne signale pas "ton email de rappel n'est pas arrivé" — il se dit simplement que l'app ne fonctionne pas et il la désinstalle. Selon Adjust, le taux de rétention moyen d'une app à J30 est de 6%. Chaque friction éliminée pousse ce chiffre vers le haut.

Comment organiser un sprint qualité quand on est dev solo ?

Être développeur solo sur un projet comme TAMSIV signifie que personne ne va planifier un sprint qualité à ta place. Voici la méthode que j'utilise :

  1. Utiliser sa propre app quotidiennement — le dogfooding est la meilleure source de bugs. Chaque irritation notée dans un mémo vocal (avec TAMSIV, évidemment).
  2. Prioriser par impact utilisateur — un bug qui affecte 100% des utilisateurs à chaque démarrage passe avant un bug qui touche 2% des cas.
  3. Limiter le scope — une journée, 4 corrections maximum. Pas de refactoring tentant en cours de route.
  4. Tester sur de vrais appareils — les 12 bêta-testeurs sont là pour ça. Un bug reproduit sur 3 appareils différents est un vrai bug.

Le sprint zéro-feature que j'ai fait la semaine suivante a confirmé cette approche : les retours des bêta-testeurs étaient bien meilleurs après ces corrections silencieuses qu'après l'ajout de nouvelles fonctionnalités.

Questions fréquentes

Combien de temps faut-il pour un sprint qualité efficace ?

Une journée concentrée suffit pour 3 à 5 corrections ciblées. L'important est de ne pas mélanger avec du développement de features — le contexte mental est différent. Je recommande un sprint qualité toutes les 2 semaines pour un projet en phase de lancement.

Comment détecter les bugs silencieux quand il n'y a pas de crash report ?

Le dogfooding quotidien est la méthode numéro un. Utilise ta propre app comme un utilisateur normal, pas comme un développeur. Les outils comme Firebase Analytics aident aussi à détecter des patterns anormaux (sessions très courtes, fonctionnalités jamais utilisées).

Pourquoi activer push ET email par défaut plutôt que laisser l'utilisateur choisir ?

Parce que la majorité des utilisateurs ne changent jamais les paramètres par défaut (recherches du Nielsen Norman Group). Le opt-out est plus respectueux que le opt-in pour les notifications de rappel — l'utilisateur a explicitement demandé à être rappelé, il s'attend à recevoir la notification sur tous les canaux disponibles.

L'erreur "Invalid Refresh Token" est-elle dangereuse pour les données ?

Non, absolument pas. C'est une erreur intermédiaire normale dans le flux de rafraîchissement de token JWT. Supabase Auth gère ça automatiquement — le token expiré est remplacé par un nouveau en arrière-plan. Le problème était uniquement l'affichage de cette erreur technique à l'utilisateur.

Comment gérer la traduction de termes de gamification (badges, niveaux) ?

J'utilise un pipeline de traduction automatique via OpenRouter, suivi d'une relecture pour les termes spécifiques au gaming. Certains termes comme "streak" ou "badge" sont souvent gardés en anglais même dans les versions localisées, car ils sont universellement compris par les utilisateurs d'apps mobiles.