La lutte contre les facteurs de coût est éternelle. En matière de développement, elle consiste d’abord à réduire les temps de développement. Pour ce faire, on peut améliorer l’efficacité et la productivité du travail, ou bien encore, comme les temps de développement sont chiffrés à la journée, augmenter le temps de travail d’une journée, au risque d’aboutir à des charges irréalistes.

Une voie trop rarement empruntée consiste simplement à agir à la source, en évitant de demander des fonctionnalités, ou d’intégrer des exigences, dont on n’aura pas besoin. Une idée simple, et pourtant…

Lutter contre les gaspillages et la complexité

Bien souvent, les développements réalisés exclusivement à partir de spécifications génèrent de nombreux gaspillages :

  • des fonctionnalités non réclamées,
  • des architectures sur-optimisées pour de la performance inutile,
  • des méta-modèles génériques surdimensionnés, et utilisés, au final, à 25 % de leurs possibilités.

L’intégration de fonctionnalités inutiles ou d’exigences injustifiées génère inévitablement des risques de dépassement de délais.

Au-delà, certains choix fonctionnels ou architecturaux peuvent induire une augmentation significative de complexité, et augmentent les coûts de manière exponentielle. Et pas uniquement sur le développement initial, mais aussi sur la maintenance, voire l’exploitation.

Jens Coldewey, relayé par Martin Fowler, désigne ces phénomènes sous le nom de complexity boosters, ou facteurs de complexité. Il en fait d’ailleurs une liste assez précise :

  • la distribution,
  • le multithreading,
  • le décalage entre deux paradigmes ou représentations, par exemple, le relationnel et l’objet, ou bien encore, XML et l’objet,
  • le développement multi-plateforme,
  • et la performance.

À cette liste, nous rajouterons la généricité.

La distribution

« Sell your favourite grandma first if you possibly can ». Cette phrase drôle et provocante de Martin Fowler résume bien l’état d’esprit dans lequel la distribution doit être envisagée, c’est-à-dire en dernier recours.

Pourtant, on a souvent l’habitude d’appréhender la distribution comme une solution d’amélioration des performances. Cette idée est généralement fausse. La distribution a bien un impact, largement sous-estimé, sur les performances. Ceci est d’ailleurs dû aux mécanismes mis en œuvre dans la distribution :

  • d’abord, la communication inter-processus, de nature extrêmement coûteuse pour les performances,
  • l’infrastructure réseau, si les deux interlocuteurs sont distants,
  • la sérialisation, ou marshalling,

Ces aspects de performance ont des incidences très fortes sur l’architecture applicative, et le niveau de granularité des services ou des objets distribués. Finalement, la distribution a un impact sur les coûts de développement, d’exploitation et de maintenance.

Tous ces éléments doivent être intégrés dans la réflexion. Et si le besoin n’est pas clair et immédiat, la distribution est une voie dans laquelle il n’est pas conseillé de s’engager.

D’un paradigme à l’autre

Ce n’est pas nouveau, le développement d’applications d’entreprise fait intervenir plusieurs paradigmes. Le relationnel est le paradigme le plus éprouvé aujourd’hui pour la manipulation et la persistance des données. L’objet s’est imposé au niveau du code de l’application, grâce à un niveau d’abstraction élevé. Enfin, XML prend une place importante dans les échanges de données.

La complexité la plus ordinaire que l’on rencontre consiste à mettre en œuvre les transitions d’un paradigme à un autre. Le cas le plus courant reste le mapping objet-relationnel. La relation entre le code et la base de données induit des problématiques naturellement complexes à appréhender, tels que :

  • les verrous,
  • les transactions,
  • les niveaux d’isolation,
  • les caches, le lazy loading,
  • l’héritage,
  • les associations,
  • le problème du n+1 select.

Ces problèmes ne doivent pas être laissés de côté. Les difficultés surviennent toujours à un moment ou un autre d’un projet, sur l’un de ces aspects. La solution consiste à faire appel à des frameworks adaptés, comme Hibernate. Ces frameworks donnent les moyens d’aborder ces difficultés, avec un niveau élevé d’abstraction.

Généricité, la fausse bonne idée

Il arrive régulièrement dans les projets qu’un problème soit modélisé à un niveau ‘méta’. Ceci se justifie parfois par la simplicité apparente qu’offre une généralisation du problème. Mais, cette simplicité n’est que très rarement effective, et aboutit le plus souvent à des ‘usines à gaz’, qu’il devient très difficile de maintenir.

La tentation est parfois trop forte d’imaginer des modèles d’une généricité absolue, par pure satisfaction intellectuelle, et pour l’excitation et la montée d’adrénaline que cela provoque. Ces modèles sont généralement élaborés pour anticiper les besoins du futur. Et c’est bien là leur tort…

Les modèles génériques exposent ainsi le projet à des coûts supplémentaires, pouvant aboutir au non respect des délais. Ils opposent des résistances à la satisfaction du besoin initial. Par la suite, ils rendent plus difficiles les évolutions. Trop souvent, lorsque le besoin évolue, il remet en cause une partie significative du méta-modèle.

Au final, Il est donc important de construire le modèle de manière pragmatique, pour les besoins de l’application, et non de manière purement intellectuelle.

YAGNI, un garde-fou

Il existe bon nombre de principes élémentaires de développement. Le premier d’entre eux est assurément YAGNI, pour You Ain’t Gonna Need It !. Cette expression suggère que seul ce qui est utile à un instant donné mérite d’être considéré. Tout ce qui pourrait éventuellement être utile plus tard n’a que peu d’intérêt.

L’expérience montre, en effet, que soit le besoin aura évolué, soit qu’il n’existera plus. Alors, rien ne sert de développer une fonctionnalité qui n’a pas été réclamée, ‘juste en prévision’, ou d’effectuer des choix d’architecture pour anticiper des problèmes de performance supposés, ou des besoins fonctionnels anticipés.

YAGNI doit cependant être mis en œuvre intelligemment. Il n’est pas question de transgresser d’autres principes tout aussi importants, parmi lesquels separation of concern, à savoir la séparation des préoccupations.

Un autre principe proche de YAGNI est KISS, pour Keep it Simple and Stupid’. Ce principe suggère simplement que la meilleure façon de rendre une application flexible et maintenable est de la simplifier au maximum. L’application est alors davantage refactorable », et donc plus accommodante face aux évolutions.

Agir à la source

Finalement, réduire les coûts de développement, c’est d’abord et avant tout ne pas développer des fonctionnalités inutiles, et repousser autant que possible la complexité. Guidé par le simple bons sens, les principes de YAGNI et KISS s’appliquent autant au fonctionnel, qu’à l’architecture, à la conception, et au code.

Alors, pourquoi ne pas commencer tout de suite la chasse aux gaspillages ?