17 nov. 2025·8 min de lecture

Machine d'état d'intégration : stopper les inscriptions inachevées

Apprenez comment une machine d'état d'intégration empêche les comptes inachevés, gère les actualisations et les retards d'e-mail, et permet aux utilisateurs de reprendre leur progression.

Machine d'état d'intégration : stopper les inscriptions inachevées

Pourquoi les utilisateurs se retrouvent bloqués en cours d'intégration

Les “inscriptions inachevées” surviennent quand votre flux d'inscription crée un enregistrement utilisateur, mais que la personne n'atteint jamais un état final utilisable. Elle peut voir un spinner indéfiniment, atterrir sur un écran « vérifiez votre e-mail » qui ne se met jamais à jour, ou être renvoyée à l'étape un à chaque connexion.

La plupart des flux cassent parce qu'ils supposent que l'onboarding est une checklist linéaire et unique. Dans la réalité, les gens actualisent quand quelque chose semble lent, appuient sur Précédent, ouvrent le même lien sur leur téléphone après avoir commencé sur un ordinateur, ou reviennent le lendemain après l'expiration d'une session.

Les déclencheurs courants semblent banals, mais coûtent cher :

  • Un problème réseau fait échouer une étape après la création du compte.
  • La vérification par e-mail arrive en retard, ou l'utilisateur clique sur un ancien lien de vérification.
  • Un timeout déconnecte l'utilisateur en plein processus et l'application perd la trace.
  • Plusieurs onglets soumettent deux fois la même étape et laissent les données dans un état étrange.
  • Une actualisation répète une action non-retrialbe (comme débiter une carte ou créer une équipe).

L'impact apparaît vite dans le support : « je ne peux pas me connecter », « ça dit vérifié mais je suis toujours bloqué », ou « il me demande encore de définir un mot de passe ». Mais la perte la plus importante est silencieuse : beaucoup de personnes ne contactent pas le support. Elles partent tout simplement.

La promesse centrale d'une machine d'état d'intégration est simple : quoi qu'il arrive, l'utilisateur peut toujours reprendre en toute sécurité. Si une étape échoue, l'application sait exactement où il en est, ce qu'il peut faire ensuite et ce qui n'est pas encore autorisé.

Les machines d'état en langage clair

Un état est juste une étiquette claire sur la position de l'utilisateur dans l'inscription. Ce n'est pas une impression, et ce n'est pas le nom d'une page. Pensez : account created, email not verified, profile incomplete, payment pending, onboarded.

Une checklist linéaire suppose que les gens avancent une fois et dans l'ordre. Dans la vraie vie, quelqu'un actualise pendant le paiement, ouvre l'e-mail de vérification une heure plus tard, appuie sur Précédent ou réessaie depuis un second onglet. Avec une checklist, votre système ne sait souvent pas s'il doit continuer, redémarrer ou les bloquer.

Une machine d'état d'intégration est un petit ensemble de règles qui répond à deux questions à chaque fois :

  • Dans quel état est cet utilisateur ?
  • Depuis cet état, quels mouvements sont autorisés (avancer, réessayer ou reculer) ?

Au lieu de deviner d'après « l'écran actuel », vous enregistrez l'état sur l'enregistrement utilisateur et ne le mettez à jour que lorsqu'une étape réussit réellement. Si une étape échoue, l'état ne change pas. Si l'utilisateur réessaie, vous relancez la même étape en toute sécurité. S'il revient plus tard, vous consultez l'état stocké et l'envoyez à l'étape valide suivante.

Gardez-le simple. Commencez par les quelques états qui changent réellement ce que l'utilisateur doit voir ensuite. Évitez de créer un petit état pour chaque clic de bouton.

Exemple : un utilisateur crée un compte, puis attend la vérification de l'e-mail. Il actualise la page, puis tente de se connecter depuis un autre appareil. Si son état est email_unverified, votre routage peut toujours l'envoyer vers “renvoyer la vérification” et jamais vers “compléter le profil” tant que la vérification n'est pas faite. Une règle comme celle-là évite beaucoup d'inscriptions bloquées.

Choisir les états qui comptent

