Compétence de Refactoring
Utilisez cette compétence lors du refactoring de fonctionnalités pour les aligner sur des motifs établis, éliminer les duplications et améliorer la réutilisation du code.
Pour savoir si l'interface utilisateur appartient à la pattern-library ou à une composition locale, utilisez la compétence design-system.
Analyse Pré-Refactoring
1. Identifier les Fonctionnalités Similaires
- Trouvez des fonctionnalités comparables dans la base de code avec des fonctionnalités similaires
- Comparez les structures de répertoires, les motifs de composants et l'utilisation des utilitaires
- Recherchez des fonctionnalités avec des fonctionnalités similaires mais des implémentations différentes
- Utilisez la recherche sémantique pour trouver des fonctionnalités connexes : "fonctionnalités avec des motifs de tableau/DataGrid similaires"
2. Trouver les Opportunités de Duplication
Recherchez des motifs de code dupliqué :
- Composants : Cherchez des fichiers de composants similaires entre les fonctionnalités (même nom ou fonctionnalité similaire)
- Hooks : Vérifiez la logique de hook en double (hooks de tableau, hooks de filtre, hooks d'agrégation)
- Utilitaires : Trouvez des fonctions utilitaires dupliquées (agrégations, formatteurs, validateurs, transformateurs)
- Styles : Identifiez des motifs de prop
sxen double ou des styles en ligne - Constantes : Vérifiez les constantes ou objets de configuration en double
- Types : Cherchez des définitions de type ou interfaces en double
3. Vérifier les Utilitaires Partagés
Avant de créer de nouveaux utilitaires, vérifiez ceux qui existent :
- Recherchez dans les répertoires
@/util/pour des utilitaires spécifiques au domaine - Vérifiez
@/util/datespour les fonctions de formatage de date - Vérifiez
@/util/numberspour les fonctions de formatage de nombre - Vérifiez
@/components/pour les composants réutilisables - Vérifiez
pattern-librarypour les composants du système de design - Utilisez grep/recherche pour trouver des implémentations existantes avant d'en créer de nouvelles
Processus de Refactoring
Étape 1 : Extraire le Code Partagé
Lorsque vous trouvez des doublons :
-
Créez des utilitaires partagés dans les répertoires
@/util/appropriés :// Exemple : util/[domaine]/[categorie].ts export const fonctionPartagee = () => { ... }; export const constantePartagee = { ... }; -
Mettez à jour toutes les fonctionnalités pour importer depuis l'emplacement partagé :
// Fichier spécifique à une fonctionnalité - ré-exporter depuis le partagé export { fonctionPartagee, constantePartagee } from "@/util/[domaine]/[categorie]"; -
Supprimez les implémentations dupliquées des répertoires de fonctionnalités
-
Mettez à jour toutes les importations dans toute la base de code
Étape 2 : Consolider les Composants
Lorsque vous trouvez des composants dupliqués :
- Identifiez la version la plus complète/riche en fonctionnalités (vérifiez les props, la fonctionnalité, les tests)
- Extrayez vers un emplacement partagé (
@/components/ou emplacement indépendant de la fonctionnalité) - Rendez les props flexibles pour prendre en charge tous les cas d'utilisation (utilisez des types unions, des props optionnelles ou des génériques)
- Mettez à jour les importations dans toutes les fonctionnalités utilisant le composant
- Supprimez les fichiers de composants dupliqués
- Vérifiez que toutes les utilisations fonctionnent toujours correctement
Étape 3 : Aligner les Motifs de Chargement
Pour les états de chargement :
- Vérifiez les fichiers
loading.tsx- doivent être des squelettes simples, pas des mises en page complètes - Supprimez les chargeurs squelettes au niveau des composants - reposez-vous sur
loading.tsxau niveau de la page pour le chargement initial - Lors des rechargements : Affichez les données existantes, pas les squelettes (sauf si explicitement nécessaire pour l'UX)
- Correspondance des motifs avec des fonctionnalités similaires dans la base de code
- Utilisez les conventions App Router de Next.js -
loading.tsxgère le chargement initial, les composants gèrent les états de rechargement
Étape 4 : Consolider les Styles
Pour le style :
- Extrayez les motifs de style réutilisables vers des composants partagés ou des utilitaires de style
- Supprimez les props
sxen ligne lorsqu'un composant partagé existe - Utilisez les composants du système de design de
pattern-libraryau lieu de styles personnalisés - Vérifiez les objets de style en double qui pourraient être des utilitaires partagés ou des extensions de thème
- Suivez les motifs Material-UI - utilisez les valeurs du thème, évitez les nombres magiques
Étape 5 : Vérifier l'Alignement des Motifs
Assurez la cohérence avec les fonctionnalités établies :
- Comparez les structures de répertoires - doivent suivre des motifs similaires (
_components/,_hooks/,_utils/) - Vérifiez les motifs de hooks - des hooks plus simples sont acceptables si les exigences de la fonctionnalité diffèrent
- Vérifiez le flux de données - doit correspondre aux fonctionnalités similaires dans la mesure du possible
- Examinez la composition des composants - doit utiliser des motifs similaires (wrappers, composition, etc.)
- Vérifiez les conventions de nommage - suivez les motifs établis (PascalCase pour les composants, camelCase pour les hooks)
Étape 6 : Consolidation des Fonctions Utilitaires
Pour les fonctions utilitaires :
- Vérifiez si la fonction existe dans les utilitaires partagés d'abord (recherchez dans la base de code)
- Comparez les implémentations - utilisez la version la plus complète/robuste
- Consolidez les doublons - déplacez vers un emplacement partagé, mettez à jour toutes les importations
- Documentez les différences - si les implémentations diffèrent intentionnellement, documentez pourquoi
- Préservez la fonctionnalité - assurez-vous que la version consolidée gère tous les cas d'utilisation
Étape 7 : Nettoyer les Commentaires
Après le refactoring, supprimez les commentaires inutiles :
- Supprimez les notes de migration - Commentaires expliquant d'où vient le code (par exemple, "isCollecting est maintenant exporté depuis Table/utils.ts")
- Supprimez les commentaires évidents - Commentaires qui ne font que reformuler ce que fait le code (par exemple, "// Ré-exporter les agrégations partagées" quand le code montre clairement des ré-exportations)
- Conservez les commentaires explicatifs - Commentaires qui expliquent POURQUOI quelque chose est fait (par exemple, "// Utiliser setTimeout pour s'assurer que l'état du formulaire est mis à jour")
- Conservez les commentaires de logique complexe - Commentaires qui expliquent une logique métier non évidente ou des cas limites
- Supprimez les commentaires TODO/FIXME - Sauf s'il s'agit d'éléments de travail futurs légitimes
Exemples de commentaires inutiles :
// ❌ Inutile - évident d'après le code
// Ré-exporter les agrégations partagées depuis util
export { latest, totalRange } from "@/util/keywords/gridAggregations";
// ❌ Inutile - note de migration, plus nécessaire
// isCollecting est maintenant exporté depuis Table/utils.ts pour éviter la duplication
// Importez-le depuis là-bas : import { isCollecting } from "../KeywordListDetail/Table/utils";
Exemples de bons commentaires :
// ✅ Bon - explique POURQUOI
// Utiliser setTimeout pour s'assurer que l'état du formulaire est mis à jour après le traitement CSV
setTimeout(() => {
void handleSubmit(onSubmit)();
}, 100);
// ✅ Bon - explique une logique complexe
// Un mot-clé est mis à jour si :
// 1. Sa valeur lastUpdated a changé (indicateur le plus fiable), OU
// 2. Il n'est plus en état de collecte ET nous avons assez interrogé (solution de repli)
Étape 8 : Améliorations de la Qualité du Code
Appliquez des motifs de qualité de code cohérents dans tout le code refactoré :
Vérifications Null/Undefined
Simplifiez les vérifications null et undefined en utilisant l'égalité lâche :
// ❌ Verbeux
if (value !== undefined && value !== null) { ... }
if (value !== undefined && value !== null && value !== "") { ... }
// ✅ Simplifié
if (value != null) { ... }
if (value != null && value !== "") { ... }
Remarque : Utilisez != null (égalité lâche) pour vérifier à la fois null et undefined en une seule vérification. C'est idiomatique en TypeScript/JavaScript et plus concis.
Imports Uniquement de Type
Utilisez import type pour les imports uniquement de type afin d'améliorer les performances de construction et de clarifier l'intention :
// ❌ Import d'exécution pour une utilisation uniquement de type
import { SomeType } from "app-api/types/domain";
// ✅ Import uniquement de type
import type { SomeType } from "app-api/types/domain";
Appliquez à :
- Définitions de type depuis
app-api - Imports d'interface
- Types utilitaires uniquement de type
- Tout import utilisé uniquement dans les annotations de type
Supprimer les Assertions de Type Inutiles
Supprimez les assertions de type lorsque TypeScript peut inférer le type correctement :
// ❌ Assertion inutile
const items = data.map((item) => item as SomeType);
// ✅ Laissez TypeScript inférer
const items = data.map((item) => item);
Quand garder les assertions : Uniquement lorsque TypeScript ne peut vraiment pas inférer le type, ou lors du rétrécissement de types d'une manière que TypeScript ne peut pas vérifier.
Supprimer la Fusion Nullish Inutile
Supprimez ?? null lorsque la valeur est déjà nullable ou que TypeScript infère le type correct :
// ❌ Inutile - valueGetter retourne déjà nullable
valueGetter: (value, row) => {
return row.someField ?? null;
};
// ✅ Simplifié
valueGetter: (value, row) => {
return row.someField;
};
Quand garder : Lors de la conversion explicite de undefined en null pour la compatibilité API ou lorsque le système de types l'exige.
Supprimer les Props Inutilisées
Supprimez les props des interfaces de composants et des implémentations lorsqu'elles ne sont pas utilisées :
// ❌ Prop inutilisée
interface ComponentProps {
item: Item;
unusedField: string; // Jamais utilisé dans le composant
}
// ✅ Interface propre
interface ComponentProps {
item: Item;
}
Ajouter des Types Explicites aux Callbacks
Ajoutez des annotations de type explicites aux paramètres de callback lorsque TypeScript ne peut pas les inférer :
// ❌ any implicite
const items = ids.map((id) => { ... });
// ✅ Type explicite
const items = ids.map((id: string | number) => { ... });
Appliquez à : Méthodes de tableau (map, filter, forEach), gestionnaires d'événements et autres callbacks où les types ne sont pas clairs d'après le contexte.
Extraire les Motifs JSX Répétés
Extrayez les motifs JSX répétés dans des composants réutilisables :
// ❌ Motif répété
{
isAggregation ? (
<Typography
variant="body2"
fontWeight="medium"
fontStyle="italic"
color="text.secondary"
>
{formattedValue}
</Typography>
) : (
<Typography variant="body2" fontWeight="medium">
{formattedValue}
</Typography>
);
}
// ✅ Composant extrait
<AggregationTypography
formattedValue={formattedValue}
isAggregation={isAggregation}
/>;
Utiliser des Fonctions d'Usine pour des Implémentations Variées
Lorsque la logique est similaire mais doit gérer différentes structures de données, utilisez des fonctions d'usine :
// ✅ Motif de fonction d'usine
export const createIntentOperators = <T extends { intent?: Intent | Intent[] }>(
normalizeToArray: (value: T) => Intent[]
) => {
return {
// Opérateurs qui fonctionnent avec un tableau normalisé
};
};
// Utilisation dans différentes fonctionnalités
const operators1 = createIntentOperators<Feature1Type>((item) => [item.intent]);
const operators2 = createIntentOperators<Feature2Type>(
(item) => item.intents || []
);
Avantages : Réutilise la logique centrale tout en permettant de gérer correctement différentes structures de données.
Renforcement de la Revue de Code (retour récurrent)
Appliquez ces conseils lors du refactoring touchant les grilles, tableaux de bord, requêtes ou formes partagées "ligne + métriques" (s'aligne avec les retours récurrents des PR de l'interface utilisateur Moz Pro).
DataGrid : bascule du panneau de détail
- Préférez
DetailPanelToggledepattern-librarypour l'expansion/réduction sur une colonne de panneau de détail au lieu d'unIconButton+@mui/iconsad hoc dans une cellule personnalisée. - Câblez l'accessibilité : passez
buttonTabIndex={0}lorsque la cellule doit être accessible au focus via tabulation (voirapp/(default)/competitive-research/top-competing-content/_module/components/Table/columns.tsx,SerpFeaturesCell). - Disposition / centrage : utilisez
sxsurBox/Stack(ou ladirection/spacingdeStacklorsque c'est le motif établi). Ne remplacez passxpar les props système de style héritées de MUI surBox(par exempledisplay,alignItemscomme props de premier niveau)—MUI déprécie cette approche au profit desx.
Typage : éviter les formes de ligne object / anonymes
- Ne faites pas circuler
readonly object[]pourbrand_definitions, fragments de ligne ou listes adjacentes à l'API lorsqu'un type structurel existe ou peut être défini une fois. - Définissez une interface "like" partagée (par exemple
BrandDefinitionLike) dans le moduleutilsde la fonctionnalité ; exportez-la ainsi qu'un seul helper d'affichage (par exemplegetBrandDefinitionDisplayLine) au lieu de dupliquergetBrandNameavec des casts dans les cellules. - Supprimez les casts redondants (
as BrandDefinitionLike[]) après avoir typé les paramètres. - Réutilisez les helpers de domaine depuis
*_module/utils/dataHelperslorsque la forme est un véritableBrandDefinition; utilisez le helper plus lâche uniquement lorsque les charges utiles du prompt-row diffèrent.
Typography vs Box (pattern-library)
- Pour le texte (états vides, "—" en ligne, spans accentuées), préférez
Typographydepattern-libraryaveccomponent="span"lorsque c'est approprié—pas deBoxfaisant office de copie. - API
Typography: utilisezvariant,color,fontWeight,componentlà où ils expriment la sémantique (ce ne sont pas dépréciés). - Wrappers
Box/ disposition : préférezsxpour l'espacement, la disposition flex, le dimensionnement et les valeurs du thème. Évitez les refactors de passage qui déplacent le style desxvers les props raccourcies "style" deBox; cela entre en conflit avec les directives actuelles de MUI.
Frontières des composants : props vs état interne
- Dérivez les booléens des
dataà l'intérieur du composant lorsque le parent n'en a pas besoin pour la disposition ou l'état d'URL (par exemple "a des termes de marque" à partir debrand_termsde la marque principale). - Gardez les bascules d'interface utilisateur internes (par exemple passer d'un mode de graphique à un autre dans une seule carte) sauf si un autre frère ou une route doit piloter le même état—alors documentez pourquoi le remontée est nécessaire.
Flux de contrôle et JSX
- Préférez un
returnprécoce pour les branches de chargement/vide au lieu d'un grand ternaire assigné à une variable immédiatement avant un seulreturn, sauf si la mémorisation nécessite vraiment une expression unique. - Comparateurs de tri : ne codez jamais en dur un sentinelle magique (par exemple
5) lié à un maximum de domaine supposé ; utilisezNumber.POSITIVE_INFINITY/ un "trier en dernier" explicite pour les valeurs null/undefined afin que le comportement reste correct si les limites changent.
Requêtes et pagination
- Évitez un
limitfixe et petit (par exemple 100) pour les listes à l'échelle du tableau de bord lorsque le backend est paginé et que les projets peuvent dépasser une page. - Bouclez avec une taille de page stable jusqu'à ce qu'une page retourne moins de lignes que la limite ou zéro ligne, ou suivez le contrat documenté de l'action (voir la documentation de l'action
moz-pro-service/ Bruno pour lalimitmaximale). - Affichez
totalou des totaux dérivés de manière cohérente si les appelants ont besoin de comptes.