Je travaille sur un projet logiciel où nous devons construire trois API. Un pour le canal bancaire de la maison , un pour le canal de l'agence et un troisième pour le canal mobile .
L'API de l'agence est la plus complète car elle possède toutes les fonctionnalités .. puis une API Home un peu plus petite puis une API mobile.
Les architectes ont ici créé une couche commune (services EJB cross-canal partagés par toutes les API). Mais alors les API sont différentes.
Il n'y a pas de grande différence pour l'instant entre les API. La grande équipe a commencé avec le canal de l'agence, et nous l'adaptons maintenant pour le canal domestique. Nous enrichissons simplement des objets spécifiquement pour notre application domestique. Sinon, le code est similaire à 95% entre les API. Les API sont construites au-dessus de Spring MVC , et elles l'ont (contrôleurs, modèles et certains utilitaires).
Fondamentalement, les contrôleurs font le mappage BO sur ChannelObject (il me semble que ce n'est pas le bon endroit pour le faire), et quelques utilitaires et sérialiseurs supplémentaires. Tout est en double pour l'instant. Ils disent que la raison de la duplication est qu'ils veulent que les API soient indépendantes. "Si demain nous voulons un comportement différent pour la maison que pour l'agence ou le mobile, nous n'aurons pas de difficulté !!"
Existe-t-il un cas où nous devrions accepter un code en double?
La duplication peut être la bonne chose à faire, mais pas pour cette raison.
Dire "nous pourrions vouloir que ces trois endroits dans la base de code se comportent différemment même s'ils sont identiques en ce moment" n'est pas une bonne raison pour la duplication à grande échelle. Cette notion pourrait s'appliquer à chaque système, et elle pourrait être utilisée pour justifier tout la duplication, ce qui n'est évidemment pas raisonnable.
La duplication ne doit être tolérée que lorsque sa suppression serait globalement plus coûteuse maintenant pour une autre raison (ne peut pas penser à une bonne pour le moment, mais assuré qu'il peut y en avoir un - pratiquement tout dans la programmation est un compromis plutôt qu'une loi).
Pour ce que vous faites, la bonne solution pourrait être par exemple extraire le comportement qui est dupliqué en ce moment dans une stratégie ou un autre modèle qui modélise le comportement en tant que classes, puis utilise trois instances de la même classe. De cette façon, lorsque vous voulez changer le comportement à l'un des trois endroits, il vous suffit de créer une nouvelle stratégie et de l'instancier en un seul endroit . De cette façon, vous n'avez qu'à ajouter quelques classes et laisser le reste de la base de code presque complètement intact.
Sandi Metz, un ingénieur logiciel renommé et auteur dans l'écosystème Ruby, a un grand article de blog et un talk où elle parle également de la relation entre la duplication et l'abstraction. Elle arrive à la conclusion suivante
la duplication est beaucoup moins chère que la mauvaise abstraction
Et je suis complètement d'accord avec elle. Permettez-moi de vous donner plus de contexte pour cette citation. Parfois, trouver la bonne abstraction est très difficile. Dans de tels cas, il est tentant de choisir n'importe quelle abstraction pour réduire la duplication. Mais plus tard, vous découvrirez peut-être que votre abstraction n'est pas valable dans tous les cas. Cependant, il est coûteux de tout changer à nouveau et de suivre un itinéraire différent (pour une bien meilleure explication, regardez-la parler!).
Alors oui, pour moi, il y a des cas exceptionnels, où accepter la duplication est la bonne décision, surtout lorsque vous n'êtes pas sûr de ce qui va arriver et que les exigences sont susceptibles de changer. D'après votre message, je suppose qu'il y a beaucoup de double emploi maintenant, mais vos collègues suggèrent que cela pourrait changer et ne pas coupler les deux applications l'une à l'autre. À mon avis, cet argument est valable et ne peut être ignoré en général.
Si les gens commencent à raisonner sur la conception avec les mots "si demain", c'est souvent un grand signe d'avertissement pour moi, surtout lorsque l'argument est utilisé pour justifier une décision qui comprend du travail et des efforts supplémentaires, pour lesquels personne ne sait vraiment si cela sera rentable, et ce qui est plus difficile à changer ou à revenir que la décision inverse.
La duplication de code réduit l'effort uniquement à court terme, mais elle augmentera les efforts de maintenance presque immédiatement, proportionnellement au nombre de lignes de code dupliquées. Notez également qu'une fois le code dupliqué, il deviendra difficile de supprimer la duplication quand il s'avère que ce n'était pas la bonne décision, tandis que si l'on ne duplique pas le code maintenant, il est toujours facile d'introduire la duplication plus tard s'il s'avère coller à = DRY n'était pas la bonne décision.
Dit que, dans les grandes organisations, il est parfois avantageux de favoriser l'indépendance des différentes équipes par rapport au principe DRY. Si vous supprimez la duplication en extrayant les 95% des parties communes des API deux, un nouveau composant conduit à un couplage de deux équipes par ailleurs indépendantes, ce ne sera peut-être pas la décision la plus sage. En revanche, si vous avez des ressources limitées et qu'une seule équipe maintiendra les deux API, je suis sûr qu'il sera dans leur propre intérêt de ne pas créer un double effort et éviter toute duplication de code inutile.
Notez en outre que cela fait une différence si les API "Accueil" et "Agence" sont utilisées exclusivement par des applications entièrement différentes, ou si l'on peut essayer d'écrire un composant construit au-dessus de ces API qui peuvent également être utilisées dans un contexte "Accueil". comme dans un contexte "Agence". Pour cette situation, avoir les parties communes des API exactement identiques (que vous ne pouvez garantir que si les parties communes ne sont pas dupliquées), rendra probablement le développement d'un tel composant beaucoup plus facile.
Donc, s'il s'avère qu'il y aura des sous-équipes vraiment différentes, chacune responsable de chacune des API, chacune avec un calendrier et des ressources différents, alors il est temps de dupliquer le code, mais pas "juste au cas où".
Duplication pour empêcher le couplage. Disons que vous avez deux gros systèmes et que vous les forcez à utiliser la même bibliothèque. Vous pouvez coupler le cycle de libération des deux systèmes. Ce n'est peut-être pas trop grave, mais disons qu'un système doit introduire un changement. L'autre doit analyser le changement et peut être affecté. Parfois, cela peut casser des choses. Même si les deux parties sont capables de coordonner les changements, cela pourrait être beaucoup de réunions, passant par les managers, les tests, les dépendances et la fin de la petite équipe autonome.
Vous payez donc le prix du code dupliqué pour gagner en autonomie et en indépendance.