Une machine d'état fonctionne mieux quand vous enregistrez des moments qui changent ce que l'utilisateur peut faire ensuite. Si vous enregistrez chaque clic, vous créez du bruit et des cas limites. Si vous enregistrez trop peu, vous ne pouvez pas reprendre de façon fiable.

Règle pratique : créez un état quand vous avez (1) écrit quelque chose d'important en base, (2) déclenché quelque chose en dehors de l'app (comme l'envoi d'un e-mail), ou (3) l'écran suivant devrait être différent si l'utilisateur actualise.

La plupart des flux d'inscription peuvent être couverts par un petit ensemble d'états comme :

  • started (compte créé)
  • email_sent (e-mail de vérification demandé)
  • verified (e-mail vérifié)
  • profile_done (champs de profil requis complétés)
  • completed (intégration terminée)

Tous les états ne doivent pas bloquer l'utilisateur. Décidez lesquels sont requis pour avancer et lesquels sont optionnels. La vérification d'e-mail peut être bloquante pour la sécurité, tandis que l'ajout d'une photo de profil peut être optionnel.

Décidez ensuite ce que l'app doit afficher pour chaque état. C'est là que beaucoup de comptes bloqués sont sauvés.

Si un utilisateur arrive avec state = email_sent, n'affichez pas une erreur générique ou ne relancez pas l'inscription. Affichez un écran clair « Vérifiez votre boîte » avec un bouton de renvoi, une option de changement d'email et une courte note sur les éventuels retards.

Si state = verified mais que le profil est incomplet, envoyez-le directement au formulaire de profil et chargez ce que vous avez déjà sauvegardé.

Définir les transitions autorisées

Une machine d'état n'aide que si vous êtes strict sur ce qui peut se passer ensuite. Écrivez une carte simple : pour chaque état, listez les quelques états dans lesquels l'utilisateur peut aller. Gardez-le prévisible.

Pensez en termes de mouvements, pas d'écrans. Un écran peut changer sans changer l'état. Un état ne doit changer que lorsque vous avez la preuve qu'une étape a réussi.

Règles qui fonctionnent bien pour la plupart des flux :

  • Donnez à chaque état 1 à 3 états suivants autorisés.
  • Ajoutez des mouvements de réentrée sûrs qui ne changent pas l'état, comme « ouvrir l'app à nouveau » ou « actualiser la page ».
  • Définissez les états terminaux tôt, tels que completed (terminé) et disabled (bloqué pour fraude, rétrofacturation, problèmes de politique).
  • Traitez l'« attente » comme un vrai état, par exemple email_verification_pending, pas un écran temporaire.
  • Décidez ce qui arrive sur un mouvement invalide (sauter une étape, soumission depuis un onglet ancien, double-clic). Habituellement : ignorer la requête, afficher un message clair et les rediriger vers l'étape correcte.

Exemple : un utilisateur s'inscrit, se retrouve en email_verification_pending, puis ferme le navigateur. Deux jours plus tard il clique sur le lien de vérification. Vos règles doivent permettre email_verification_pending -> email_verified, puis le déplacer vers l'étape suivante. Elles ne doivent pas permettre email_verification_pending -> completed simplement parce qu'un endpoint « finish » a été appelé.

Étape par étape : implémenter un flux d'intégration reprenable

Un flux reprenable tient une promesse : peu importe où un utilisateur abandonne (actualisation, paiement échoué, e-mail lent), l'app peut toujours décider quoi afficher ensuite.

Commencez par stocker une unique source de vérité pour la progression. Ajoutez un champ onboarding_state sur l'enregistrement utilisateur, ou créez un enregistrement d'onboarding séparé si vous avez besoin d'historique, de tentatives ou de reprises multiples.

Ensuite, centralisez la logique de routage. Écrivez une fonction qui prend l'utilisateur (et éventuellement l'enregistrement d'onboarding) et renvoie l'écran suivant, comme "verify_email" ou "create_workspace". Cette fonction est le cœur de votre machine d'état d'intégration.

Traitez ensuite chaque étape comme “complète seulement en cas de succès”. Si un utilisateur soumet un formulaire de profil, validez et sauvegardez d'abord, puis avancez l'état seulement après. Si quelque chose échoue, laissez l'état où il était pour que l'utilisateur puisse réessayer.

