Backend: 21 entrées (general, prisma, contracts, auth, patterns) Frontend: 9 entrées (navigation, tests, general, performance, patterns) Workflow: 5 entrées (story-tracking) Nouveau fichier: backend/patterns/general.md 95_a_capitaliser.md purgé. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10 KiB
Frontend — Risques & vigilance : Général
Extrait de la base de connaissance Lead_tech. Voir
knowledge/frontend/risques/README.mdpour l'index complet.
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
Regex globale /g en singleton — bug lastIndex stateful
Risques
- Une regex avec flag
/gou/ydéfinie comme constante au niveau module maintient un étatlastIndexentre les appels String.prototype.replace()réinitialiselastIndex, mais.test()ou.exec()ne le font pas → bug stateful difficile à détecter, souvent introduit par un refactor ultérieur
Symptômes
.test(str)retourne alternativementtrue/falsesur la même chaîne selon l'ordre d'appel- Bug non reproductible en isolation, uniquement en séquence d'appels
Bonnes pratiques / mitigations
// ❌ RISQUÉ — regex globale partagée entre tous les appels
const LINK_PATTERN = /https?:\/\/\S+/gi;
function processLinks(content: string) {
return content.replace(LINK_PATTERN, ...); // OK today
// Mais si quelqu'un ajoute LINK_PATTERN.test(x) ailleurs → bug lastIndex
}
// ✅ SÛR — nouvelle instance à chaque appel, aucun état partagé
function makeLinkPattern(): RegExp {
return /https?:\/\/\S+/gi;
}
function processLinks(content: string) {
return content.replace(makeLinkPattern(), ...);
}
-
Règle : les regex avec flag
/gou/yutilisées pour transformation de strings → toujours créer via une factory, jamais en singleton de module -
Contexte technique : TypeScript / React Native — app-alexandrie 24-03-2026
Alert.prompt iOS-only — fonctionnalité silencieusement cassée sur Android
Risques
Alert.promptne déclenche rien sur Android (retourneundefinedsilencieusement).- Les tests unitaires passent (mock), mais le flux ne s'exécute jamais sur 50 % des devices en production.
Symptômes
- Flux de saisie utilisateur qui fonctionne sur simulateur iOS mais est inactif sur Android
- Aucun message d'erreur côté dev ni côté utilisateur
Bonnes pratiques / mitigations
- Ne jamais utiliser
Alert.promptdans un projet Expo cross-platform. - Remplacer par une modale custom :
Modal+TextInputReact Native — portable, accessible, testable. - Wrapper le
TextInputdansKeyboardAvoidingViewavecbehavior={Platform.OS === 'ios' ? 'padding' : 'height'}.
- Contexte technique : React Native / Expo cross-platform — app-alexandrie 31-03-2026
Primitive UI couplée au contexte parent (layout ou namespace métier)
Risques
- Une primitive générique (
PageShell,ContentCard,SectionWrapper) qui embarque des classes de surface, de largeur ou de namespace métier devient non réutilisable hors de son premier contexte - Le couplage reste silencieux au lint et au typecheck, puis force l'ajout progressif de props
variant,layout,widthou de classes externes contradictoires
Symptômes
- La primitive applique directement des classes comme
.card,.card--dashboard,.dashboard__item,.profile__card - Le parent doit contourner le style natif de la primitive pour l'utiliser dans un autre écran
- Les classes
namespace__elementfuitent dans des composants supposés agnostiques du domaine
Bonnes pratiques / mitigations
- Une primitive pose le squelette sémantique ; le parent pose la surface visuelle (card, width, background, espacement de contexte)
- Ne pas injecter de classes de namespace métier sur une primitive générique via
class - Si une variation réutilisable existe vraiment, l'exprimer via une API explicite et bornée (
tone,variant) plutôt que par des classes métier ad hoc - Contexte technique : Vue 3 / CSS modulaire — RL799_V2, 02-04-2026
Migration partielle vers un composant standard — classes legacy conservées
Risques
- La coexistence de classes legacy (
.primary,.ghost,.danger) et de classes du nouveau composant (.app-btn--primary,.app-btn--ghost) crée une ambiguïté durable de convention - Les nouveaux développements continuent d'utiliser l'ancien système faute de règle claire, ce qui ralentit la standardisation
Symptômes
- Deux façons de produire la même affordance coexistent dans le même repo
- Un composant dédié existe, mais des liens ou boutons continuent d'utiliser les anciennes classes globales
Bonnes pratiques / mitigations
- Lorsqu'un composant standardise une affordance, supprimer en même temps les classes CSS globales équivalentes
- Si un reliquat legacy doit rester temporairement, documenter explicitement son périmètre et sa date de sortie attendue
- En review, traiter toute nouvelle utilisation d'une classe legacy équivalente comme une régression de standardisation
- Contexte technique : Vue 3 / design system léger — RL799_V2, 02-04-2026
ARIA roles sans comportement clavier associé
Risques
- Poser
role="menu"/role="menuitem"sur un composant sans implémenter le pattern clavier donne une fausse impression d'accessibilité - Les rôles ARIA trompent les lecteurs d'écran et violent WCAG 2.1 (4.1.2 Name, Role, Value)
Symptômes
role="menu"sans fermeture viaEscape- Pas de navigation
ArrowUp/ArrowDownni de roving tabindex
Bonnes pratiques / mitigations
Poser role="menu" / role="menuitem" implique obligatoirement :
- Fermeture via
Escape - Navigation via
ArrowUp/ArrowDown - Roving tabindex (
tabindex="0"sur l'item actif,-1sur les autres) - Focus automatique du premier item à l'ouverture
Règle : ne jamais poser un role ARIA de widget interactif sans implémenter le pattern clavier correspondant (cf. WAI-ARIA Authoring Practices)
- Contexte technique : Vue 3 / accessibilité — RL799_V2 03-04-2026
Duplication de logique métier dans les composants UI (monorepo)
Risques
- Dans un monorepo avec un package partagé (
shared), les fonctions utilitaires métier (ex: conversion grade → rang) sont redéfinies localement dans les composants ou pages frontend - Ce type de duplication silencieuse provoque des divergences à terme
Symptômes
- Fonction
switch/caseou mapping identique à une fonction déjà exportée parshared - Même signature et même logique dans plusieurs fichiers de couches différentes (composant, page, service)
Bonnes pratiques / mitigations
-
Les fonctions utilitaires métier ne doivent jamais être redéfinies localement dans les composants ou pages frontend
-
Importer systématiquement depuis le package partagé (
@monrepo/sharedou équivalent) plutôt que de copier-coller la logique -
Signal review : grep des fonctions utilitaires existantes dans shared avant de valider un nouveau switch/case
-
Contexte technique : Vue 3 / monorepo — RL799_V2 06-04-2026
Event listeners globaux pour interactions modales
Risques
window.addEventListener('keydown')pour capturer Escape dans une modale crée un listener global qui peut confliter avec d'autres modales- Le listener fuit si le composant est mal démonté
Symptômes
window.addEventListener('keydown', handler)dans un composant modale- Cleanup dans
onBeforeUnmountmais risque de fuite si le démontage échoue
Bonnes pratiques / mitigations
-
Utiliser
@keydown.escapedirectement sur l'élément dialog avectabindex="-1"+ focus automatique à l'ouverture -
Élimine le besoin de cleanup et scope l'interaction au composant
-
Signal review : dans tout composant modale, vérifier que les listeners clavier sont sur l'élément, pas sur
window -
Contexte technique : Vue 3 / modales — RL799_V2 06-04-2026
Boutons imbriqués dans les listes interactives
Risques
- Un
<button>ou<a>contenant un autre élément interactif (bouton, lien) est du HTML invalide - Casse l'accessibilité et produit un comportement imprévisible selon les navigateurs
Symptômes
<button>conteneur avec un<button>enfant (ex: étoile favori dans une carte cliquable)- Comportement de clic imprévisible, événements qui ne remontent pas correctement
Bonnes pratiques / mitigations
-
Utiliser un
<div>conteneur avec des boutons séparés côte à côte -
Si toute la ligne doit être cliquable, séparer la zone de clic principale (bouton content) de l'action secondaire (bouton étoile/action)
-
Signal review : dans tout composant liste avec actions inline, vérifier qu'aucun élément interactif n'est imbriqué dans un autre
-
Contexte technique : HTML / accessibilité — RL799_V2 06-04-2026
Fire-and-forget sans feedback sur actions non-critiques
Risques
- Une action asynchrone non-critique (cache IndexedDB, analytics, sync) lancée en fire-and-forget sans feedback masque les échecs
- L'utilisateur croit que l'action est faite (ex: document disponible hors-ligne) alors qu'elle a échoué
Symptômes
.then(...).catch(() => {})sur une action secondairecatch { /* ignore */ }sans log ni feedback visuel
Bonnes pratiques / mitigations
-
Même si l'action est non-bloquante, afficher un feedback discret en cas d'échec (toast, badge absent)
-
L'utilisateur doit pouvoir distinguer "fait" de "échoué silencieusement"
-
Signal review : tout
.catch(() => {})oucatch { /* ignore */ }mérite au minimum un log ou un feedback visuel -
Contexte technique : frontend / actions async — RL799_V2 07-04-2026