veza/apps/web/AUDIT_STRATEGIQUE_FRONTEND_2025.md

281 lines
20 KiB
Markdown
Raw Normal View History

# Audit stratégique frontend — Veza
**Date** : 2025-02-08
**Équipe fictive** : Staff Frontend Engineer, Principal Product Designer, Architecte logiciel senior, Tech Lead perf/a11y/DX, Product Manager.
**Objectif** : Diagnostic sans complaisance, écart vs Discord/Spotify, feuille de route et recommandations actionnables.
---
## A. Diagnostic brutal mais honnête
### Niveau global de lapplication
**Verdict : intermédiaire à senior (6/10), avec des poches de niveau amateur et quelques bases solides.**
Justification en une phrase : *Vous avez mis en place une structure feature-based, un design system documenté, des patterns (skeletons, optimistic UI, MSW), et une batterie de tests/stories, mais lexécution est inégale, les règles du projet sont régulièrement violées (valeurs arbitraires, double source dauth), et le ressenti “produit fini” est entamé par des correctifs visibles (fix-*.css, patches dans main.tsx) et une cohérence a11y/UX partielle.*
- **Architecture** : Correcte en intention (features/, services/, stores/, router lazy), mais **artisanale** dans les détails (double source dauth, callbacks no-op en route config, stores globaux vs feature stores).
- **Qualité du code** : Bonne sur les parties auditées (auth, playlists, player), **dette structurelle** (duplication auth Context vs Store, ~130 fichiers avec valeurs Tailwind arbitraires malgré une règle explicite).
- **Performance** : Bonne base (lazy routes, React Query, skeletons), mais pas de stratégie Suspense/streaming ni de priorisation explicite perçue vs mesurée. Voir *Checklist priorisation performance* ci-dessous.
- **Design system** : **Solide** sur le papier (KŌDŌ v3, tokens, layout primitives, dark mode), **fragile** en pratique (règles de layout violées massivement, pas de lint pour les primitives).
- **UX / micro-interactions** : Suffisante pour un produit interne, **insuffisante** pour un produit “premium” (feedback incohérent, états vides/erreur pas toujours traités de façon homogène).
- **Accessibilité** : Présente (aria, roles sur une partie des composants), mais **bonus** et non pilier (a11y Storybook en `todo`, pas de politique WCAG claire).
- **DX** : **Correcte** (docs internes, MSW, Storybook centralisé), mais **terrain miné** sur certains sujets (deux façons daccéder à lauth, nombreux fix-* à comprendre, seuils de coverage peutêtre non tenus).
---
### 5 forces réelles
1. **Design system pensé et documenté**
`index.css` : palette sémantique, layout primitives (`max-w-layout-content`, `min-h-layout-main`, etc.), easings, keyframes, dark mode. Les règles du projet interdisent les valeurs arbitraires et imposent ces primitives — même si non respectées partout, la cible est claire.
2. **Storybook-first et MSW bien intégrés**
Preview avec MSW en mode strict (`onUnhandledRequest: 'error'`), décorateur global (Theme, QueryClient, Router, Toast, Audio, Auth, i18n), pas dimport de providers dans les stories. Handlers MSW très complets (~1600 lignes). Environ 324 stories : bonne couverture de surface.
3. **Patterns de chargement et derreur explicites**
`LOADING_STATES_PATTERN.md`, composant `LoadingState` (spinner / inline / skeleton / minimal), skeletons dédiés par page (UserProfilePage, CloudFileBrowser, CourseDetailView, etc.). Données servies par React Query avec états loading/error gérés au niveau page.
4. **Optimistic UI et utilitaires métier**
`OPTIMISTIC_UPDATES.md` et `optimisticUpdates.ts` (createOptimisticUpdate, createArrayOptimisticUpdate, createToggleOptimisticUpdate). Utilisation dans playlists, likes, commentaires. Rollback et invalidation documentés.
5. **Surface de tests unitaires et dintégration**
~186 fichiers `*.test.tsx` et ~100 `*.test.ts`, intégration auth/playlists/player, Vitest avec coverage (seuils 80 % configurés). E2E Playwright et visuels présents. Base saine pour monter en rigueur.
---
### 5 faiblesses structurelles majeures
1. **Double source de vérité pour lauth**
`AuthContext` + `useAuth()` (Storybook, ProtectedRoute, quelques pages) vs `authStore` (Zustand) + `useAuthStore()` (App, Sidebar, Login, etc.). `App.tsx` et hydratation utilisent le store ; le décorateur Storybook utilise le Context. Risque de désync, double appel à lAPI, et confusion pour tout nouveau dev. Dette darchitecture prioritaire.
2. **Règles de layout systématiquement violées**
La règle “pas de valeurs arbitraires (w-[…], z-[…], etc.)” et “utiliser les layout primitives” est explicite dans `.cursorrules` et `index.css`, mais environ **130 fichiers** utilisent `w-[`, `h-[`, `z-[`, etc. La Sidebar ellemême utilise `w-64`, `z-[90]`, `z-[95]`, `left-6`, `top-20`. Le design system est sousexploité et la cohérence visuelle fragilisée.
3. **Correctifs applicatifs visibles et fragiles**
`main.tsx` et `App.tsx` chargent plusieurs correctifs : `fix-input-focus.css`, `fix-login-form.css`, `fixDisplayIssues`, `fixInputFocus`. Cela indique des problèmes de conception (focus, formulaires) résolus par patch plutôt quà la source. Coût de maintenance et risque de régression élevé.
4. **Route config et couplage produit**
`routeConfig.tsx` injecte des callbacks vides dans les éléments de route : `onCreateProduct={() => {}}`, `onNavigateTrack={() => {}}`. La routing ne doit pas porter de callbacks métier ; ces props devraient remonter dun layout ou dun contexte. Signe que la frontière entre routing et feature nest pas claire.
5. **Accessibilité et qualité perçue en option**
Storybook : `a11y: { test: 'todo' }`. Beaucoup de composants ont des `aria-*` ou `role`, mais de façon non systématique (ex. `KodoEmptyState` sans role/aria pour létat vide, Sidebar sans `role="navigation"` ni `aria-label`). Pas de politique WCAG ni de critères de sortie a11y. Laccessibilité reste un “plus” et non un pilier, ce qui limite la maturité produit et linclusivité.
---
## B. Écart avec Discord & Spotify
Tableau comparatif **technique et concret** (pas didéalisation).
| Dimension | Discord (référence) | Spotify (référence) | Veza (état actuel) | Écart principal |
|----------|--------------------|--------------------|--------------------|------------------|
| **Architecture** | Domains clairs, state par feature, peu de global ; routing découplé du métier. | App shell léger, données par view, cache agressif. | Features + stores globaux + double auth (Context + Store). | Une seule source dauth ; pas de callbacks dans la route config ; frontière nette layout/feature. |
| **UX** | Feedback immédiat, états loading/empty/error partout, modales et transitions prévisibles. | Transitions fluides, skeletons systématiques, pas de “trous” visuels. | Skeletons et LoadingState présents mais pas partout ; empty/error variables ; correctifs CSS. | Checklist loading/empty/error par écran ; suppression des fix-* par refonte des composants concernés. |
| **Performance** | Time to interactive court, lazy lourd, prioritisation du above-the-fold. | Streaming / progressive, cache et préchargement. | Lazy routes OK ; pas de Suspense boundary au niveau route ; pas de stratégie “perçu vs mesuré”. | Suspense + skeletons aux limites de route ; métriques perçues (LCP, INP) et objectifs chiffrés. |
| **Design** | Cohérence stricte, tokens partout, pas de valeurs en dur. | Design system unique, thème et spacing prévisibles. | Tokens et primitives définis mais ~130 fichiers en arbitraire ; primitives peu utilisées. | Lint (ou rule) qui interdit les arbitraires ; migration progressive vers primitives. |
| **Sensation de qualité** | App “qui ne bugue pas”, erreurs gracieuses, pas de demi-mesures visibles. | Polish, micro-interactions, pas de formulaires “cassés” en prod. | Correctifs visibles (fix-*), callbacks no-op, incohérences a11y. | Réduire la dette visible : un correctif = une issue de refonte ; zero no-op dans la config. |
En résumé : **Veza a les briques (design system, patterns, tests, Storybook)** mais manque de **rigueur dexécution** (une seule auth, respect des règles de layout, accessibilité et UX traitées comme non négociables) et de **finition** (plus de polish, moins de patches) pour se rapprocher de Discord/Spotify.
---
### Checklist priorisation performance
À traiter dans cet ordre pour maximiser limpact ressenti sans tout refactorer :
1. **First meaningful paint**
- Éviter tout JS bloquant avant le premier rendu (splitting par route déjà en place).
- Sassurer que le shell (header + sidebar ou placeholder) + un skeleton de contenu saffichent sans attendre les données.
2. **Skeletons systématiques**
- Chaque page qui fetch des données doit afficher un skeleton dédié (pas un spinner générique) pendant le chargement.
- Déjà partiellement fait ; compléter les écrans manquants (voir Phase 2).
3. **Optimistic UI pour les actions fréquentes**
- Like, follow, add to playlist, etc. : mise à jour immédiate + rollback si erreur.
- Déjà documenté et utilisé ; étendre aux actions qui nont pas encore le pattern.
4. **Réduction des re-renders inutiles**
- Vérifier les composants lourds (listes, tableaux) : memo, sélecteurs Zustand fins, ou découpage pour limiter les sous-arbre qui se re-rendent.
- React Query évite déjà des refetch inutiles si `staleTime` est configuré ; auditer les queryKeys et invalidation pour éviter des cascades.
5. **Métriques et objectifs**
- Mesurer LCP, INP (ou FID), TTI sur les parcours critiques (login, dashboard, lecture).
- Fixer des objectifs (ex. LCP < 2.5 s sur 3G) et un process de revue en cas de régression.
6. **Suspense aux limites de route**
- Envelopper les arbres de route dans `<Suspense fallback={<PageSkeleton />}>` pour que le navigateur puisse afficher le fallback sans attendre tout le bundle enfant.
- Optionnel mais utile dès que le bundle par route grossit.
---
## C. Feuille de route de transformation
### Phase 1 — Fondations (ce qui doit être corrigé avant tout)
**Objectifs**
- Une seule source de vérité pour lauth.
- Arrêt des correctifs “magiques” et renforcement du respect du design system.
**Actions concrètes**
1. **Unifier lauth**
- Décider : soit tout sur Zustand (authStore), soit tout sur Context (AuthProvider). Recommandation : **Zustand** (déjà utilisé par App, hydratation, Sidebar).
- Migrer tous les usages de `useAuth()` vers `useAuthStore()` (ou linverse si vous choisissez Context).
- Faire du AuthProvider un simple consommateur du store (sil est gardé pour Storybook/compat).
- Supprimer la duplication et documenter la décision dans un ADR.
2. **Supprimer les callbacks de la route config**
- Retirer `onCreateProduct`, `onNavigateTrack`, etc. des éléments de route.
- Fournir ces comportements via layout (contexte ou props depuis un parent commun) ou navigation programmatique + state.
3. **Remplacer les fix-* par des corrections à la source**
- Pour chaque `fix-input-focus`, `fix-login-form`, etc. : identifier le composant (formulaire, input, focus trap) et corriger le focus, les états et le style dans le composant ou le design system.
- Supprimer les imports de fix-* une fois le comportement correct par défaut.
4. **Lint / règle pour les valeurs arbitraires**
- Règle ESLint (ou plugin Tailwind) interdisant les classes du type `w-[...]`, `h-[...]`, `z-[...]` pour layout/spacing (avec liste dexceptions si besoin).
- Introduire les exceptions progressivement et traiter les ~130 fichiers par batch (par répertoire ou par feature).
**Risques**
- Régression auth si la migration nest pas testée (E2E login/logout, refresh, Storybook).
- Régressions visuelles si on supprime les fix-* sans vérifier tous les écrans.
**Indicateurs de réussite**
- Un seul mécanisme dauth utilisé partout ; plus dimport de fix-* dans `main.tsx`/`App.tsx`.
- Aucun callback métier dans `routeConfig.tsx`.
- Lint en place et au moins un premier batch (ex. `components/layout`) sans violations.
---
### Phase 2 — Stabilisation et cohérence
**Objectifs**
- Loading / empty / error systématiques.
- Accessibilité traitée comme exigence, pas comme option.
- Design system respecté (primitives, tokens).
**Actions concrètes**
1. **Checklist par écran**
- Pour chaque page/feature : état Loading (skeleton dédié ou générique), Empty (KodoEmptyState ou équivalent), Error (ErrorDisplay + retry).
- Documenter la checklist et lappliquer aux nouvelles features ; remédier les écrans existants sans état manquant.
2. **Accessibilité**
- Remplacer `a11y: { test: 'todo' }` par une config réelle dans Storybook (règles axe-core, seuils).
- Définir un niveau cible (ex. WCAG 2.1 AA) et une liste de composants critiques (formulaires, navigation, player).
- Corriger Sidebar (role="navigation", aria-label), KodoEmptyState (role/aria pour empty state), et autres composants identifiés par un audit axe.
3. **Migration vers layout primitives**
- Remplacer progressivement les valeurs arbitraires par les classes du design system (`max-w-layout-content`, `min-h-layout-main`, etc.) et léchelle Tailwind.
- Prioriser : layout (Sidebar, Navbar, content), puis composants partagés (modales, cartes).
4. **Optimistic UI et erreurs**
- Vérifier que tous les usages doptimistic updates ont un rollback et un feedback utilisateur en cas déchec (toast, réversion visuelle).
- Centraliser la logique derreur (ex. `apiToastHelper`, ErrorDisplay) pour éviter des traitements ad hoc.
**Risques**
- Scope trop large si on veut tout migrer dun coup ; prioriser par trafic ou par criticité.
**Indicateurs de réussite**
- Chaque écran couvert par la checklist loading/empty/error.
- Storybook a11y activé et 0 violation sur les stories des composants critiques.
- Réduction mesurée du nombre de violations “arbitraires” (objectif chiffré par sprint).
---
### Phase 3 — Polish et excellence perçue
**Objectifs**
- Transitions et micro-interactions cohérentes.
- Performance perçue (skeleton, priorisation) et mesurée (LCP, INP).
- Réduction des “petits bugs” visuels et comportementaux.
**Actions concrètes**
1. **Suspense et streaming**
- Définir des Suspense boundaries au niveau des routes (ou des sous-arbres) avec fallback skeleton.
- Vérifier que le premier paint utile (header + skeleton contenu) est rapide.
2. **Métriques et objectifs**
- Suivre LCP, INP (ou FID), TTI.
- Définir des objectifs (ex. LCP < 2.5 s sur 3G) et un process de revue si dégradation.
3. **Micro-interactions**
- Utiliser les variables déjà définies (`--duration-fast`, `--ease-out`, etc.) partout où cest pertinent (hover, focus, modales).
- Passer en revue les CTA et les listes interactives pour un feedback immédiat (disabled + spinner ou état “loading” sur le bouton).
4. **Nettoyage produit**
- Supprimer ou documenter tout code mort et composants non utilisés.
- Sassurer quaucun no-op ou placeholder visible ne reste dans les parcours principaux.
**Risques**
- Sur-optimisation sans mesure ; garder les métriques comme garde-fou.
**Indicateurs de réussite**
- LCP/INP dans la cible sur les parcours critiques.
- Pas de régression a11y ; design system respecté sur les nouvelles livraisons.
---
### Phase 4 — Scalabilité et long terme
**Objectifs**
- Code prêt pour 10× fonctionnalités et 10× équipe.
- Dette technique visible et pilotée.
- Expérimentations (A/B, feature flags) sans tout casser.
**Actions concrètes**
1. **Frontières de domaines**
- Clarifier ce qui vit dans `features/` vs `components/` (ex. pas de duplication forms auth dans les deux).
- Règle simple : composants réutilisables et design system dans `components/` ; logique métier et pages dans `features/`.
2. **State et données**
- Documenter quand utiliser React Query vs Zustand (server state vs client state).
- Éviter de dupliquer des données serveur dans des stores globaux (library, etc.) sans stratégie dinvalidation claire.
3. **Feature flags et expérimentations**
- Introduire un mécanisme léger de feature flags (build-time ou runtime) pour découpler déploiement et activation.
- Tester une feature derrière flag sans impacter les parcours principaux.
4. **Documentation et onboarding**
- Un README frontend à jour (structure, auth unique, design system, comment lancer Storybook et tests).
- Créer ou mettre à jour `docs/STORYBOOK_CONTRACT.md` (référencé dans le code) : états requis (Loading, Error, Empty), pas de providers dans les stories, MSW pour lAPI.
**Risques**
- Refactors trop larges ; privilégier des incréments et des ADR pour les décisions darchitecture.
**Indicateurs de réussite**
- Nouveau dev opérationnel (première feature livrée) en un temps cible (ex. &lt; 2 jours).
- Dette listée et priorisée (ex. backlog “dette” avec critères de sortie).
---
## D. Recommandations non évidentes
- **Ne pas “nettoyer” tout le code arbitraire dun coup.**
Une équipe junior aurait tendance à tout remplacer en un big bang. Mieux vaut : lint qui bloque en *nouveau* code, et migration par zones (layout dabord, puis composants partagés). Les 130 fichiers se traitent par lots avec revue visuelle et régression.
- **Traiter lauth comme un projet de migration, pas comme une tâche.**
Faire un inventaire exhaustif (grep `useAuth` / `useAuthStore`), un plan de migration (ordre des fichiers), des tests E2E et Storybook qui valident login/logout/refresh, puis migrer avec une PR par zone. Un “quick fix” (tout basculer dun coup) sans tests est risqué.
- **Ne pas activer a11y dans Storybook sans budget de correction.**
Activer axe avec des règles strictes du jour au lendemain va faire exploser les “failed”. Mieux : activer sur un sous-ensemble de stories (ex. composants ui/ + layout), corriger ces stories, puis élargir. a11y doit devenir un critère de merge sur les composants critiques.
- **Documenter pourquoi chaque fix-* existe.**
Avant de les supprimer, ajouter un commentaire ou un petit doc : “fix-login-form existe parce que …”. Cela évite de recréer le même patch plus tard et guide la refonte (quel composant doit prendre en charge le focus, etc.).
- **Mettre des indicateurs de “maturité” par feature.**
Une grille simple (loading / empty / error / a11y / tests / Storybook) par feature ou page. Permet de prioriser les remédiations et de ne pas prétendre que “tout est à niveau” alors que certaines zones sont encore fragiles.
- **Réserver du temps “dette” dans chaque sprint.**
Un projet “correct” reporte la dette ; un projet qui vise lexcellence en consacre une part fixe (ex. 20 %). Chaque sprint : X violations arbitraires en moins, Y écrans avec checklist complète, Z stories avec a11y passante.
- **Éviter dajouter de nouveaux stores globaux “au fil de leau”.**
Vous avez déjà auth, ui, library, cart, chat. Avant den ajouter un nouveau : vérifier si React Query + état local (ou store de feature) ne suffit pas. Les stores globaux multiplient les sources de vérité et compliquent le debugging et la cohérence.
---
## Synthèse
- **Niveau actuel** : Intermédiaire à senior (6/10), avec de vraies bases (design system, patterns, MSW, tests, Storybook) et des faiblesses structurelles (double auth, violations de layout, correctifs, a11y en option).
- **Écart Discord/Spotify** : Moins une question de stack que de **rigueur dexécution** (une auth, respect des règles, a11y et UX non négociables) et de **finition** (moins de patches, plus de polish).
- **Ordre recommandé** : Fondations (auth, route config, fix-*, lint) → Stabilisation (checklist loading/empty/error, a11y, primitives) → Polish (Suspense, métriques, micro-interactions) → Scalabilité (frontières, state, flags, doc).
- **Ton** : Exigeant mais réaliste : le projet peut viser un niveau “world-class” à condition de traiter la dette identifiée comme un plan structuré et de ne pas ajouter de nouvelle dette sur lauth et le layout.