Checklist d'implémentation compacte :

  • Persistez l'état en base (pas seulement dans le navigateur)
  • Utilisez une unique fonction “décider l'étape suivante” partout
  • Avancez l'état uniquement après le succès réel de l'étape
  • À chaque chargement de l'app, routez selon l'état courant
  • Conservez un petit journal d'audit des changements d'état

Ce journal d'audit compte plus qu'on ne le pense. Quand le support reçoit « j'ai vérifié mon e-mail mais je suis toujours bloqué », le journal doit montrer si la vérification a été reçue et quel état le compte a.

Exemple : Sam s'inscrit sur mobile, demande la vérification par e-mail, puis ouvre l'app plus tard sur desktop. Si votre app appelle toujours la même fonction de décision au chargement, Sam arrive sur « Vérifié ? Oui. Suivant : créer l'espace », au lieu d'une page morte ou d'une boucle.

Gérer actualisations, bouton Précédent et onglets multiples

Enforce state on the server
Nous diagnostiquons l'état, les transitions et les règles backend pour que l'UI ne se trompe pas.

Traitez l'onboarding comme interrompable à tout moment. Les gens actualisent quand quelque chose semble coincé, appuient sur Précédent quand ils s'inquiètent, et ouvrent deux onglets quand un code n'apparaît pas. Si votre flux ne fonctionne que dans un scénario parfait en onglet unique, vous créerez des comptes bloqués.

Règle sûre : n'importe quelle page peut se recharger à tout moment. À chaque chargement, demandez au serveur où se trouve l'utilisateur dans la machine d'état, puis affichez l'étape qui correspond à cet état. Ne supposez pas que la dernière URL est la vérité. Les URLs sont la navigation ; l'état est la source de vérité.

