7 erreurs techniques qui plombent les projets web

Ces erreurs sont courantes, souvent invisibles au début, et coûteuses à corriger plus tard. Voici comment les identifier et les éviter dans vos projets.

#1

Pas de gestion d'environnements

Symptômes

  • Configuration en dur dans le code
  • "Ça marche sur ma machine mais pas en prod"
  • Credentials commités dans Git

Pourquoi c'est un problème

Mélanger configuration et code rend le déploiement hasardeux. Chaque environnement (dev, staging, prod) a ses propres paramètres : base de données, clés API, URLs. Sans séparation claire, vous risquez d'envoyer des emails de test en production ou de corrompre des données.

Solution

Utilisez des variables d'environnement. Tous les frameworks modernes supportent les fichiers .env :

# .env.example (commite) DATABASE_URL= STRIPE_KEY= MAIL_HOST= # .env (ignoré par git) DATABASE_URL=mysql://user:pass@localhost/myapp STRIPE_KEY=sk_test_xxx MAIL_HOST=smtp.mailtrap.io

Ajoutez .env à votre .gitignore et fournissez un .env.example avec les clés sans valeurs.

#2

Absence de tests automatisés

Symptômes

  • Peur de toucher au code existant
  • Bugs qui reviennent après correction
  • Refactoring impossible sans casser des fonctionnalités

Pourquoi c'est un problème

Sans tests, chaque modification est un pari. Vous ne pouvez pas savoir si votre changement a cassé autre chose. Le code devient "fragile" et l'équipe finit par éviter de le toucher, ce qui accélère la dette technique.

Solution

Commencez petit. Vous n'avez pas besoin de 100% de couverture. Testez d'abord :

  • Les chemins critiques : authentification, paiement, création de compte
  • La logique métier complexe : calculs de prix, permissions, workflows
  • Les bugs corrigés : chaque bug fixé mérite un test de non-régression
// Test Laravel basique public function test_user_can_checkout() { $user = User::factory()->create(); $product = Product::factory()->create(['price' => 1000]); $response = $this->actingAs($user) ->post('/checkout', ['product_id' => $product->id]); $response->assertRedirect('/confirmation'); $this->assertDatabaseHas('orders', [ 'user_id' => $user->id, 'total' => 1000 ]); }
#3

Requêtes N+1

Symptômes

  • Pages qui ralentissent avec le volume de données
  • Centaines de requêtes SQL pour une seule page
  • Timeout sur les listings

Pourquoi c'est un problème

Le problème N+1 survient quand vous chargez une liste d'objets (1 requête) puis accédez à une relation pour chaque élément (N requêtes). 100 produits avec leurs catégories = 101 requêtes au lieu de 2.

// Mauvais : N+1 requêtes $posts = Post::all(); // 1 requête foreach ($posts as $post) { echo $post->author->name; // +1 requête par post } // Bon : 2 requêtes $posts = Post::with('author')->get(); foreach ($posts as $post) { echo $post->author->name; // déjà chargé }

Solution

  • Utilisez l'eager loading (with() en Laravel, select_related() en Django)
  • Activez un outil de debug SQL (Laravel Debugbar, Django Debug Toolbar)
  • En Laravel, activez Model::preventLazyLoading() en dev pour détecter les N+1
#4

Pas de validation côté serveur

Symptômes

  • Données incohérentes en base
  • Erreurs SQL cryptiques pour les utilisateurs
  • Failles de sécurité (injection, XSS)

Pourquoi c'est un problème

La validation JavaScript côté client est facilement contournable. Curl, Postman, ou simplement la console du navigateur permettent d'envoyer n'importe quoi. La validation serveur est la seule garantie.

// Laravel : Form Request class StoreOrderRequest extends FormRequest { public function rules() { return [ 'email' => 'required|email|max:255', 'quantity' => 'required|integer|min:1|max:100', 'product_id' => 'required|exists:products,id', ]; } }

