Refactoriser une structure de code désordonnée sans casser la production
Refactorisez une codebase désordonnée avec un plan pratique pour les frontières de dossiers, le nommage, l'extraction de modules et des PRs petites qui maintiennent le comportement stable.

Ce que signifie vraiment une structure de code désordonnée
Une structure de code désordonnée n'est pas juste « beaucoup de fichiers. » C'est quand l'organisation ne reflète plus le fonctionnement du produit. Vous ne savez plus où doit aller une modification, donc chaque changement paraît risqué.
Dans les vrais dépôts, cela se manifeste souvent par un mélange de responsabilités : du code UI qui parle directement à la base de données, des vérifications d'auth dispersées dans les pages, et des dossiers « utils » qui contiennent en réalité une grande partie de la logique métier. Un autre symptôme courant est l'import circulaire (A importe B, B importe A). Ils peuvent créer des comportements d'exécution étranges et pousser les gens vers des rustines juste pour démarrer l'application.
Le nommage est le problème discret. Vous verrez trois façons de nommer la même chose (userService, users.service, user_manager), ou des dossiers qui ne signifient rien (misc, temp, old2). Les gens dupliquent du code parce qu'ils ne trouvent pas l'endroit approprié, et le désordre s'aggrave.
Vous ressentez les problèmes de structure au quotidien plus que dans les diagrammes d'architecture :
- De petits changements prennent des heures car vous poursuivez des dépendances à travers le dépôt.
- Les bugs se répètent parce que les corrections atterrissent à un endroit alors que le vrai comportement se trouve ailleurs.
- Les releases font peur parce qu'une petite édition peut casser quelque chose d'apparemment sans rapport.
Un nettoyage en vaut la peine quand la structure ralentit principalement votre équipe ou provoque des bugs évitables. Si le produit est stable, les changements rares, et l'équipe comprend la disposition actuelle, un gros refactor peut attendre. Souvent, la meilleure approche est « touch it, tidy it » : améliorez la structure uniquement dans la zone où vous apportez déjà une modification pour une fonctionnalité ou un bug.
« Ne pas tout casser » signifie que le comportement reste identique pendant que la structure change. Les utilisateurs ne doivent rien remarquer. Le bénéfice est des changements plus sûrs : diffs plus petits, frontières plus claires et moins de surprises lors des tests et des releases.
Définir des objectifs et des garde-fous avant de toucher au code
Les refactors avancent plus vite quand vous pouvez répondre à une question : qu'est-ce qu'on essaie d'améliorer ? Choisissez une cible principale, comme accélérer les changements, réduire les bugs ou faciliter l'intégration des nouveaux. Si vous essayez de tout réparer en même temps, vous passerez une semaine à déplacer des fichiers sans rendre le système plus simple à maintenir.
Ensuite, écrivez ce qui doit rester stable. La stabilité n'est pas seulement « les tests passent. » C'est le vrai contrat que votre app a avec les utilisateurs et les autres systèmes.
Mettez d'accord les garde-fous avant le premier commit :
- Non négociables : APIs publiques, schéma de la base de données, routes et flux UI clés qui ne doivent pas changer.
- Périmètre : ce qui est dans le périmètre et ce qui est hors-limite (pour l'instant).
- Limite temporelle : une durée claire (par exemple 2–3 jours) avant réévaluation.
- Preuves : quelles vérifications doivent être au vert (tests, lint, build, plus un court smoke test).
- Fini signifie : « ces dossiers sont nettoyés et documentés, » pas « le dépôt entier est parfait. »
Exemple : vous héritez d'une app générée par IA où l'auth marche « la plupart du temps, » les routes sont dupliquées et les fichiers vivent n'importe où. Vos garde-fous pourraient être : ne pas changer le flux de connexion, les cookies de session ou les tables de la base cette semaine. L'objectif est seulement de regrouper l'auth derrière un module et de déplacer les fichiers liés dans un dossier unique avec des noms cohérents. Vous aurez ainsi une victoire livrable sans introduire un nouveau bug mystérieux.
Si vous travaillez avec d'autres, mettez les garde-fous par écrit et obtenez un accord rapide. Cela évite les débats en cours de refactor et garde les revues concentrées sur les résultats plutôt que sur les opinions.
Carte rapide de la structure actuelle (30 à 60 minutes)
Avant de déplacer des dossiers, passez une heure concentrée à faire une carte approximative. Cela réduit le risque car vous cessez de deviner où le comportement commence et où il se propage.
Commencez par les points d'entrée : où le système se réveille et commence à faire le travail. De nombreuses apps ont plus d'un point d'entrée : serveur web, worker programmé, consommateur de queue, script CLI et parfois un runner de migrations.
Puis trouvez la douleur : les fichiers et dossiers que l'on touche constamment. Un fort taux de changement signifie souvent que ces fichiers font trop de choses ou que tout dépend d'eux.
Gardez une carte simple pendant que vous travaillez :
- 3 à 6 points d'entrée (ce qui s'exécute d'abord et comment ça démarre)
- 5 à 10 fichiers ou dossiers à fort churn (où les changements atterrissent sans cesse)
- 3 à 5 points chauds de dépendances (importés par beaucoup d'autres modules)
- Une phrase pour chaque dossier top-level décrivant son but (même si c'est moche)
Un point chaud de dépendance est là où les refactors meurent. Un indice rapide est un fichier « utils » unique qui contient des helpers d'auth, du formatage de dates, du code de base de données et des appels API. S'il est importé partout, chaque changement devient risqué.
Gardez la carte honnête, pas flatteuse. « src/helpers : bazar où on a mis tout ce qu'on ne savait pas où ranger » est utile. Plus tard, vous pourrez transformer cette phrase en plan.
Définir des frontières de dossiers qui réduisent le couplage
Si vous voulez réorganiser sans casser la production, commencez par tracer des frontières claires. L'objectif est simple : un changement dans une zone ne devrait pas obliger à éditer tout le dépôt.
Choisissez un modèle de dossiers que vous pouvez expliquer en une phrase
Choisissez le modèle le plus simple qui correspond à la façon de penser de votre équipe :
- Par fonctionnalité : tout pour « billing » vit ensemble (UI, logique, données).
- Par couche :
ui/,domain/,data/sont séparés et les fonctionnalités se trouvent à l'intérieur. - Hybride : dossiers par fonctionnalité en haut, avec
ui/,domain/,data/à l'intérieur de chaque fonctionnalité.
Chacun de ces modèles peut fonctionner. Ce qui compte, c'est que « où va ce fichier ? » ait une réponse évidente.
Écrivez des règles simples pour ce qui appartient où
Définissez les frontières en langage courant. Par exemple :
- UI : composants, écrans, formulaires et logique d'affichage.
- Domain : règles métier et décisions (calcul de prix, vérifications d'éligibilité).
- Accès aux données : clients API, requêtes DB et persistance.
Ajoutez ensuite une courte liste « ne pas traverser » pour éviter le couplage accidentel pendant le refactor :
- L'UI n'importe pas directement l'accès aux données.
- Le domaine n'importe pas l'UI.
- Aucun appel DB direct en dehors de la couche data access.
- Pas de lecture des secrets d'env en dehors d'un seul module de config.
- Aucun dossier fonctionnalité n'accède aux internals d'une autre fonctionnalité.
Un scénario simple : si un écran de checkout a besoin du total d'une commande, il doit appeler une fonction du domaine (comme calculateTotal). Cette fonction peut appeler l'accès aux données via une petite interface, pas via du SQL brut ou un client API direct.
Enfin, décidez de la propriété. Nommez un réviseur (ou un petit groupe) pour chaque zone afin que les ruptures de frontières soient détectées tôt.
Conventions de nommage et petites règles qui maintiennent l'ordre
Les refactors échouent souvent pour une raison ennuyeuse : les gens ne savent pas où placer le prochain fichier, donc le désordre recommence. Les conventions de nommage semblent pointilleuses, mais elles éliminent les décisions quotidiennes et empêchent les exceptions « juste cette fois ».
Choisissez des conventions que votre équipe suivra réellement. Si une règle nécessite un débat à chaque fois, elle est trop stricte ou trop astucieuse. L'objectif est la cohérence, pas la perfection.
Quelques bases à noter :
- Noms de fichiers et dossiers : choisissez kebab-case (
user-profile.ts) ou camelCase (userProfile.ts) et tenez-vous-y. - Singulier vs pluriel : par exemple, utilisez le singulier pour les dossiers de module (
invoice/) et le pluriel seulement pour les collections (invoices/). - Exports : favorisez les exports nommés pour le code partagé ; évitez les exports par défaut sauf si une raison claire existe.
- Fichiers index : soit bannissez-les, soit limitez-les à la ré-exportation de l'API publique, pour que les imports restent prévisibles.
- Un concept par fichier : si un fichier dépasse un écran ou deux, scindez-le par responsabilité.
Un petit dossier « golden example » vaut mieux qu'un long document. Gardez-le minuscule et proche de ce que vous construisez le plus souvent :
features/
auth/
api.ts
routes.ts
components/
LoginForm.tsx
index.ts
Quand quelqu'un ajoute un nouvel écran d'auth, il peut copier le patron sans deviner.
Une petite règle légère pour le nouveau code aide le refactor à tenir même si le nettoyage n'est pas terminé :
- Les nouveaux fichiers suivent les nouvelles règles de nommage et vivent dans les nouveaux dossiers.
- « Touch it, tidy it » : si vous modifiez un fichier, déplacez-le au bon endroit ou corrigez le nom.
- Pas de nouveaux tiroirs
misc.
Si cela paraît difficile à appliquer, c'est souvent un signe que la structure ne correspond pas encore au fonctionnement réel de l'app.
Pas à pas : extraire un module en toute sécurité
Choisissez d'abord une cible petite et peu risquée. Un bon départ est une utilité partagée que beaucoup de fichiers importent (formatage de date, feature flags, validation d'entrée) ou une fonctionnalité unique assez autonome. Si vous tentez d'extraire un domaine central dès le premier jour, vous passerez votre temps à poursuivre des surprises.
Un refactor sûr change la forme du code sans en altérer le comportement. L'astuce consiste à créer une frontière, puis à déplacer le code derrière elle petit à petit.
Séquence sûre d'extraction
Commencez par écrire en une phrase la promesse du nouveau module : ce qu'il fait et ce qu'il ne fera pas. Puis :
- Geler le comportement derrière une interface. Une fonction ou classe exportée suffit. Gardez l'interne moche pour l'instant ; l'extérieur doit rester simple.
- Bougez par petits commits. Mettez à jour les imports au fur et à mesure. Si vous devez renommer des choses, faites-le après le déplacement pour garder les diffs lisibles.
- Vérifiez après chaque déplacement. Lancez l'app et les tests. Si vous n'avez pas de tests, faites une vérification manuelle rapide du flux qui l'utilise.
- Supprimez l'ancien code en dernier. Seulement lorsque vous pouvez prouver que rien n'en dépend (recherchez les imports, vérifiez les logs runtime, confirmez qu'il n'existe pas de copies dupliquées).
- Ajoutez quelques tests ciblés à la frontière. Un happy path, un cas limite, un cas d'échec suffisent généralement.
Exemple : vous trouvez trois fichiers qui répondent « l'utilisateur est-il connecté ? » de façon légèrement différente. Créez un module authSession avec getSession() et requireUser(). D'abord, que ces fonctions appellent l'ancien code. Ensuite, déplacez la logique dans le module, mettez à jour les appelants un par un et ajoutez 2 ou 3 tests qui verrouillent les résultats attendus.
L'extraction met aussi en lumière le couplage caché : globals, responsabilités mêlées, valeurs secrètes dans des fichiers aléatoires et helpers « temporaires » devenus permanents.
Comment livrer le refactor en utilisant des PRs incrémentales
Ce travail est le plus sûr lorsqu'il est traité comme une série de petites livraisons, pas comme une réécriture unique. Les PRs incrémentales gardent le rayon d'impact réduit. Quand quelque chose casse, vous le repérez vite et vous pouvez revenir en arrière sans panique.
Gardez les PRs petites et ennuyeuses
Visez un type de changement par PR : déplacer un dossier, renommer un groupe de fichiers ou extraire un module. Si vous avez envie de « corriger 2–3 trucs pendant que j'y suis, » notez-les et faites-les plus tard.
Faites d'abord les changements mécaniques : déplacements, renommages, formatage, mises à jour des chemins d'import. Ils sont faciles à relire parce que le comportement reste inchangé.
Une fois la structure stabilisée, faites les changements de comportement dans des PRs séparées (corrections de logique, changements de forme de données, gestion d'erreurs). Mélanger structure et comportement est la façon dont les refactors deviennent des bugs mystérieux.
Rédigez des descriptions de PR qui aident les relecteurs à vérifier
Facilitez la revue en incluant :
- Ce qui a changé (une phrase)
- Le risque (ce qui pourrait casser et où)
- Comment vérifier (un court test manuel plus les contrôles automatiques clés)
- Plan de rollback (généralement « revert this PR »)
Exemple : « Déplacé le code lié à l'auth dans modules/auth et mis à jour les imports. Risque : le routage de connexion. Vérifier : s'inscrire, se connecter, se déconnecter, rafraîchir la session. »
Si plusieurs personnes travaillent, mettez-vous d'accord sur un ordre qui évite les conflits. Fusionnez d'abord les PRs de frontière, puis les extractions qui s'appuient sur la nouvelle organisation. Assignez des propriétaires pour éviter que deux personnes modifient les mêmes fichiers à fort churn en même temps.
Garder le comportement stable pendant que la structure change
L'objectif est ennuyeux : l'app doit se comporter de la même façon pour les utilisateurs. La plupart des refactors échouent parce que de petits changements de comportement s'insèrent sans être remarqués.
Commencez par des checks smoke qui reflètent l'usage réel du produit. Gardez la liste courte pour que vous l'exécutiez à chaque fois :
- Inscription, connexion, déconnexion (et réinitialisation de mot de passe)
- Créer l'objet principal de votre app (commande, post, projet, ticket)
- Mettre à jour et supprimer cet objet
- Une action monétaire (checkout, facture, changement d'abonnement), si vous en avez
- Une action de message (email, notification, webhook), si vous en avez
Les tests sont des déclencheurs, pas un projet de perfection. Si la couverture est faible, écrivez 2 à 5 tests à forte valeur autour des flux ci-dessus, puis arrêtez. Quelques tests end-to-end ou d'intégration valent souvent mieux que des dizaines de petits tests unitaires pour attraper les erreurs de refactor.
Surveillez les échecs silencieux. L'auth peut se casser sans erreur évidente (cookies, stockage de session, chemins de redirection). Les paiements et les emails peuvent « échouer avec succès » si les callbacks ne sont pas branchés ou si les jobs en arrière-plan ne tournent pas. Les queues peuvent perdre des tâches si les noms de jobs ou les chemins d'import changent pendant l'extraction.
Rendez les échecs visibles rapidement. Assurez-vous de pouvoir voir les logs et erreurs au même endroit, et que les erreurs incluent assez de contexte (ID utilisateur, ID de requête, nom du job). Pendant le refactor, ajoutez quelques lignes de log ciblées autour des frontières critiques (auth, facturation, emails sortants), puis supprimez-les une fois la situation stabilisée.
Pièges courants qui font dérailler les refactors
La plupart des refactors échouent quand de petits changements s'accumulent jusqu'à ce que personne ne sache ce qui est sûr. Surveillez ces pièges tôt.
Le piège « ça build encore »
Déplacer des fichiers peut sembler anodin, surtout quand les tests sont verts. Mais si vous déplacez des choses sans décider de l'interface publique, vous finirez par casser des imports à travers l'app, ou forcer tout le monde à « juste mettre à jour le chemin » dans des dizaines d'endroits.
Un pattern plus sûr est de garder des points d'entrée stables (par exemple, un fichier index par module qui définit l'API publique) et de changer les chemins internes derrière cette interface.
Évitez aussi de créer un nouveau tiroir misc comme solution rapide. Un nouveau misc devient le même problème en une semaine. Si quelque chose n'a pas de maison claire, traitez cela comme un signal que vos frontières sont floues.
Les portes dérobées cachées entre modules
L'extraction n'est pas terminée quand les fichiers sont déplacés. Elle est terminée quand le module peut se tenir seul.
Les portes dérobées habituelles sont les cross-imports (le module A importe silencieusement le module B) et les globals partagés (objets de config, singletons, caches mutables) qui permettent au code de traverser les frontières.
Pièges qui nuisent à la qualité des revues :
- Renommer beaucoup de fichiers et symboles en même temps que déplacer des dossiers
- Mélanger travail de refactor et nouvelles fonctionnalités
- Laisser en place l'ancienne et la nouvelle API « temporairement » et ne jamais supprimer l'ancien chemin
- Faire une PR géante que les relecteurs ne peuvent que survoler
- Changer accidentellement le comportement (par ex. déplacer du code d'auth et altérer l'ordre des middlewares)
Exemple : une équipe extrait un module auth, mais quelques écrans continuent d'importer un global currentUser depuis l'ancien dossier. Tout semble OK jusqu'en production, où un cold start charge les modules dans un autre ordre.
Checklist rapide pour chaque PR de refactor
Les petites PRs sont la façon de réorganiser la structure sans transformer la production en jeu de pistes.
Avant d'ouvrir la PR
Si vous ne pouvez pas expliquer le changement en deux phrases, la PR est probablement trop grosse.
- Limitez le périmètre à une correction de frontière, un ensemble de renommages ou un déplacement de module.
- Listez les points d'entrée que vous pourriez affecter (routes, commandes CLI, jobs background, imports depuis d'autres packages).
- Notez un plan de rollback (généralement « revert this PR » ; parfois une exportation de compatibilité pour une release).
- Lancez lint et tests localement et notez 2 à 3 smoke checks.
- Confirmez qu'il n'y a pas de changement de comportement sauf si le titre de la PR l'indique.
Exemple concret : si vous déplacez des helpers d'auth dans un nouveau module, indiquez la route de login, le refresh de token et les imports de middleware comme points d'entrée. Smoke checks : s'inscrire, se connecter, se déconnecter, rafraîchir le token, charger une page protégée.
Après avoir ouvert et fusionné la PR
Faites un rapide passage de sanity avant la tranche suivante :
- Vérifiez que vous n'avez pas introduit d'import circulaire (un build et une vérification des dépendances le révéleront généralement).
- Supprimez les dossiers temporaires créés pendant le déplacement, ou renommez-les en leur domicile final.
- Assurez-vous que l'équipe est d'accord sur l'endroit où vont désormais les nouveaux fichiers (un court commentaire dans la PR suffit souvent).
- Mettez à jour les notes simples : un petit README dans le dossier, ou un bref commentaire expliquant le but du dossier.
- Confirmez que la PR n'a pas modifié silencieusement des APIs publiques (exports, chemins de fichiers que d'autres importent).
Étapes suivantes : garder l'élan (et savoir quand demander de l'aide)
Choisissez une petite vraie fonctionnalité et utilisez-la comme chemin de preuve. Si vous partez d'un prototype généré par IA, une tranche de départ habituelle est : extraction des vérifications d'auth hors des composants UI, regroupement de l'accès aux données dans un module, et réduction de l'UI au rendu.
Refactorez d'abord une tranche verticale, et reportez le reste. Par exemple : « Sign in -> load account -> show dashboard » et rendez-la banale et prévisible. Déplacez les vérifications d'auth en un seul endroit, regroupez l'accès aux données dans un module unique et gardez l'UI concentrée sur l'affichage. Réservez les gros débats (renommages complets de dossiers, changements de framework, modélisation parfaite du domaine) pour plus tard, quand vous aurez de l'élan et un filet de sécurité.
Lorsque vous expliquez le plan aux parties prenantes non techniques, commencez par ce qui reste stable : écrans, logique de tarification, données clients. Puis expliquez ce qui change en interne : moins de surprises en production, responsabilité plus claire et gestion des secrets plus sûre.
Il est souvent plus rapide de demander de l'aide quand le code est profondément en spaghetti (tout s'importe mutuellement), quand vous voyez des drapeaux de sécurité (clés exposées, requêtes dangereuses) ou quand l'auth est déjà peu fiable.
Si votre codebase vient d'outils comme Lovable, Bolt, v0, Cursor ou Replit et que vous passez plus de temps à démêler qu'à construire, FixMyMess (fixmymess.ai) peut réaliser un audit gratuit du code pour identifier les zones les plus risquées et l'ordre le plus sûr pour les corriger avant que vous ne commenciez à tout déplacer.
Questions Fréquentes
How do I know if my codebase structure is actually “messy” or just big?
Une structure est « messy » quand vous ne savez plus où placer une modification et que chaque édition paraît risquée. Signes fréquents : responsabilités mélangées (l'UI accédant directement à la base de données), logique métier cachée dans des dossiers « utils », noms incohérents pour la même chose, et imports circulaires provoquant des comportements d'exécution étranges.
When is a structural refactor worth doing, and when should I wait?
Refactorez quand la structure ralentit clairement la livraison ou cause des bugs récurrents, pas seulement parce que c'est désordonné. Si le produit est stable, que les changements sont rares et que l'équipe peut travailler en sécurité, un gros nettoyage peut attendre. Préférez souvent la stratégie « touch it, tidy it » : améliorez la zone que vous modifiez déjà.
What guardrails should I set before I start moving files around?
Choisissez un objectif principal (par ex. : rendre les changements d'auth prévisibles) et notez les éléments non négociables : routes, schéma de la base, et flux utilisateurs critiques. Ajoutez une limite de temps et une définition claire de « done » pour éviter de déplacer des fichiers indéfiniment.
What’s the fastest way to understand a repo before refactoring it?
Passez 30–60 minutes à cartographier les points d'entrée et les zones chaudes avant de toucher la structure. Identifiez où l'application démarre, quels fichiers changent souvent, et quels modules sont importés partout, puis écrivez une phrase honnête sur l'objectif de chaque dossier de premier niveau.
How do I choose folder boundaries that don’t turn into arguments?
Commencez par une règle simple et appliquable, par exemple « l'UI n'appelle pas la base de données » et « les secrets proviennent d'un seul module de config ». Choisissez ensuite un modèle de dossiers (par fonctionnalité, par couche, ou hybride) qui correspond à la façon dont votre équipe parle du produit.
What naming conventions actually prevent the mess from returning?
Rendez les conventions banales et faciles à suivre pour éviter les décisions quotidiennes. Choisissez un style de nommage, décidez de l'usage des fichiers index, et conservez un petit dossier « golden example » montrant le modèle à reproduire.
What’s a safe way to extract a module without breaking production?
Extraites de façon sûre, les fonctions d'un module restent derrière une interface claire. Créez d'abord l'API publique (une fonction ou une classe exportée), faites que l'ancienne logique soit appelée depuis cette interface, puis déplacez les appels un par un. Vérifiez après chaque déplacement et ne supprimez l'ancien code qu'une fois sûr qu'il n'est plus utilisé.
How small should refactor PRs be, and what should I avoid mixing in?
Gardez chaque PR concentrée sur un type de changement et évitez de mélanger refactorings structurels et modifications de comportement. Si une PR mélange mouvements de fichiers et changements logiques, les relecteurs ne pourront pas évaluer le risque et vous augmenterez les chances d'introduire un bug mystérieux.
What if I don’t have good tests—how do I keep behavior stable?
Définissez une courte suite de vérifications manuelles qui reflètent les usages réels (smoke tests) et exécutez-les à chaque changement structurel. Si les tests sont faibles, ajoutez 2 à 5 contrôles à forte valeur autour des frontières critiques (auth, facturation, emails) pour que les erreurs de refactor soient visibles rapidement.
When should I get help instead of trying to refactor an AI-generated codebase myself?
Demandez de l'aide si tout s'importe mutuellement, si l'auth est déjà instable, si des secrets sont exposés ou si des requêtes non sécurisées apparaissent : ce sont des signes que vous perdrez du temps à démêler seul. FixMyMess (fixmymess.ai) propose un audit gratuit des codebases générés par IA pour identifier les zones les plus risquées et l'ordre le plus sûr pour les corriger.