Évitez de garder la progression critique uniquement dans le navigateur (localStorage, flags en mémoire, variable d'état React). Ces éléments se perdent facilement à l'actualisation, en mode privé ou lors d'un changement d'appareil. Stockez la progression côté backend avec des timestamps, et considérez le stockage client comme un confort.

Les onglets multiples créent des problèmes de « double soumission ». Si deux onglets tentent de compléter la même étape, le second ne doit pas casser le compte. Répondez « déjà complété » et routez vers l'avant.

Quelques choix d'UI réduisent aussi la panique :

  • Proposez un point d'entrée clair « Continuer l'onboarding » dans l'app après la connexion.
  • Affichez l'étape en cours et ce qui vient ensuite.
  • Si une étape est en attente, expliquez pourquoi et ce qui la résout.
  • Offrez « Redémarrer l'onboarding » seulement si vous pouvez le faire sans perte de données.

Retards de vérification par e-mail sans cul-de-sac

La vérification par e-mail est lente par nature. Les messages sont retardés, tombent en spam ou arrivent après que quelqu'un a fermé l'onglet. Si votre flux suppose que la vérification est instantanée, vous finirez avec des inscriptions qui ne se récupèrent jamais.

Dans une machine d'état, traitez la vérification comme un travail asynchrone. Un modèle propre sépare « on a envoyé un e-mail » de « l'adresse est vérifiée ». Gardez un état comme email_sent (ou awaiting_email_verification) et un flag séparé email_verified = true/false. Ainsi, l'utilisateur peut actualiser, revenir demain ou changer d'appareil sans tomber dans un cul-de-sac.

Sur l'écran d'attente, proposez des actions sûres qui ne réinitialisent pas la progression ou ne créent pas de doublons :

  • Renvoyer l'e-mail de vérification (avec rate-limit, même utilisateur, même état)
  • Re-vérifier le statut de vérification
  • Changer l'adresse e-mail (avec confirmation et nouvel envoi)
  • Utiliser une autre méthode de connexion (si vous la supportez, et seulement après confirmation d'identité)

Gérez aussi le cas « mauvaise adresse » avec une indication simple. Une phrase comme « Utilisé la mauvaise adresse ? Mettez-la à jour ici et nous enverrons un nouveau lien. » évite beaucoup de tickets.

Scénario : Sam s'inscrit sur mobile, puis va sur un laptop pour finir la configuration. L'e-mail de vérification arrive en retard et Sam clique dessus sur le téléphone. Si votre app stocke un état en attente et vérifie email_verified au chargement suivant, Sam peut continuer sur le laptop immédiatement. Pas de nouveau compte et pas de boucle vers l'étape un.

Rendre les étapes réessayables et idempotentes

See what’s broken
Obtenez un audit de code gratuit avant de vous engager sur des travaux.

Les gens font des double-clics. Les téléphones perdent la connexion. Les navigateurs retentent des requêtes. Si votre onboarding suppose que chaque étape s'exécute exactement une fois, vous vous retrouverez avec des comptes à l'état intermédiaire.

Réessayable signifie qu'une étape peut être tentée à nouveau après un échec. Idempotent signifie que l'exécuter deux fois donne le même résultat que l'exécuter une fois. Vous voulez les deux.

Exemple simple : “Créer un workspace”. Si un utilisateur appuie deux fois ou que la requête timeoute et que l'app retente, vous ne devez pas créer deux workspaces, deux profils de facturation, ou un utilisateur qui ne peut pas avancer parce que la seconde requête échoue.

Un schéma pratique est la clé d'idempotence. Quand le client démarre une action (par exemple créer un workspace), générez une clé unique pour cette action et envoyez-la avec la requête. Côté serveur, stockez la clé avec l'enregistrement résultant. Si la même clé revient, renvoyez le résultat déjà créé au lieu de créer un doublon.

Règles qui préviennent la plupart des comptes bloqués :

  • Traitez chaque action « create » comme « créer ou renvoyer l'existant », indexée par un token d'idempotence.
  • Mettez des contraintes d'unicité en base (par exemple, un workspace par utilisateur pendant l'onboarding).
  • Passez à l'état suivant seulement après que la base soit cohérente.
  • Si quelque chose échoue en cours d'étape, enregistrez une raison d'échec claire et gardez l'utilisateur dans l'état sûr précédent.
  • R rendez l'UI sûre aussi (désactivez les boutons pendant les requêtes), mais ne comptez pas uniquement dessus.

Un bug fréquent est d'actualiser l'état d'onboarding en premier, puis d'essayer de créer les données. Si la seconde partie échoue, l'utilisateur semble « passé » l'étape mais il manque des enregistrements.

Erreurs courantes qui créent des comptes bloqués

La plupart des bugs d'onboarding bloqués ne sont pas sophistiqués. Ils arrivent quand votre app enregistre la progression trop tôt, ou fait plus confiance au navigateur qu'au serveur.

Un piège courant est de marquer une étape comme complétée avant qu'elle ne le soit vraiment. Par exemple, mettre payment_complete quand la page de checkout se charge, pas quand le fournisseur de paiement confirme le succès. Si l'onglet actualise ou que le callback échoue, l'utilisateur est alors dans un état qu'il ne peut pas satisfaire.

Une autre cause fréquente est de se reposer sur l'état côté client (localStorage, flags en mémoire, index d'étape React) pour décider de la suite. Les utilisateurs ouvrent un nouvel onglet, effacent le stockage ou changent d'appareil, et votre flux perd le fil. Dans une machine d'état, le serveur doit être la source de vérité et l'UI doit s'y conformer.

Erreurs qui créent systématiquement des inscriptions à moitié finies :

  • Progresser avec des URLs (comme /onboarding/step-3) au lieu d'un statut côté serveur que le backend applique.
  • Créer un nouvel utilisateur à chaque tentative de vérification échouée, menant à des doublons et à la confusion « mauvais compte ».
  • Mauvaise gestion des retards de vérification d'e-mail : l'app suppose que le mail sera cliqué tout de suite, puis bloque toutes les autres actions.
  • Pas de chemin de retry sûr : la resoumission crée un second workspace, une seconde souscription ou un profil cassé.
  • Pas de plan de secours : quand quelque chose casse, l'utilisateur n'a pas de moyen clair de demander de l'aide ou de réinitialiser.

Exemple : Sam s'inscrit, demande un e-mail de vérification, puis actualise en attendant. Le frontend avance un compteur d'étapes et suppose que Sam est vérifié, alors que le backend exige toujours la vérification. Sam est redirigé en boucle.

Vérifications rapides avant de déployer

Avant de publier, testez le flux comme font les vrais utilisateurs : ils actualisent, changent d'appareil, cliquent sur Précédent et attendent des heures l'e-mail. Si votre machine d'état gère ces cas sans confusion, vous éviterez la plupart des comptes bloqués.

