# Front-end — Risques & vigilance

Ce fichier recense des risques front-end susceptibles de provoquer :

- bugs subtils,
- comportements inattendus,
- dette technique rapide,
- régressions UX/perf/a11y.

Dernière mise à jour : 10-03-2026

---

## Règles d’utilisation

- Chaque entrée doit dire :
  - ce qui peut mal se passer,
  - comment on le voit (symptômes),
  - comment on le maîtrise (mitigation).
- Si c’est lié à une stack / version : on note le contexte.

---

## Index

- [Auth côté client](#risque-auth-cote-client)
- [Erreurs silencieuses / écrans blancs](#risque-erreurs-silencieuses)
- [Mélange server state / client state](#risque-melange-server-client-state)
- [Appels API en state local d’écran](#risque-api-state-local-ecran)
- [Performances : sur-renders + bundle](#risque-performances-sur-renders)
- [Accessibilité oubliée (a11y)](#risque-accessibilite-oubliee)
- [Catch silencieux — erreur inconnue sans feedback utilisateur](#risque-catch-silencieux)
- [Auto-reset d’un état dégradé sur toute réponse 2xx](#risque-auto-reset-etat-degrade)
- [Refresh store en fire-and-forget après mutation](#risque-refresh-store-fire-and-forget)

---

<a id="risque-auth-cote-client"></a>
## Auth côté client (mauvaise séparation des responsabilités)

### Risques

- Le front “décide” des permissions au lieu d’appliquer un contrat backend
- Affichage d’actions interdites / fuite d’informations dans l’UI
- Tokens stockés de façon dangereuse (XSS)

### Symptômes

- Différences entre “ce que l’UI permet” et “ce que l’API accepte”
- Bugs “ça marche chez moi” selon sessions/rôles
- Incohérences sur refresh / multi-tab

### Bonnes pratiques / mitigations

- Le backend reste source de vérité (authz)
- Cacher l’UI ≠ sécuriser : toujours sécuriser côté API
- Stockage tokens : privilégier cookies httpOnly si modèle adapté
- Gérer proprement expiration/refresh + révocation

### Contexte technique

- Observé : (à compléter)
- Stack : (à préciser)

---

<a id="risque-erreurs-silencieuses"></a>
## Erreurs silencieuses / écrans blancs

### Risques

- Exceptions non gérées → app inutilisable
- États async mal gérés → UI incohérente (loading infini, vide incompris)

### Symptômes

- Écran blanc après une action
- Toast générique “Une erreur est survenue” sans corrélation
- Pas de moyen de reproduire / diagnostiquer

### Bonnes pratiques / mitigations

- Pattern “états UI explicites” (loading/empty/error)
- Boundary d’erreur UI + fallback
- Logging minimal côté client avec requestId/traceId quand possible

---

<a id="risque-melange-server-client-state"></a>
## Mélange server state / client state

### Risques

- Cache pollué par des états UI (onglets, filtres)
- UI qui reflète une donnée périmée sans le savoir
- Re-renders et bugs de synchronisation

### Symptômes

- “Ça revient tout seul” après refresh
- Données affichées ≠ données du backend
- Debug très long car état implicite

### Bonnes pratiques / mitigations

- Séparer explicitement server state vs client state
- Invalidation/reload explicite du server state
- État UI local réinitialisable

---

<a id="risque-api-state-local-ecran"></a>
## Appels API gérés en state local d’écran (refactor coûteux)

### Risques

- Server state non partageable entre écrans (liste/detail, wizard, tabs) → duplication et incohérences
- Pas de cache / invalidation standard → bugs subtils et re-fetchs inutiles
- Refactor tardif quand l’epic s’étend (mutations, cache, offline, pagination)

### Symptômes

- Même appel API recopié dans plusieurs écrans
- Un écran “A” modifie une ressource mais l’écran “B” n’est jamais rafraîchi
- Code review qui force un refactor vers un store/cache au milieu d’un epic

### Bonnes pratiques / mitigations

- Par défaut : créer un store de domaine (ex : Zustand) ou un cache de server state pour tout domaine susceptible d’être réutilisé
- Centraliser `isLoading`/`error`/`data` et la stratégie de refresh/invalidation
- Exception acceptable : état purement UI, local et jetable (ex : input de recherche, filtres temporaires non persistés)

---

<a id="risque-performances-sur-renders"></a>
## Performances : sur-renders + bundle non maîtrisé

### Risques

- App lente sur mobile
- Bundle qui grossit sans contrôle
- Chargements inutiles (images, libs)

### Symptômes

- Input lag
- Temps de chargement qui dérive à chaque feature
- Requêtes réseaux inutiles

### Bonnes pratiques / mitigations

- Lazy loading routes/features
- Mesurer (au minimum) : temps de chargement + re-renders critiques
- Politique images (formats, tailles, lazy)
- Audit régulier des dépendances

---

<a id="risque-accessibilite-oubliee"></a>
## Accessibilité oubliée (a11y)

### Risques

- App inutilisable au clavier/lecteur d’écran
- Régressions silencieuses sur focus/labels

### Symptômes

- Modales impossibles à fermer au clavier
- Inputs sans labels/erreurs non annoncées
- Focus “perdu”

### Bonnes pratiques / mitigations

- Checklist a11y minimale sur chaque écran clé
- Gestion de focus (modales, erreurs formulaire)
- Labels/aria cohérents + tests simples

---

<a id="risque-catch-silencieux"></a>
## Catch silencieux — erreur inconnue sans feedback utilisateur

### Risques

- Un `catch` qui ne traite que les cas connus laisse l'utilisateur face à un spinner qui disparaît sans message
- L'état d'erreur reste implicite → impossible de diagnostiquer ou de reproduire

### Symptômes

- Bouton spinner qui s'arrête, rien ne se passe
- Pas de toast / message d'erreur affiché
- Erreur "avalée" silencieusement dans les logs

### Bonnes pratiques / mitigations

```typescript
} catch (err: unknown) {
  const code = (err as { code?: string }).code;
  if (code === 'SUBSCRIPTION_REQUIRED') {
    setSubscriptionRequired(true);
  } else {
    setError('Une erreur est survenue. Veuillez réessayer.'); // toujours un fallback
  }
}
```

- **Règle** : tout `catch` doit avoir une branche `else` (ou `default`) qui affiche un feedback utilisateur explicite.
- Contexte technique : React Native / Expo — 09-03-2026

---

<a id="risque-auto-reset-etat-degrade"></a>
## Auto-reset d’un état dégradé sur toute réponse 2xx

### Risques

- Le client sort trop tôt d’un mode dégradé alors que la cause serveur est toujours présente
- Le bandeau ou l’état read-only clignote puis disparaît à tort
- Les utilisateurs retentent une action d’écriture qui va encore échouer

### Symptômes

- Un GET réussi réinitialise `isReadOnly` ou `isDegraded`
- L’UI redevient “normale” alors que Redis ou un service critique est toujours indisponible
- Les erreurs reviennent immédiatement à la prochaine mutation

### Bonnes pratiques / mitigations

- Ne réinitialiser l’état dégradé qu’après une requête d’écriture réussie
- Exclure `GET` et `HEAD` de la logique de reset
- Conserver le mode dégradé tant qu’aucune mutation n’a prouvé le retour à la normale
- Contexte technique : React Native / Expo — 10-03-2026

---

<a id="risque-refresh-store-fire-and-forget"></a>
## Refresh store en fire-and-forget après mutation

### Risques

- L’UI affiche un succès alors que la resynchronisation a échoué
- État local incohérent avec l’état serveur
- Erreurs silencieuses impossibles à diagnostiquer

### Symptômes

- Mutation réussie puis store jamais rafraîchi
- Spinner coupé avant que l’écran soit réellement à jour
- Données anciennes qui persistent jusqu’au prochain reload

### Bonnes pratiques / mitigations

- `await` explicite du refresh si l’UI dépend du résultat
- Gestion d’erreur dédiée sur la phase de resynchronisation
- N’utiliser le fire-and-forget que pour un effet secondaire réellement non bloquant
- Contexte technique : React Native / Expo — 10-03-2026
