Explorer
KNOW-ANT-IA-003

Code — Anti-patterns fréquents : refacto, async, erreurs silencieuses, abstractions

Domaine
ia
Type
anti-pattern
Priorité
P1

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.