J'ai une question liée au style de codage. Dans mon exemple, c'est Java, mais je pense que cela peut être une question générique concernant les langages qui évoluent rapidement.
Je travaille sur une base de code Java qui a été principalement écrite en ciblant Java 8. Le code a été migré vers Java 12. Par migration, je veux dire que le code peut s'exécuter sur jdk12, donc les modifications/ajouts nécessaires à la bibliothèque ont été effectués, mais les nouvelles fonctionnalités du langage n'ont pas encore été utilisées (par exemple mot-clé var).
Ma question est, quelle devrait être l'approche pour introduire de nouveaux éléments de langage du point de vue de la lisibilité? Quelle est l'importance de la cohérence?
J'ai quelques approches possibles dans mon esprit.
Je sais que cette question est un peu difficile à répondre (comme les questions de style de codage en général), mais j'espère quand même qu'il y aura une conclusion.
A Rome, fais comme les Romains.
C'est bien si une base de code entière suit un style cohérent. Cependant, cela piégera son style dans le passé.
Si vous êtes enchanté par un nouveau style enchevêtré, la pire chose que vous puissiez faire est de le disperser au hasard parmi le code laissé dans l'ancien style.
C'est pourquoi lorsque vous repeignez une maison d'une couleur différente, vous ne le faites pas seulement là où la peinture est endommagée. Vous le faites pièce par pièce.
Il vaut mieux faire le changement dans une certaine limite. Ceux qui lisent le code devraient pouvoir facilement prédire quel style ils trouveront à chaque endroit.
Changer le style de code de travail est une refactorisation. Ne refactorisez pas sans tests. Et ne refactorisez pas tout d'un coup. Faites une pièce, reculez et demandez aux autres ce qu'ils pensent.
Lorsque vous ne pouvez pas faire tout cela, restez avec l'ancien style. Utilisez uniquement de nouvelles fonctionnalités linguistiques qui se marient bien avec l'ancien style.
Je travaille avec C # depuis la version 1.1 (vers 2003), et j'ai une expérience directe de la façon dont nous avons introduit de nouvelles fonctionnalités de langage dans notre base de code croissante au fil des ans. Nous les avons simplement présentés chaque fois qu'ils étaient disponibles, et tous les membres de l'équipe ont commencé à les utiliser dans le domaine du code sur lequel ils travaillaient.
Ces fonctionnalités comprenaient
collections génériques (au lieu de collections faiblement typées)
syntaxe de propriété améliorée
le mot clé var
méthodes d'extension
des outils fonctionnels comme les expressions lambda, les fermetures, etc.
Linq
asynchrone/attendre
(pour n'en citer que quelques exemples).
Notez que même dans le code nouvellement écrit, chacune de ces fonctionnalités est utilisée en conjonction avec des fonctionnalités de langage plus anciennes. Vous ne commencez pas à remplacer chaque boucle possible par une construction Linq simplement parce que Linq est disponible. Vous ne commencez pas à utiliser des méthodes d'extension pour chaque fonction simplement parce que "les méthodes d'extension sont disponibles". Et utiliser var
aujourd'hui pour chaque déclaration de variable ne rend pas votre code plus lisible.
Par conséquent, l'expérience réelle n'était pas aussi mauvaise que les autres réponses pourraient le faire croire - bien au contraire. Dans une base de code plus grande, au cas où vous ne voudriez pas vous en tenir aux vieilles constructions maladroites pour toujours, vous devez commencer à utiliser les nouvelles constructions quelque part , mais vous ne pouvez pas les introduire partout - et cela n'aurait même aucun sens de les utiliser partout. Vous ne mettez généralement pas à jour les sections de code de travail vers de nouvelles constructions de langage lorsqu'il n'y a pas d'autre raison impérieuse. Cela conduit automatiquement à certaines sections avec plus de nouvelles constructions de langage, certaines avec moins et certaines sans aucune, toutes fonctionnant bien et suffisamment lisibles, côte à côte, souvent pendant des années.
Donc, ma recommandation est la suivante: ne pas trop y penser. La lisibilité du code où ces fonctionnalités sont mélangées est très subjective, et dans une équipe où tout le monde connaît bien les fonctionnalités de l'ancien et du nouveau langage, c'est moins un problème que l'on pourrait penser.
Vous demandez comment gérer la dette technique. En supposant que les éléments critiques ont été pris en charge (mise à jour du code pour fonctionner avec la nouvelle version de Java), les types de modifications que vous souhaitez effectuer n'ont pas d'impact direct sur la base d'utilisateurs. Par conséquent, il est difficile d'amener les propriétaires de produits ou la direction à accepter d'investir dans cette migration.
Si vous gérez un projet open source, vous n'avez pas à vous soucier de cet aspect de gestion. Dans ce cas, c'est à votre communauté de déterminer la vitesse à laquelle vous investissez dans les mises à jour.
Il y a des risques
Changer votre code de travail sans un ensemble sain de tests unitaires, c'est comme marcher sur une corde raide sans filet. Si vous êtes très prudent, cela peut être fait, mais quand les choses tournent mal, elles peuvent aller épiquement mal.
Il est toujours préférable de faire ce que vous pouvez pour atténuer les risques lorsque vous remboursez la dette technique.
Si vous effectuez une autre refactorisation/modification dans une classe, apportez l'ensemble de la classe pour correspondre à vos directives de codage actuelles. Évitez d'avoir des styles incohérents dans la même classe. Les classes doivent être suffisamment petites pour que vous puissiez le faire facilement et avec un faible risque, et la classe sera probablement testée de toute façon lorsque vous la modifiez.
Vous pouvez appliquer une modification sur la base de code entière en une seule fois si elle peut être appliquée mécaniquement. Par exemple. si vous décidez que var foo = Foo()
est meilleur que Foo foo = Foo()
alors vous pouvez raisonnablement appliquer ceci en toute sécurité à la base de code en une seule fois avec un outil de refactoring.