Parent : [[INDEX-IA]]
Code — Anti-patterns fréquents : refacto, async, erreurs silencieuses, abstractions
❌ Refactorer et changer le comportement en même temps
Problème : Mélanger refacto et changement fonctionnel rend impossible l'identification de la source d'un bug introduit.
Solution : Règle stricte : un commit refacto = zéro changement de comportement observable. Un commit feature = zéro restructuration de code existant. Si les deux sont nécessaires : refacto d'abord, feature ensuite, deux commits séparés.
Exemple : Renommer + extraire une méthode + corriger un edge case dans le même commit → impossible à reverter proprement.
❌ Mutation d'état partagé en async
Problème : Un état mutable partagé entre coroutines ou threads produit des bugs non reproductibles qui explosent sous charge.
Solution : Toute variable modifiée dans un contexte async doit être locale ou protégée. En Python async : ne jamais modifier une liste/dict global dans un async def sans lock. En JS : méfiance sur les closures capturant des variables mutables dans des callbacks.
Exemple : results = [] au scope module, async def fetch(): results.append(x) — race condition garantie dès que deux requêtes arrivent simultanément.
❌ Gestion d'erreur qui cache l'erreur
Problème : Un try/except trop large ou un .catch(() => {}) vide transforme un bug explosif en bug silencieux indétectable.
Solution : Ne jamais catcher une exception sans soit la logger avec stacktrace complète, soit la re-throw. Un catch vide est pire qu'aucun catch. En prod, toute exception catchée doit déclencher au minimum un log structuré avec contexte.
Exemple : try { ... } catch(e) {} en JS — l'erreur disparaît, le state est corrompu, l'utilisateur voit un écran vide sans raison apparente.
❌ Abstraire trop tôt — la généralisation prématurée
Problème : Créer une abstraction avant d'avoir 3 cas d'usage réels produit une abstraction qui s'ajuste mal au 4ème cas.
Solution : Règle des 3 : dupliquer jusqu'à 2 occurrences, abstraire à la 3ème. Et seulement si les 3 cas sont suffisamment similaires. La duplication est moins chère que la mauvaise abstraction.
Exemple : Créer BaseRepository<T> après une seule entité → au 3ème modèle, les cas limites (soft delete, audit log, multi-tenant) explosent l'abstraction.
❌ Dépendance sur l'ordre d'exécution non garanti
Problème : Du code qui fonctionne en dev parce que l'ordre d'exécution est accidentellement correct explose en prod sous une charge différente.
Solution : Rendre les dépendances d'initialisation explicites dans le code, pas dans la documentation. Tout module qui a besoin qu'un autre soit initialisé doit le vérifier ou le déclencher lui-même.
Exemple : Service A suppose que la connexion DB est initialisée par le main(). Fonctionne. Puis un worker instancie A directement → crash non reproductible en dev.
Pattern — Refactorer sans casser : strangler fig
Problème : Remplacer une implémentation critique en une fois garantit une régression en prod non anticipée.
Solution : Faire coexister l'ancien et le nouveau code, router 1% du trafic vers le nouveau, valider, augmenter progressivement, supprimer l'ancien quand à 100%. Ne jamais supprimer l'ancien avant validation complète en prod.
Exemple : if (USE_NEW_PRICING_ENGINE) newCalc() else oldCalc() — feature flag, déploiement progressif, rollback en 1 ligne.
Pattern — Écrire du code qu'une IA comprend bien : nommage intentionnel
Problème : Du code avec des noms génériques (data, temp, obj, x) produit des suggestions IA incorrectes faute de contexte sémantique.
Solution : Nommer chaque variable selon son rôle métier exact, pas son type. userInvoiceLineItems > items. filteredActiveSubscriptions > subs. Ajouter des commentaires sur les invariants non-évidents.
Exemple : const d = u.filter(x => x.s === 1) vs const activeUsers = users.filter(u => u.status === ACTIVE) — la seconde génère des suggestions 10x plus précises.
Pattern — Écrire du code qu'une IA comprend bien : isoler les effets de bord
Problème : Du code qui mélange calcul pur et effets de bord force l'IA à raisonner sur deux problèmes simultanément.
Solution : Séparer systématiquement : fonctions pures sans effets de bord d'un côté, orchestrateurs avec effets de l'autre. Les fonctions pures sont testables, prévisibles, et compréhensibles sans contexte additionnel.
Exemple : calculateDiscount(price, rules) retourne un nombre, ne touche pas à la DB. applyDiscountToOrder(orderId) appelle la DB et calculateDiscount. Deux fonctions, deux responsabilités.