Les Écrans Blancs Tuent la Confiance. J'en Avais 31.
J'ai découvert 31 écrans blancs dans mon SaaS — parce que j'avais oublié que la multi-location, ce n'est pas qu'une question d'accès aux données, c'est aussi une question de contexte d'URL. Voici comment Claude Code m'a aidé à tout corriger en une seule nuit.
1er novembre, 8h47. J'ai reçu l'email.
"Hey Chandler — j'ai cliqué sur 'Voir' et j'ai eu une page blanche. Est-ce que j'ai perdu toutes mes données d'entretien ?"
Mon estomac s'est serré. Les pages blanches ne sont pas des bugs. Ce sont des tueurs de confiance.
J'ai ouvert la console de développement. J'ai vérifié la route. Un utilisateur agence, en train de consulter le client "Acme Corp", avait cliqué sur un bouton, et... l'URL était passée de `/clients/acme-corp/agents/agent-name/results/123` à juste `/agent-name/results/123`.
Contexte client perdu. React Router ne trouvait pas la route. Écran blanc.
"Bon, c'est un seul bug," j'ai pensé. "Je vais le corriger et passer à autre chose."
J'ai demandé à Claude Code d'analyser la codebase pour des patterns similaires, puis j'ai continué ma journée avec la famille. Le déjeuner. Les courses. Les enfants. La routine habituelle.
En fin d'après-midi, j'ai regardé ce que ça avait donné. Claude Code avait trouvé le pattern — et ce n'était pas joli.
À 19h42, j'avais corrigé 14 bugs. À 19h48, j'en avais trouvé 8 de plus. À 19h56, le total atteignait 31 au total.
Tous la même cause racine. Tous le même correctif. Tous parce que j'avais oublié une chose quand j'avais construit la multi-location : la navigation, ce n'est pas qu'une question de données, c'est une question de contexte.
---
C'était vraiment grave ?
Soyons honnêtes sur ce que ces 31 bugs signifiaient concrètement :
Pour les utilisateurs :
- Des utilisateurs agence qui tombaient sur des écrans blancs (l'app semblait cassée, pas bugguée)
- Perte de progression en pleine session de travail
- "Est-ce que cette plateforme est assez stable pour nos clients ?"
- Chaque écran blanc = un pas de plus vers l'annulation de l'essai
Pour moi :
- 5+ rapports de bugs par jour (tous liés à la navigation)
- 2-3 heures de débogage pour chacun individuellement
- Impossible de livrer de nouvelles fonctionnalités (trop occupé à gérer les incendies)
- Une vraie peur : "Et si j'avais cassé autre chose sans le savoir ?"
La question existentielle : Si je ne peux même pas faire fonctionner la navigation, pourquoi quelqu'un ferait-il confiance à STRAŦUM pour la stratégie marketing de ses clients ?
Les écrans blancs tuent la confiance plus vite que n'importe quoi d'autre.
---
Le bug qui a tout déclenché
Laisse-moi te montrer exactement ce qui s'est passé.
Le flux utilisateur (ce qui aurait dû fonctionner) :
1. Aller sur `/clients/acme-corp/agents/analysis`
2. Cliquer sur "Démarrer la session"
3. Terminer l'analyse
4. Cliquer sur "Voir les résultats"
5. Voir les résultats sur `/clients/acme-corp/agents/analysis/results/123`
Ce qui s'est passé en réalité :
1. ✅ `/clients/acme-corp/agents/analysis` (bon)
2. ✅ Démarrer la session (ça marche)
3. ✅ Terminer l'analyse (données sauvegardées)
4. ❌ Cliquer sur "Voir les résultats" → PAGE BLANCHE
5. ❌ L'URL est passée à `/analysis/results/123` (contexte client perdu)
React Router cherchait une route sur `/analysis/results/123`. Elle n'existait pas pour les utilisateurs agence. Il n'affichait rien.
L'utilisateur voit : écran blanc. Pas de message d'erreur. Pas de spinner de chargement. Juste... rien.
---
Ce que j'ai trouvé quand j'ai (enfin Claude Code) creusé
Voici à quoi ressemblait le code cassé :
```typescript
// AgentPage.tsx (VERSION SANITISÉE - le pattern cassé)
import \{ useParams, useNavigate \} from 'react-router-dom';
export function AgentPage() \{
const { clientSlug \} = useParams<\{ clientSlug: string \}>();
const navigate = useNavigate();
const handleViewResults = (sessionId: string) => \{
// Problème : Route codée en dur, sans contexte client
navigate(`/agent-name/results/${sessionId\}`);
};
return (
// ... composant
);
}
```
Tu vois le problème ?
J'avais extrait `clientSlug` de l'URL. Je l'utilisais pour récupérer les données. Mais au moment de naviguer, je l'avais complètement oublié.
Les routes agence ressemblent à ça : `/clients/acme-corp/agents/agent-name`
Les routes SME ressemblent à ça : `/agent-name`
J'avais codé en dur le pattern SME. Utilisateurs agence = cassé.
---
La révélation : j'en avais 31
14h15. J'avais corrigé le premier bug. Commit. Satisfait.
15h42. J'en avais trouvé un autre sur un agent différent. Même pattern. Corrigé.
16h18. Un autre agent. Pareil.
17h30. J'ai arrêté et j'ai fixé mon écran pendant un bon 10 minutes.
Chaque page d'agent avait le même bug. Chaque composant imbriqué qui naviguait. Chaque élément UI partagé avec un bouton "Aller à...".
Je pouvais les corriger un par un et passer 2 jours dessus. Ou trouver le pattern et tout corriger de façon systématique.
J'ai choisi la voie systématique.
---
Le correctif : une navigation consciente du contexte
Au lieu d'utiliser `useParams()` dans chaque composant, j'ai demandé à Claude Code de créer un provider Context :
```typescript
// contexts/ClientContext.tsx
import \{ createContext, useContext \} from 'react';
import \{ useParams \} from 'react-router-dom';
interface ClientContextValue \{
clientSlug: string | null;
\}
const ClientContext = createContext<ClientContextValue | null>(null);
export function ClientContextProvider(\{ children \}: \{ children: React.ReactNode \}) \{
// Extraire clientSlug UNE SEULE FOIS au niveau du layout
const { clientSlug \} = useParams<\{ clientSlug: string \}>();
return (
<ClientContext.Provider value=\{{ clientSlug: clientSlug || null \}}>
\{children\}
</ClientContext.Provider>
);
}
export function useClientContext() \{
const context = useContext(ClientContext);
if (!context) {
throw new Error('useClientContext doit être utilisé dans un ClientContextProvider');
\}
return context;
}
```
Ensuite j'ai enveloppé les routes agence :
```typescript
// App.tsx
<Route path="/clients/:clientSlug/*" element={
<ClientContextProvider>
<ClientLayout />
</ClientContextProvider>
}>
<Route path="agents/analysis" element={<AnalysisAgent />} />
<Route path="agents/strategy" element={<StrategyAgent />} />
\{/* ... toutes les routes scopées client */\}
</Route>
```
Maintenant chaque composant à l'intérieur a accès à `clientSlug` via le contexte, pas via les params.
Le helper de routing (parce que taper le même if/else 20 fois, c'est lassant) :
```typescript
// hooks/useContextRoute.ts
import \{ useClientContext \} from '@/contexts/ClientContext';
export function useContextRoute() \{
const { clientSlug \} = useClientContext();
const buildRoute = (route: string) => \{
if (clientSlug) {
// Route agence : /clients/acme-corp/agents/...
const cleanRoute = route.startsWith('/') ? route.slice(1) : route;
return `/clients/${clientSlug\}/$\{cleanRoute\}`;
}
// Route SME : /agent-name/...
return route;
};
return \{ buildRoute, clientSlug \};
}
```
Utilisation (tellement plus propre) :
```typescript
// AgentPage.tsx (VERSION SANITISÉE - le pattern corrigé)
import \{ useClientContext \} from '@/contexts/ClientContext';
import \{ useNavigate \} from 'react-router-dom';
import \{ useContextRoute \} from '@/hooks/useContextRoute';
export function AgentPage() \{
const { clientSlug \} = useClientContext();
const \{ buildRoute \} = useContextRoute();
const navigate = useNavigate();
const handleViewResults = (sessionId: string) => \{
// Ça marche pour les utilisateurs SME ET Agence
navigate(buildRoute(`agents/analysis/results/${sessionId\}`));
};
return (
// ... composant
);
}
```
Une seule fonction helper. Consciente du contexte. Fonctionne pour les deux types d'utilisateurs.
---
Le correctif systématique : un jour, 31 fichiers
Une fois que j'avais le pattern, c'est devenu mécanique.
1er novembre 2025 - Le sprint
Après-midi (14h - 19h) - Trouver et catégoriser :
- Découverte du pattern sur toutes les pages d'agent
- Construction du provider Context et du helper de routing
- Test de l'approche sur le premier agent
Soirée (19h - 20h) - Le correctif systématique :
19h42 - Première vague (14 bugs) :
```
fix(multi-tenant): corriger 14 bugs de navigation et refactoriser tous les agents en useClientContext()
Frontend Changes (8 fichiers) :
- Refactorisé 6 agents pour utiliser le hook useClientContext()
- Corrigé 14 bugs de navigation qui perdaient le contexte client sur plusieurs agents
Backend Changes (5 fichiers) :
- Ajouté le paramètre client_id à toutes les opérations de sauvegarde
- Mis à jour les classes de base pour extraire client_id correctement
Fichiers modifiés : 14
Insertions : +732
Suppressions : -164
```
19h48 - Deuxième vague (8 bugs supplémentaires) :
```
fix(multi-tenant): corriger 8 bugs de navigation sur la page strategy
- Corrigé 8 boutons de navigation qui perdaient le contexte client
- Total de bugs corrigés : 22 (14 + 8)
Fichiers modifiés : 1
Insertions : +71
Suppressions : -23
```
19h56 - Vague finale (9 bugs supplémentaires) :
```
fix(multi-tenant): corriger 9 bugs de navigation dans les pages interview et tool
- Corrigé les problèmes de navigation restants dans les composants imbriqués
- Total de bugs corrigés : 31 bugs dans toute l'application
Fichiers modifiés : 2
Insertions : +46
Suppressions : -10
```
J'ai parcouru chaque flux d'agent. Utilisateur SME. Utilisateur agence. J'ai cliqué sur chaque bouton. Pas d'écrans blancs.
Bilan total : 17 fichiers modifiés, 849 insertions, 197 suppressions.
Temps investi : Environ 6 heures de travail non continu avec Claude Code (découverte à 14h → dernier commit à 20h).
Temps économisé : Probablement 30+ heures de corrections de bugs individuels et de support utilisateur.
---
Ce que j'ai appris (à la dure)
1. La navigation multi-tenant est plus difficile que l'isolation des données
Tu peux filtrer les données par `org_id`. C'est la partie facile.
Mais la navigation ? Tu as deux structures d'URL valides pour la même fonctionnalité :
```
SME : /agent-name/session/123
Agence : /clients/acme-corp/agents/agent-name/session/123
```
Chaque appel `navigate()` doit savoir quel pattern utiliser. Une seule erreur, et les utilisateurs voient des pages blanches.
2. useParams() te ment
```typescript
const \{ clientSlug \} = useParams();
```
Ça marche... jusqu'à ce que la route change. Alors `clientSlug` devient `undefined`, et ta prochaine navigation se casse.
React Context ne ment pas. Il est toujours disponible, toujours cohérent.
3. Compte chaque bug avant de crier victoire
Je pensais avoir 14 bugs. Puis j'en ai trouvé 8 de plus. Puis 9 de plus.
La leçon : Grep toute ta codebase, pas seulement les fichiers où tu penses qu'il y a des bugs.
4. Quand tu trouves le même bug deux fois, arrête-toi et crée un pattern
Correction individuelle : 31 bugs = probablement une semaine
Correction systématique : Trouver le pattern → Créer un helper → Corriger toutes les instances = 6 heures
Les 10 minutes que j'ai passées à fixer mon écran à 17h30 m'ont économisé des jours.
5. Les bugs de navigation sont des menaces existentielles
On s'obsède sur la gestion d'état, la récupération de données, l'optimisation des API.
Mais une navigation cassée = écrans blancs = "Cette plateforme est cassée."
Les utilisateurs se fichent de tes politiques RLS ou de ton architecture multi-tenant. Ce qui les importe, c'est que cliquer sur "Voir les résultats" affiche des résultats. Ce même fossé entre "ça fonctionne techniquement" et "c'est vraiment fini" est réapparu quand j'ai construit une app iOS native avec l'IA — Claude Code a généré le scaffold rapidement, mais le polish qui fait que les utilisateurs font confiance à un produit ? C'est les 40% que seuls les humains peuvent apporter.
---
Le pattern (pour ton app multi-tenant)
Si tu construis un SaaS multi-tenant avec des URLs hiérarchiques :
✅ Étape 1 : Crée un provider Context au niveau du layout
```typescript
<ClientContextProvider>
\{/* Toutes les routes scopées tenant */\}
</ClientContextProvider>
```
✅ Étape 2 : Construis un helper de routing
```typescript
const \{ buildRoute \} = useContextRoute();
navigate(buildRoute('agents/analysis/session/123'));
```
✅ Étape 3 : Ne jamais coder les routes en dur
```typescript
// ❌ MAUVAIS
navigate('/analysis/session/123');
// ✅ BON
navigate(buildRoute('agents/analysis/session/123'));
```
✅ Étape 4 : Grep tous les appels de navigation
```bash
# Trouver chaque appel navigate()
grep -r "navigate(" src/ > navigation_audit.txt
# Trouver les routes codées en dur
grep -r "navigate('/" src/ | grep -v "buildRoute"
```
✅ Étape 5 : Tester avec tous les types d'utilisateurs
Flux SME. Flux Agence. Chaque bouton. Chaque lien. Pas d'écrans blancs.
---
Les résultats
Avant le 1er novembre :
- 31 bugs de navigation qui se cachaient
- 5+ rapports de bugs par jour
- Des utilisateurs agence qui questionnaient la stabilité de la plateforme
- Moi : terrifié à l'idée de livrer de nouvelles fonctionnalités
Après le 1er novembre :
- 0 bug de navigation
- 0 ticket de support lié à la navigation
- Pattern établi pour le développement futur
- Moi : confiant pour livrer de nouvelles routes d'agent
Vélocité de développement :
- Ajouter de nouvelles routes : 5 minutes (c'était 30 minutes + "est-ce que ça va casser quelque chose ?")
- Rapports de bugs : 0 (c'était 5+ par jour)
- Confiance des utilisateurs : Restaurée ("La plateforme semble stable maintenant")
Les 6 heures que j'ai passées à corriger ces bugs ont été remboursées 10× en charge de support réduite et en confiance utilisateur retrouvée.
---
Pourquoi je partage ça
La navigation multi-tenant, ce n'est pas sexy. Personne ne célèbre "corrigé 31 bugs en 6 heures" comme on célèbre "livré une nouvelle fonctionnalité."
Mais si tu construis un SaaS multi-tenant comme moi avec https://stratum.chandlernguyen.com/ — une plateforme marketing IA pour les agences — tu vas rencontrer ça. Peut-être pas 31 bugs. Peut-être juste 5. Mais tu vas les rencontrer.
Quand ça arrive, rappelle-toi :
1. Context > Params pour l'état de navigation
2. Systématique > Individuel pour les correctifs
3. Grep toute ta codebase (tu trouveras plus que tu ne penses)
4. Teste avec tous les types d'utilisateurs avant de crier victoire
Et si tu te retrouves à fixer un écran blanc en te demandant où est passé ton contexte, sache que j'ai été exactement dans la même situation. :)
Quel est le bug le plus douloureux que tu aies livré à de vrais utilisateurs — le genre dont tu n'as entendu parler que parce que quelqu'un te l'a signalé ? Je serais vraiment curieux d'entendre ça.
Cordialement, Chandler
La série architecture STRAŦUM : Cette crise de navigation faisait partie d'un voyage multi-tenant plus large. Tout a commencé avec la construction de la multi-location au Jour 2, s'est intensifié quand j'ai dû reconstruire tout le schéma au Jour 67, et a conclu quand j'ai découvert que ma base de données était correcte mais 296 fois trop lente.
---
Toujours en train de coder, d'apprendre, et de trouver des bugs par groupes de 31.
Demande un accès alpha sur https://stratum.chandlernguyen.com/request-invitation
---
P.S. — L'utilisateur qui a signalé ce premier bug ? Il n'avait pas perdu ses données d'entretien. Elles étaient sauvegardées en base de données. Il ne pouvait juste pas les voir à cause d'une route cassée. Quand j'ai corrigé ça à 19h42, tout son travail était encore là. Ce petit soulagement valait largement les 6 heures.
---