Solution

  • Validez TOUJOURS côté serveur, la validation JS est juste pour l'UX
  • Utilisez les Form Requests (Laravel) ou les Forms (Django)
  • Vérifiez les permissions : l'utilisateur a-t-il le droit d'accéder à cette ressource ?
#5

Logique métier dans les controllers

Symptômes

  • Controllers de 500+ lignes
  • Duplication de code entre controllers
  • Impossible de réutiliser la logique (API, commandes CLI, jobs)

Pourquoi c'est un problème

Le controller devrait juste orchestrer : recevoir la requête, appeler la logique métier, retourner la réponse. Quand la logique est dans le controller, elle devient impossible à tester unitairement et à réutiliser.

// Mauvais : logique dans le controller public function store(Request $request) { $order = new Order(); $order->user_id = auth()->id(); $order->total = 0; foreach ($request->items as $item) { $product = Product::find($item['id']); $order->total += $product->price * $item['quantity']; // ... 50 lignes de plus } // envoi email, notification, etc. } // Bon : delegation a un service public function store(Request $request, OrderService $orderService) { $order = $orderService->createFromCart( auth()->user(), $request->validated() ); return redirect()->route('orders.show', $order); }

Solution

  • Extrayez la logique dans des Services ou Actions
  • Le controller ne devrait pas dépasser 50-100 lignes
  • Règle simple : si vous ne pouvez pas décrire ce que fait le controller en une phrase, il fait trop de choses
#6

Pas de stratégie de déploiement

Symptômes

  • Déploiement manuel via FTP
  • "On déploie que le vendredi matin"
  • Rollback = restaurer une sauvegarde

Pourquoi c'est un problème

Un déploiement manuel est source d'erreurs : fichier oublié, mauvais serveur, configuration incorrecte. Plus c'est pénible, moins on déploie souvent, plus les releases sont grosses et risquées.

Solution

Automatisez. Même un script bash basique vaut mieux que du manuel :

# Script de deploiement minimal git pull origin main composer install --no-dev --optimize-autoloader php artisan migrate --force php artisan config:cache php artisan route:cache php artisan queue:restart

Mieux : utilisez un outil de CI/CD (GitHub Actions, GitLab CI) ou une plateforme (Laravel Forge, Ploi, Render).

#7

Ignorer les logs et le monitoring

Symptômes

  • Découvrir les bugs par les utilisateurs
  • Pas de visibilité sur les erreurs en production
  • Impossible de debugger les problèmes intermittents

Pourquoi c'est un problème

En production, vous n'avez pas accès au debugger. Les logs sont votre seule fenêtre sur ce qui se passe. Sans monitoring, les erreurs s'accumulent silencieusement jusqu'à ce qu'un utilisateur vous contacte... ou parte.

Solution

  • Centralisez les logs : Sentry, Bugsnag, ou même un simple fichier structuré
  • Loggez avec contexte : user_id, request_id, données pertinentes
  • Alertez sur les erreurs critiques : Slack, email, SMS pour les erreurs 500
  • Monitorez les métriques : temps de réponse, taux d'erreur, queue length
// Bon : log avec contexte Log::error('Payment failed', [ 'user_id' => $user->id, 'order_id' => $order->id, 'amount' => $amount, 'error' => $exception->getMessage(), ]);

Checklist de santé projet

Avant de partir en production

  • Variables d'environnement séparées du code
  • Credentials hors du repo Git
  • Tests sur les fonctionnalités critiques
  • Validation serveur sur tous les formulaires
  • Eager loading sur les listings
  • Script de déploiement automatisé
  • Monitoring des erreurs configuré
  • Backups automatiques de la base
  • HTTPS activé
  • Headers de sécurité configurés

Ces erreurs ne sont pas des fautes. Elles arrivent quand on va vite, qu'on manque d'expérience, ou qu'on hérite d'un projet existant. L'important est de les identifier et de les corriger progressivement. Un projet parfait n'existe pas, un projet qui s'améliore, si.

Pour aller plus loin

Besoin d'aide sur votre projet ?

Je peux vous accompagner dans le développement ou l'optimisation de votre application.

Me contacter