Pour chaque état, confirmez :

  • Un état correspond à un écran (ou message) clair et à une action suivante claire.
  • L'actualisation conserve la même progression et montre le même “quoi faire ensuite”.
  • Retenter la même étape deux fois ne crée pas de doublons (comptes, orgs, paiements, invitations).
  • La vérification peut être renvoyée, re-vérifiée et complétée plus tard sans redémarrer l'inscription.
  • Le support peut voir l'état courant et les transitions récentes (timestamps et erreurs).

Ensuite, faites trois tests “brisez-le volontairement” dans un vrai navigateur :

  1. Ouvrez la même étape dans deux onglets, complétez-la dans l'Onglet A, puis actualisez l'Onglet B. L'Onglet B doit se mettre à jour en toute sécurité.

  2. Coupez Internet en plein étape, soumettez, puis réessayez quand vous êtes reconnecté. Vous devez soit obtenir le même résultat de succès, soit une erreur claire avec un retry sûr.

  3. Retardez la vérification : demandez l'e-mail, attendez 10 minutes, cliquez dessus et revenez à l'app. Vous devez atterrir au bon endroit, pas sur une page blanche ou une boucle “recommencez”.

Exemple : un sauvetage réaliste d'un onboarding bloqué

Fix onboarding loops
Nous réparons la logique d'inscription cassée, la vérification des e-mails et les boucles de routage dans les apps générées par IA.

Maya s'inscrit à un essai SaaS un après-midi chargé. Elle crée un compte, voit « Vérifiez votre e-mail », puis ferme l'onglet.

Le lendemain, l'e-mail de vérification arrive enfin. Elle clique dessus, mais sa session d'origine a expiré. Dans un flux fragile, c'est une impasse : l'app ne sait pas si elle est nouvelle, vérifiée ou à moitié créée.

Avec une machine d'état d'intégration, l'app consulte l'état courant de Maya et reprend depuis là. Le clic de vérification marque son compte comme vérifié même si elle est déconnectée, puis l'envoie à l'étape suivante requise.

Quelques minutes plus tard, Maya ouvre l'app dans deux onglets. Dans l'un, elle complète son profil ; dans l'autre, elle est toujours sur « Complétez votre profil ». Quand elle enregistre dans le premier onglet, l'état du compte avance une seule fois. Le second onglet s'actualise et voit le nouvel état au lieu d'écraser quoi que ce soit.

L'expérience de Maya reste cohérente :

  • S'inscrit : voit « Vérifiez votre e-mail » avec une option « Renvoyer » sûre.
  • Clique un e-mail tardif : la vérification réussit et on lui demande de se connecter.
  • Se connecte : arrive sur « Complétez le profil », pas sur la page d'accueil.
  • Utilise deux onglets : l'onglet en retard se met à jour avec « Déjà complété » et passe à la suite.

Le support voit aussi une histoire propre : état courant, timestamp de la dernière étape complétée et dernière erreur (le cas échéant). C'est la différence entre résoudre un problème en minutes et deviner pendant des heures.

Prochaines étapes : cartographiez votre flux et débloquez vite

Si les gens peuvent s'inscrire mais ne pas finir de façon fiable, considérez l'onboarding comme une fonctionnalité produit, pas un ensemble d'écrans. Une machine d'état d'intégration simple vous donne une source de vérité pour l'endroit où se trouve un utilisateur et ce qu'il est autorisé à faire ensuite.

Commencez sur papier. Notez chaque étape que votre app attend (création de compte, vérification d'e-mail, acceptation des conditions, création d'un workspace, ajout d'un paiement, invitation d'équipe). Puis regroupez-les en 5 à 8 états clairs qui décrivent la progression, pas les pages. Les états doivent rester stables même si l'UI change.

