Programmation par copier-coller
La programmation par copier-coller est la production de code source par de nombreux copier-coller. Elle est un palliatif à des compétences insuffisantes en programmation informatique, mais peut être due à des limitations technologiques (par exemple, un environnement de développement peu expressif) là où des fonctions ou des bibliothèques logicielles seraient normalement utilisées. Elle est considérée comme acceptable voire nécessaire, par exemple avec le code redondant (boilerplate en anglais), le déroulage de boucle manuel (quand le compilateur ne sait pas le faire automatiquement), ou avec certains idiomes de programmation. Dans ces cas, cette pratique est prise en charge, par les éditeurs de texte et environnements de développement sous la forme de snippets.
Origines
La programmation par copier-coller est souvent pratiquée par des développeurs inexpérimentés ou des étudiants pour qui l’écriture de code à partir de zéro est difficile et qui préfèrent chercher une solution toute faite ou une solution partielle qu'ils peuvent adapter pour résoudre leur propre problème[1].
Les développeurs inexpérimentés qui copient souvent du code ne comprennent pas complètement le code qu'ils copient. Le problème vient plus de leur inexpérience et de leur manque de courage en programmation que du copier-coller en lui-même. Le code vient souvent de sources diverses telles que le code de leurs amis ou de leur collègues, les forums internet, le code produit par les professeurs ou assistant professeurs ou les livres sur la programmation. Le résultat est un assemblage de styles de code différents avec du code superflu.
Cette pratique est également la cause de bugs : les décisions de conception provenant du code copié peuvent ne plus s'appliquer là où le code est reproduit.
Le code collé peut être impénétrable car les noms des variables, classes, fonctions et autres sont rarement modifiés, même si leur utilisation peut être complètement différente dans le nouveau contexte[1].
Duplication
Utiliser des snippets
Le copier-coller est aussi utilisé par des développeurs expérimentés qui disposent d'une bibliothèque de snippets bien testés et génériques qui sont faciles à adapter pour une tâche spécifique[2].
Étant une forme de duplication de code, la programmation par copier-coller hérite des mêmes problèmes: ces problèmes sont exacerbés si le code ne garde pas de lien sémantique entre la source et les copies. Dans ce cas, si des changements doivent être apportés, le développeur perd du temps à chercher les clones. Ce problème peut être amoindri si le code d'origine et/ou les copies sont commentés de manière appropriée; cependant il faut toujours répéter le même changement en de nombreux endroits. Par ailleurs, puisque les commentaires ne sont souvent pas mis à jour lors de la maintenance[3], les commentaires précisant où trouver les clones ont la réputation de devenir caducs.)
Les tenants des méthodologies orientées objet rejettent l'utilisation du copier-coller avec une bibliothèque de code. Au lieu de copier de manière répétée un algorithme générique, l'algorithme serait abstrait dans une classe encapsulée réutilisable. La classe est écrite d'une manière flexible, pour permettre l'héritage et la surcharge, afin que tout le code appelant puisse utiliser ce code générique directement, au lieu de modifier le code original[4]. Lorsque des fonctionnalités supplémentaires sont nécessaires, la bibliothèque est modifiée (tout en maintenant la compatibilité descendante). Ainsi, si un bug est corrigé dans l'algorithme originel ou bien s'il est amélioré, tous les logiciels qui l'utilisent en bénéficient.
Brancher le code
Brancher le code est une pratique classique dans les grandes équipes de développement, car cela permet de développer sur plusieurs branches en parallèle, ce qui réduit donc les cycles de développement. L'utilisation de branches implique:
- Un gestionnaire de contrôle de version qui supporte les branches
- Les branches sont fusionnées à nouveau lorsque le développement parallèle est terminé.
Le copier-coller est une alternative moins formelle que l'utilisation de branches, souvent utilisée lorsque l'on s'attend à ce que les branches divergent de plus en plus avec le temps, comme lorsqu'un nouveau produit est créé à partir d'un produit existant.
Lorsque l'on crée un produit à partir d'un produit existant, la programmation par copier-coller présente des avantages évidents puisque les nouveaux développements n'affectent pas le code du produit existant:
- Il n'y a pas besoin d’exécuter des tests de régression sur le produit existant, ce qui dégage du temps pour que l'assurance qualité travaille sur le lancement du nouveau produit, et réduit le temps de mise sur le marché.
- Il n'y a pas de risque d'introduire des bugs dans le produit existant, ce qui pourrait frustrer la base d'utilisateurs existante.
Les désavantages sont:
- Si le nouveau produit ne diverge pas autant qu’envisagé du produit existant, alors les deux bases de code devront être maintenues (doublant les coûts) là où un seul produit aurait suffi. Ceci peut mener à un refactoring coûteux et une fusion manuelle finalement.
- La base de code dupliqué double le temps nécessaire pour implémenter les changements demandés sur les deux produits; ce qui augmente le temps de mise sur le marché pour ces changements, et pourrait en fait absorber tout le temps gagné par l'utilisation initiale d'une base de code séparée.
Comme ci-dessus, l'alternative au copier-coller serait une approche modulaire:
- D'abord isoler le code à partager entre les deux produits dans des bibliothèques.
- Utiliser ces bibliothèques (plutôt qu'une deuxième copie de la base de code) comme fondation pour le développement du nouveau produit.
- Si des versions postérieures a la deuxième version sont envisagées par la suite, cette approche fonctionne mieux car les bibliothèques déjà existantes réduisent les cycles de développement de toutes les versions suivant la deuxième [5].
Tâches répétitives ou variations d'une tâche
Une des formes les plus néfaste de la programmation par copier-coller apparait dans du code exécutant une tâche répétitive, ou une variation de la même tâche qui dépend d'une variable. Chaque instance est copiée-collée depuis le code situé au-dessus, et des modifications mineures sont apportées. Les effets néfastes incluent:
- Le copier-coller produit souvent de longues méthodes (qui est un code smell).
- Chaque instance crée du code dupliqué, avec tous les problèmes vus précédemment, avec une portée bien plus grande. Il est commun de voir des vingtaines d'instances dupliquées; voire des centaines parfois. Les corrections de bugs, ou l’évolution du code deviennent très difficiles et coûteux[6].
- Un tel code devient moins lisible, car il est difficile de discerner exactement ce qui diffère entre chaque répétition. Ceci augmente les risques et le coût de modifier le code.
- La programmation procédurale rejette explicitement le copier-coller pour les tâches répétitives. Dans ce paradigme, une approche préférable à la succession de tâches répétitives consiste à créer une fonction ou une sous-routine qui regroupe les instructions répétés. Cette fonction est ensuite appelée de manière répétitive par la routine parente, ou bien même appelée dans une boucle. Un tel code est dit « bien décomposé », et il est recommandé car il est plus facile à lire et plus facilement extensible[7].
- La règle applicable dans ce cas est « ne vous répétez pas ».
Choix de conception délibéré
La programmation par copier-coller est occasionnellement acceptée comme une pratique de développement appropriée. C'est le cas avec du code code redondant (boilerplate en anglais) tel que les déclarations de classes ou l'import de bibliothèques standards, ou en utilisant un modèle de code existant (avec un contenu vide ou des ébauches de fonctions) en tant que guides à compléter.
L’utilisation d'idiotismes de programmation et de patrons de conception sont similaires au copier-coller, puisqu'ils utilisent un code convenu. Dans certains cas ils peuvent être capturés dans des snippets, qui sont alors collés lorsqu'on en a besoin, même s'ils sont souvent écrits par le développeur d’après ses souvenirs. Parfois, ces idiomes ne peuvent pas être réduits à un snippet. Cependant, dans la plupart des cas, un idiome sera soit suffisamment long pour être abstrait dans une fonction, soit suffisamment court pour qu'il puisse être directement tapé au clavier.
Exemple
Un exemple simple est une boucle for qui peut être écrite comme ceci: for (int i=0; i!=n; ++i) {}
.
Voici un exemple de code utilisant une boucle for :
void foo(int n) {
for (int i=0; i!=n; ++i) {
/* body */
}
}
La boucle pourrait avoir été générée avec le snippet suivant (qui spécifie les types et noms de variable):
for ($type $loop_var = 0; $loop_var != $stop; ++$loop_var) {
/* body */
}
Références
- (en)"Revisiting Novice Programmers Errors". acm.org.
- (en) « Building ASP.NET Web Pages Dynamically in the Code-Behind », codeproject.com (consulté le )
- (en)Spinellis, Diomidis.
- (en)Lewallen, Raymond. "4 major principles of Object-Oriented Programming". codebetter.com.
- (en)Eriksen, Lisa.
- (en)Nigel Cheshire and Richard Sharpe.
- (en)"Stanford University, CS 106X ("Programming Abstractions") Course Handout: "Decomposition"" (PDF).
- Portail de la programmation informatique