Votre problème est très courant. Et un vrai problème aussi, car il n'y a pas de bonne solution.
Nous sommes dans la même situation ici, enfin je dirais pire, avec 13 millions de ligne de code, du chiffre d'affaire et plus de 800 développeurs travaillant sur le code. Nous discutons souvent du même problème que celui que vous décrivez.
La première idée - que vos développeurs ont déjà utilisée - consiste à remanier le code commun dans certaines classes utilitaires. Notre problème avec cette solution, même avec la programmation en binôme, le mentorat et la discussion, est que nous sommes tout simplement trop nombreux pour que cela soit efficace. En fait, nous nous développons en sous-équipes, les gens partageant les connaissances dans leur sous-équipe, mais les connaissances ne transitent pas entre les sous-équipes. Peut-être avons-nous tort, mais je pense que même la programmation en binôme et les discussions ne peuvent rien faire dans ce cas.
Nous avons également une équipe d'architecture. Cette équipe est chargée de traiter les questions de conception et d'architecture et de créer des utilitaires communs dont nous pourrions avoir besoin. Cette équipe produit en fait quelque chose que nous pourrions appeler un cadre d'entreprise. Oui, c'est un cadre, et parfois il fonctionne bien. Cette équipe est également chargée de promouvoir les meilleures pratiques et de faire prendre conscience de ce qui doit être fait ou non, de ce qui est disponible ou non.
Une bonne conception de l'API de base de Java est l'une des raisons du succès de Java. De bonnes bibliothèques tierces à code source ouvert comptent également pour beaucoup. Même une petite API bien conçue permet d'offrir une abstraction vraiment utile et peut contribuer à réduire considérablement la taille du code. Mais vous savez, créer un cadre et une API publique n'est pas du tout la même chose que de coder une classe utilitaire en deux heures. Cela a un coût très élevé. Une classe utilitaire coûte 2 heures pour le codage initial, peut-être 2 jours avec le débogage et les tests unitaires. Lorsque vous commencez à partager du code commun sur de grands projets/équipes, vous créez vraiment une API. Vous devez alors assurer une documentation parfaite, un code vraiment lisible et facile à maintenir. Lorsque vous publiez une nouvelle version de ce code, vous devez rester rétrocompatible. Vous devez le promouvoir à l'échelle de l'entreprise (ou au moins de l'équipe). De 2 jours pour votre petite classe utilitaire, vous passez à 10 jours, 20 jours ou même 50 jours pour une API à part entière.
Et la conception de votre API n'est peut-être pas très bonne. Ce n'est pas que vos ingénieurs ne soient pas brillants - ils le sont en fait. Mais êtes-vous prêt à les laisser travailler 50 jours sur une petite classe utilitaire qui aide simplement à analyser les nombres d'une manière cohérente pour l'interface utilisateur ? Êtes-vous prêt à les laisser redessiner le tout lorsque vous commencerez à utiliser une interface utilisateur mobile avec des besoins totalement différents ? Avez-vous également remarqué que les ingénieurs les plus brillants du monde créent des API qui ne seront jamais populaires ou qui disparaîtront lentement ? Vous voyez, le premier projet web que nous avons réalisé n'utilisait que des frameworks internes ou pas de framework du tout. Nous avons ensuite ajouté PHP/JSP/ASP. Puis, en Java, nous avons ajouté Struts. Maintenant, JSF est la norme. Et nous envisageons d'utiliser Spring Web Flow, Vaadin ou Lift...
Tout ce que je veux dire, c'est qu'il n'y a pas de bonne solution, les frais généraux augmentent de façon exponentielle avec la taille du code et de l'équipe. Le partage d'une grande base de code limite votre agilité et votre réactivité. Tout changement doit être fait avec soin, vous devez penser à tous les problèmes d'intégration potentiels et tout le monde doit être formé aux nouvelles spécificités et fonctionnalités.
Mais le principal point de productivité dans une entreprise de logiciels n'est pas de gagner 10 ou même 50 lignes de code lors de l'analyse du XML. Un code générique pour ce faire atteindra de toute façon un millier de lignes de code et recréera une API complexe qui sera stratifiée par des classes utilitaires. Lorsqu'un utilisateur crée une classe utilitaire pour analyser le XML, il s'agit d'une bonne abstraction. Il donne un nom à une douzaine ou même une centaine de lignes de code spécialisé. Ce code est utile parce qu'il est spécialisé. L'API commune permet de travailler sur des flux, des URL, des chaînes de caractères, n'importe quoi. Elle possède une fabrique qui permet de choisir l'implémentation de l'analyseur. La classe utilitaire est bonne car elle ne fonctionne qu'avec ce parseur et avec des chaînes de caractères. Et parce que vous n'avez besoin que d'une seule ligne de code pour l'appeler. Mais bien sûr, ce code utilitaire est d'un usage limité. Il fonctionne bien pour cette application mobile, ou pour charger une configuration XML. Et c'est pourquoi le développeur a ajouté la classe utilitaire pour cela en premier lieu.
En conclusion, au lieu d'essayer de consolider le code pour l'ensemble de la base de code, j'envisagerais de répartir la responsabilité du code au fur et à mesure que les équipes se développent :
- transformer votre grande équipe qui travaille sur un grand projet en petites équipes qui travaillent sur plusieurs sous-projets ;
- veiller à ce que l'interfaçage soit bon pour minimiser les problèmes d'intégration, mais laisser l'équipe disposer de son propre code ;
- au sein de ces équipes et des bases de code correspondantes, assurez-vous de disposer des meilleures pratiques. Pas de code dupliqué, bonnes abstractions. Utilisez les API existantes et éprouvées de la communauté. Utilisez la programmation en binôme, une documentation solide sur les API, des wikis... Mais vous devriez vraiment laisser les différentes équipes faire leurs choix, construire leur propre code, même si cela signifie un code dupliqué entre les équipes ou des décisions de conception différentes. Vous savez, si les décisions de conception sont différentes, c'est peut-être parce que les besoins sont différents.
Ce que vous gérez réellement, c'est la complexité. En fin de compte, si vous créez une base de code monolithique, très générique et avancée, vous augmentez le temps nécessaire aux nouveaux arrivants pour se mettre à niveau, vous augmentez le risque que les développeurs n'utilisent pas du tout votre code commun, et vous ralentissez tout le monde parce que tout changement a beaucoup plus de chances de casser la fonctionnalité existante.