Un plan rapide qui paie souvent :

  • Définissez vos états et votre état final (par exemple : SignedUp, EmailPending, Verified, ProfileComplete, Active).
  • Enregistrez chaque changement d'état (qui, de, vers, quand et pourquoi).
  • Suivez les abandons par état, pas par page.
  • Ajoutez une règle centrale « router par état » pour que les actualisations et liens profonds atterrissent correctement.
  • Choisissez une étape fragile (souvent les retards d'e-mail ou le paiement) et rendez-la reprenable en priorité.

Une petite habitude qui évite beaucoup de douleur : quand quelque chose échoue, ne laissez pas l'utilisateur dans « inconnu ». Remettez-le dans un état connu avec une action suivante claire, même si cette action est « réessayer » ou « vérifiez votre e-mail plus tard ».

Si vous avez hérité d'une app générée par IA où l'inscription fonctionne en démo mais casse sous les vraies tentatives, retards d'e-mails et changements d'onglets, FixMyMess (fixmymess.ai) se concentre sur le diagnostic et la réparation de ces flux : persistance d'état, vérifications de transition et logique backend qui empêche les utilisateurs d'être abandonnés en plein onboarding.

Questions Fréquentes

What is an onboarding state machine, and why would I use one?

Une machine d'état rend l'onboarding résumable. Plutôt que de deviner la progression à partir de la page courante, vous stockez un champ clair onboarding_state sur l'utilisateur et ne l'avancez que lorsqu'une étape réussit réellement, ainsi les actualisations, tentatives répétées et changements d'appareil n'enferment pas les utilisateurs dans des boucles.

How many onboarding states should I start with?

Commencez avec les quelques états qui changent ce que l'utilisateur peut faire ensuite. La plupart des produits peuvent couvrir l'inscription avec environ 5–8 états, par exemple “account created”, “email unverified”, “profile incomplete” et “completed”. Créer un état pour chaque clic ajoute des cas limites sans améliorer la récupération.

Where should I store onboarding progress—frontend or backend?

Persistez-le côté serveur, généralement comme un champ onboarding_state sur l'enregistrement utilisateur. Utilisez le stockage navigateur seulement pour la commodité, car il disparaît à l'actualisation, en mode privé, lors d'un changement d'appareil ou en multi-onglets.

Can users safely resume onboarding after closing the tab or switching devices?

Oui, si vous centralisez le routage. À chaque chargement après la connexion, appelez une unique fonction “decide next step” qui lit l'état stocké et envoie l'utilisateur sur la bonne page, même s'il rouvre l'app demain ou sur un autre appareil.

How do I prevent email verification delays from causing dead ends?

Considérez la vérification d'e-mail comme un travail asynchrone. Gardez un état d'attente comme email_sent et un flag séparé email_verified, puis affichez un écran “vérifiez votre boîte” qui peut être actualisé en toute sécurité et permet de renvoyer ou de changer l'adresse sans redémarrer le compte.

How do I avoid double-submits creating duplicates when users open multiple tabs?

Rendez les actions critiques idempotentes. Utilisez une clé d'idempotence pour les opérations “create” (création d'espace de travail, démarrage de facturation) et imposez des contraintes d'unicité en base pour que la seconde soumission renvoie le résultat existant au lieu de créer des doublons ou des erreurs.

What should happen if someone tries to skip steps or submits an old form?

N'avancez pas l'état tant que vous n'avez pas la preuve que l'étape a réussi, et rejetez ou ignorez les mouvements invalides. Si un utilisateur tente de sauter des étapes, affichez un message clair et ramenez-le à l'étape suivante correcte basée sur l'état stocké.

Do I really need an onboarding audit log?

Conservez une petite trace d'audit des changements d'état avec timestamps et raisons d'échec. Cela transforme un « je suis vérifié mais toujours bloqué » en une vérification rapide de l'événement reçu et de l'état réel du compte.

What are the fastest tests to run before shipping this?

Testez le flux comme le ferait un vrai utilisateur : actualisez à mi-étape, ouvrez deux onglets et complétez dans l'un, changez d'appareil et retardez la vérification par e-mail. L'objectif est que chaque rechargement mène à une action suivante valide et que les tentatives répétées ne corrompent jamais les données.

How do I fix a signup flow that was built by an AI tool and gets users stuck?

Cherchez des compteurs d'étapes pilotés par le frontend, des progrès encodés dans des URLs et des mises à jour d'état faites avant l'écriture en base ou la confirmation externe. Le code généré vite par IA a souvent l'air correct en démo mais casse sous les tentatives répétées, les timeouts et les callbacks asynchrones ; la réparation implique généralement de déplacer la logique d'état côté serveur et de rendre les étapes idempotentes.