Pourquoi bien architecturer trop tôt est une erreur

Matthieu Werner
Temps de lecture : 5 min
symfony php webperf
Pourquoi bien architecturer trop tôt est une erreur
Pourquoi bien architecturer trop tôt est une erreur

Il y a un réflexe que l’on retrouve dans beaucoup de projets, et qui part généralement d’une bonne intention.

Dès le départ, on cherche à bien faire. On structure, on abstrait, on sépare, on anticipe. L’objectif est d’éviter les problèmes plus tard, de construire quelque chose de solide, et de ne pas avoir à tout refaire dans six mois.

Sur le papier, c’est difficile de critiquer cette approche. Une architecture propre, pensée dès le début, donne l’impression de sécuriser le projet, mais dans la pratique, c’est souvent l’inverse qui se produit.


Le point de départ

On démarre sur un projet assez classique. Une API backend, quelques endpoints, une logique métier encore limitée, et un volume qui reste raisonnable. Rien de particulièrement complexe à ce stade.

Et pourtant, très vite, une architecture complète se met en place. On retrouve un enchaînement de couches assez classique :

Controller → Handler → Service → Repository → DTO → Presenter → Serializer

Avec, en complément, des interfaces un peu partout, une injection de dépendances systématique, et une séparation stricte des responsabilités.

Tout est propre, structuré, cohérent. Mais déjà, quelque chose commence à se dégrader, même si ce n’est pas encore évident.


Le premier signal

Ce qui met généralement la puce à l’oreille, c’est le coût d’une opération simple.

Prenons un cas trivial : récupérer un utilisateur.

On se retrouve avec une interface :

interface UserRepository {
    public function findById(UserId $id): User;
}

Puis une implémentation :

final class UserRepositoryDoctrine implements UserRepository {
    // ...
}

Un handler :

final class GetUserHandler {
    public function __construct(
        private UserRepository $repo
    ) {}
}

Et enfin un controller :

final class GetUserController {
    public function __invoke(...) {}
}

Chaque élément est justifiable individuellement. Le problème, c’est que l’ensemble est déjà disproportionné par rapport au besoin.


Le coût caché

Ce type d’architecture a un coût immédiat, mais il est rarement perçu comme tel.

Il ne se voit pas dans les métriques techniques. Il se voit dans la vitesse d’exécution du projet.

Chaque évolution demande plus de fichiers, plus de navigation, plus de contexte mental. Ajouter une feature simple devient progressivement plus long qu’il ne devrait l’être, non pas à cause de la complexité métier, mais à cause de la structure elle-même.

On commence alors à passer plus de temps à maintenir l’architecture qu’à faire évoluer le produit.


Le faux argument de la scalabilité

La justification est presque toujours la même : on anticipe.

On veut être prêt pour le scale, éviter une refonte, construire quelque chose de propre dès le départ. L’intention est saine, mais elle repose souvent sur une hypothèse fragile.

Dans la réalité, plusieurs choses se produisent systématiquement : la majorité des projets ne scalent jamais autant que prévu, les besoins évoluent, et les abstractions initiales deviennent rapidement inadaptées.

On se retrouve donc avec une architecture conçue pour un futur qui n’arrive pas, ou qui arrive sous une forme différente.


Le problème de l’abstraction prématurée

Une abstraction a du sens lorsqu’elle permet de masquer une complexité réelle ou de gérer plusieurs implémentations.

Avant cela, elle ne fait qu’ajouter une couche supplémentaire.

Prenons un exemple classique :

interface PaymentProvider {
    public function charge(Money $amount): void;
}

Avec une seule implémentation :

final class StripePaymentProvider implements PaymentProvider {}

À ce stade, l’interface n’apporte aucune valeur. Elle ne protège rien, elle ne simplifie rien. Elle anticipe simplement un besoin hypothétique.

Et cette anticipation a un coût.


Le vrai impact

Ce coût devient réellement visible avec le temps.

Au bout de quelques mois, l’onboarding devient plus lent, la compréhension du code plus difficile, et le debug plus complexe. Certaines duplications apparaissent, mais elles sont masquées par les couches d’abstraction.

Paradoxalement, on peut même observer une dégradation des performances. Non pas à cause d’un problème fondamental, mais simplement parce que l’empilement de couches et d’indirections finit par avoir un impact réel.


Ce que montrent les projets qui durent

Avec un peu de recul, un pattern assez clair se dégage.

Les architectures qui tiennent dans la durée ne sont pas celles qui étaient les plus propres ou les plus complètes au départ. Ce sont celles qui ont évolué progressivement, en réponse à des contraintes concrètes.

Elles ne sont pas moins structurées. Elles sont simplement arrivées au bon moment.


Quand il faut vraiment architecturer

Il existe des signaux assez fiables qui indiquent qu’il est temps de structurer davantage.

La duplication devient réelle, la logique métier se complexifie, certaines contraintes techniques apparaissent, et le besoin de découplage devient concret.

À ce moment-là, l’architecture n’est plus une anticipation. Elle devient une réponse.


Une approche plus pragmatique

Sur les premières itérations, une approche plus directe est souvent plus efficace.

final class UserService {
    public function getById(string $id): array {
        return $this->connection->fetchAssociative(
            'SELECT * FROM users WHERE id = ?',
            [$id]
        );
    }
}

Ce n’est pas parfait au sens académique du terme, mais c’est lisible, rapide à faire évoluer, et largement suffisant à ce stade.

Puis, lorsque les contraintes apparaissent, on extrait, on factorise, on structure. Mais on le fait avec une raison concrète.


Le point d’équilibre

Le sujet n’est pas d’opposer code rapide et code propre, ni pragmatisme et architecture.

Le sujet est le timing.

Une architecture a un coût. Elle doit répondre à un besoin réel, et non à une projection.


Conclusion

Une bonne architecture n’est pas celle que l’on imagine au départ.

C’est celle qui émerge progressivement, lorsque les contraintes deviennent réelles et que les choix doivent être faits en connaissance de cause.

Avant cela, le choix le plus efficace reste souvent le plus simple — et rarement le plus abstrait.


TL;DR

  • L’architecture trop tôt ralentit plus qu’elle n’aide
  • Les abstractions sans besoin réel ajoutent de la complexité
  • Le coût principal est une perte de vitesse
  • Les bons designs émergent avec le temps
  • Le bon critère n’est pas “propre”, mais “nécessaire”

Auteur

Matthieu Werner

Partager cet article

Prêt à transformer vos idées ?

Découvrez comment La Programmerie peut donner vie à vos projets avec passion, expertise et une touche de magie technologique ✨

Email

Réponse garantie sous 24h

Téléphone

Disponible 9h-18h

Rendez-vous

Parlons de votre projet

Notes techniques

Recevez nos explorations par e-mail — articles et veille, sans spam.

Nous respectons votre vie privée et ne partageons jamais vos données.