Je travaille en tant que développeur de logiciels depuis de nombreuses années. D'après mon expérience, les projets deviennent de plus en plus complexes et impossibles à maintenir, à mesure que de plus en plus de développeurs participent au développement du produit.
Il semble que les logiciels, à un certain stade de développement, ont tendance à se "pirater", surtout quand aucun des membres de l’équipe qui a défini l’architecture ne travaille plus dans la société.
Je trouve frustrant qu'un développeur qui doit changer quelque chose ait du mal à se faire une idée d'ensemble de l'architecture. Par conséquent, il existe une tendance à résoudre les problèmes ou à apporter des modifications d'une manière qui va à l'encontre de l'architecture d'origine. Le résultat est un code de plus en plus complexe et encore plus difficile à comprendre.
Existe-t-il des conseils utiles sur la manière de maintenir le code source réellement maintenable au fil des ans?
Réponses:
La seule vraie solution pour éviter la pourriture de code est de bien coder!
Comment bien coder est une autre question. C'est déjà assez difficile, même si vous êtes un excellent programmeur travaillant seul. Dans une équipe hétérogène, cela devient encore plus difficile. Dans les (sous) projets externalisés ... il suffit de prier.
Les bonnes pratiques habituelles peuvent aider:
la source
Les tests unitaires sont votre ami . Leur mise en œuvre force un faible couplage. Cela signifie également que les parties "hacky" du programme peuvent facilement être identifiées et refactorisées. Cela signifie également que toutes les modifications peuvent être testées rapidement afin de ne pas compromettre les fonctionnalités existantes. Cela devrait encourager vos développeurs à modifier les méthodes existantes plutôt que de dupliquer le code, de peur de casser des choses.
Les tests unitaires servent également de documentation supplémentaire pour votre code, décrivant ce que chaque partie doit faire. Avec des tests unitaires approfondis, vos programmeurs ne devraient pas avoir besoin de connaître l'architecture complète de votre programme pour apporter des modifications et utiliser des classes / méthodes existantes.
En tant qu’effet secondaire intéressant, nous espérons que les tests unitaires réduiront également le nombre de bogues.
la source
Tout le monde ici parle rapidement de la pourriture du code , et je comprends tout à fait et suis d'accord avec cela, mais il manque toujours l'image plus grande et le problème plus important qui se pose ici. La pourriture du code ne se produit pas simplement. De plus, les tests unitaires sont bien cités, mais ils ne résolvent pas vraiment le problème. On peut avoir une bonne couverture de tests unitaires et un code relativement exempt de bugs, tout en ayant un code et une conception pourris.
Vous avez mentionné que le développeur travaillant sur un projet avait des difficultés à implémenter une fonctionnalité et manquait l'image globale de l'architecture globale et implémentait donc un piratage dans le système. Où est le leadership technique pour imposer et influencer la conception? Où sont les revues de code dans ce processus?
Vous ne souffrez pas réellement de pourriture du code, mais d'une pourriture d'équipe . Le fait est que cela ne devrait pas avoir d'importance si les créateurs originaux du logiciel ne font plus partie de l'équipe. Si le responsable technique de l'équipe existante comprend pleinement et véritablement la conception sous-jacente et est doué pour le rôle de responsable technique, ce ne serait pas un problème.
la source
Nous pouvons faire plusieurs choses:
Donner à une personne la responsabilité globale de l'architecture. Lors du choix de cette personne, assurez-vous qu'elle possède la vision et les compétences nécessaires pour développer et gérer une architecture, et qu'elle dispose de l'influence et de l'autorité nécessaires pour aider les autres développeurs à suivre l'architecture. Cette personne doit être un développeur expérimenté, à qui la direction fait confiance et qui est respecté par ses pairs.
Créez une culture où tous les développeurs s'approprient l'architecture. Tous les développeurs doivent être impliqués dans le processus de développement et de maintien de l'intégrité architecturale.
Développer un environnement où les décisions architecturales sont facilement communiquées. Encouragez les gens à parler de design et d'architecture, pas seulement dans le contexte du projet en cours, mais en général.
Les meilleures pratiques de codage facilitent la compréhension de l'architecture à partir du code - prenez le temps nécessaire pour refactoriser, commenter le code, développer des tests unitaires, etc. prendre le temps de développer et de suivre vos propres normes.
Assurez-vous que toute la documentation nécessaire est claire, concise, à jour et accessible. Rendez les diagrammes d'architecture de haut et de bas niveau publics (épinglés au mur peut aider) et publiquement gérables.
Enfin, en tant que perfectionniste naturel, je dois reconnaître que l’intégrité architecturale est une aspiration louable, mais qu’il peut y avoir des choses plus importantes, telles que la constitution d’une équipe capable de travailler ensemble et d’expédier un produit fonctionnel.
la source
La façon dont je vais sur ce problème est de le couper à la racine:
Mon explication utilisera les termes de Microsoft / .NET , mais sera applicable à toute plate-forme / boîte à outils:
la source
Nettoyez le code pourri en procédant à une refactorisation pendant l'écriture des tests unitaires. Remboursez (cette) dette sur tout le code que vous touchez, chaque fois que vous:
Accélérez grandement votre cycle de développement test-first en:
Code du refactor pour utiliser un couplage faible (d'unités à cohésion interne élevée) en:
La croissance organique est bonne. la conception initiale est mauvaise.
Avoir un chef de file qui connaît bien la conception actuelle. Sinon, lisez le code du projet jusqu'à ce que vous en soyez informé.
Lire des livres de refactoring.
la source
Réponse simple: vous ne pouvez pas .
Voilà pourquoi vous devriez viser pour écrire petit et simple , le logiciel. Ce n'est pas facile.
Ce n'est possible que si vous réfléchissez suffisamment longtemps à votre problème apparemment complexe pour le définir de manière aussi simple et concise que possible.
La solution à des problèmes véritablement grands et complexes peut souvent encore être résolue en s'appuyant sur des modules petits et simples.
En d'autres termes, comme d'autres l'ont souligné, la simplicité et le couplage lâche sont les ingrédients clés.
Si cela n’est ni possible ni réalisable, vous effectuez probablement des recherches (problèmes complexes sans solution simple connue, ni solution connue du tout). Ne vous attendez pas à ce que la recherche produise directement des produits maintenables, ce n'est pas à quoi la recherche sert.
la source
Je travaille sur une base de code pour un produit qui est en développement continu depuis 1999, alors comme vous pouvez l'imaginer, il est assez complexe à présent. La plus grande source de piratage dans notre base de code provient des nombreuses fois où nous avons dû le porter d’ ASP Classic à ASP.NET , d’ADO à ADO.NET, des publications à la publication à Ajax , en passant par les librairies d’interface utilisateur, les normes de codage, etc.
Dans l’ensemble, nous avons fait un travail raisonnable pour maintenir la base de code maintenable. Les principales choses qui ont contribué à cela sont:
1) Refactorisation constante - Si vous devez toucher un morceau de code hacky ou difficile à comprendre, vous devrez prendre le temps supplémentaire pour le nettoyer et disposer d’une marge de manœuvre dans l’agenda pour le faire. Les tests unitaires rendent cela beaucoup moins effrayant, car vous pouvez tester contre des régressions plus facilement.
2) Gardez un environnement de développement soigné - Restez vigilant sur la suppression de code qui n’est plus utilisé, et ne laissez pas de copies de sauvegarde / de travail / de code expérimental dans le répertoire du projet.
3) Normes de codage uniformes pour la vie du projet - Voyons les choses en face, notre vision des normes de codage évolue avec le temps. Je suggère de rester fidèle à la norme de codage avec laquelle vous avez commencé pendant toute la durée d'un projet, à moins que vous n'ayez le temps de revenir en arrière et de mettre à jour tout le code pour qu'il soit conforme à la nouvelle norme. C'est bien que vous ayez dépassé la notation hongroise à présent, mais appliquez cette leçon à de nouveaux projets et ne vous contentez pas de basculer à mi-parcours sur ce nouveau projet.
la source
Depuis que vous avez associé la question à la gestion de projet, j'ai essayé d'ajouter des points non codés :)
Planifiez le chiffre d'affaires - supposez que toute l'équipe de développement aura disparu au moment où elle entrera en phase de maintenance - aucun développeur digne de ce nom ne veut être bloqué pour maintenir son système pour toujours. Commencez à préparer le matériel de passation dès que vous avez le temps.
La cohérence / uniformité ne peut pas être suffisamment soulignée. Cela découragera une culture de «faire cavalier seul» et encouragera les nouveaux développeurs à demander s'ils ont des doutes.
Continuez à utiliser les technologies, les modèles de conception et les normes, car un nouveau développeur dans l'équipe (à n'importe quel niveau) aura plus de chances de devenir rapidement opérationnel.
Documentation - en particulier architecture - pourquoi les décisions ont été prises et normes de codage. Conservez également les références / notes / feuilles de route dans la documentation du domaine métier. Vous seriez surpris de voir à quel point il est difficile pour une entreprise d’expliquer ce qu’elle fait à un développeur sans expérience du domaine.
Définissez clairement les règles - pas seulement pour votre équipe de développement actuelle, mais pensez aux futurs développeurs de maintenance. Si cela signifie mettre un hyperlien vers la documentation de conception et de codage pertinente sur chaque page, qu’il en soit ainsi.
Assurez-vous que l'architecture et en particulier les couches de code sont clairement délimitées et séparées - cela permettra potentiellement de remplacer des couches de code à mesure que de nouvelles technologies apparaissent, par exemple, remplacer une interface utilisateur Web Forms par une interface utilisateur HTML5 jQuery , etc. acheter un an ou plus de longévité supplémentaire.
la source
Une propriété du code hautement maitainable est la pureté de la fonction .
La pureté signifie que les fonctions doivent renvoyer le même résultat pour les mêmes arguments. Autrement dit, ils ne devraient pas dépendre des effets secondaires d'autres fonctions. En outre, il est utile s’ils n’ont pas eux-mêmes d’effets secondaires.
Cette propriété est plus facile à observer que les propriétés de couplage / cohésion. Vous n'avez pas à faire tout ce qui est en votre pouvoir pour l'atteindre, et je le considère personnellement plus utile.
Lorsque votre fonction est pure, son type est une très bonne documentation en soi. De plus, écrire et lire de la documentation en termes d'arguments / valeur de retour est beaucoup plus facile que celui mentionnant un état global (auquel peuvent éventuellement accéder d'autres threads O_O).
En tant qu'exemple d'utilisation intensive de la pureté pour améliorer la maintenabilité, vous pouvez voir GHC . Il s’agit d’un grand projet d’environ 20 ans dans lequel de vastes restructurations sont en cours et de nouvelles fonctionnalités majeures sont en cours d’introduction.
Enfin, je n'aime pas trop le point "Keep it simple". Vous ne pouvez pas garder votre programme simple lorsque vous modélisez des choses complexes. Essayez de faire un compilateur simple et votre code généré finira probablement très lentement. Bien sûr, vous pouvez (et devriez) simplifier les fonctions individuelles, mais l’ensemble du programme ne sera pas simple en conséquence.
la source
En plus des autres réponses, je recommanderais des calques. Pas trop, mais assez pour séparer les différents types de code.
Nous utilisons un modèle d'API interne pour la plupart des applications. Il existe une API interne qui se connecte à la base de données. Puis une couche d' interface utilisateur . Différentes personnes peuvent travailler à chaque niveau sans perturber ou casser d'autres parties des applications.
Une autre approche consiste à amener tout le monde à lire comp.risks et The Daily WTF afin d’apprendre les conséquences d’un mauvais design et d’une mauvaise programmation. Ils redouteront donc de voir leur propre code publié sur The Daily WTF .
la source
Étant donné que nombre de ces réponses semblent concerner de grandes équipes, même dès le départ, je vais exposer mon point de vue dans le cadre d’une équipe de développement composée de deux personnes (trois si vous incluez le concepteur) pour une startup.
Évidemment, les conceptions et les solutions simples sont préférables, mais lorsque vous avez le type qui paie littéralement votre salaire, vous n'avez pas nécessairement le temps de penser à la solution la plus élégante, simple et facile à gérer. Dans cet esprit, mon premier point important est:
Documentation Pas de commentaires, le code doit être essentiellement auto-documenté, mais il peut s'agir de documents de conception, de hiérarchies et de dépendances de classes, de paradigmes architecturaux, etc. En outre, il peut être utile de documenter les pseudo-bibliothèques étranges qui apparaissent, telles que "ajouter cette classe à un élément pour cette fonctionnalité", car elles empêchent également les utilisateurs de réécrire les fonctionnalités.
Cependant, même si vous avez une limite de temps sévère, j’estime qu’une autre bonne chose à garder à l’esprit est:
Évitez les hacks et les solutions rapides. À moins que la solution rapide ne soit la solution réelle, il est toujours préférable de résoudre le problème sous-jacent, puis de le résoudre. À moins que vous ayez littéralement un scénario "faites-le fonctionner dans les 2 prochaines minutes ou si vous êtes congédié", il est préférable de corriger le problème maintenant, car vous n'allez pas réparer le code plus tard, vous allez simplement passez à la tâche suivante que vous avez.
Et mon conseil préféré est plutôt une citation, bien que je ne me souvienne plus de la source:
"Code comme si la personne qui vient après toi est un psychopathe homicide qui sait où tu vis"
la source
/** Gets the available times of a clinic practitioner on a specific date. **/
ou/** Represents a clinic practitioner. **/
.Un principe qui n’a pas été mentionné, mais que j’estime important, est le principe ouvert / fermé .
Vous ne devez pas modifier le code développé et testé: tout code de ce type est scellé. Étendez plutôt les classes existantes au moyen de sous-classes, ou utilisez-les pour écrire des wrappers, des classes de décorateur ou pour utiliser le modèle qui vous convient. Mais ne changez pas le code de travail .
Juste mes 2 cents.
la source
Sois un éclaireur . Laissez toujours le code plus propre que vous ne l'avez trouvé.
Réparez les fenêtres cassées . Tous ces commentaires "changent dans la version 2.0" lorsque vous utilisez la version 3.0.
Lorsqu'il y a de gros problèmes, concevez une meilleure solution en équipe et faites-le. Si vous ne pouvez pas réparer le piratage en équipe, vous ne comprenez pas assez bien le système. "Demander de l'aide à un adulte." Les personnes les plus âgées pourraient avoir déjà vu cela auparavant. Essayez de dessiner ou d’extraire un schéma du système. Essayez de dessiner ou d’extraire les cas d’utilisation particulièrement difficiles en tant que diagrammes d’interaction. Cela ne résout pas le problème, mais au moins, vous pouvez le voir.
Quelles hypothèses ne sont plus vraies qui ont poussé la conception dans une direction particulière? Il pourrait y avoir un petit refactoring caché derrière une partie de ce gâchis.
Si vous expliquez le fonctionnement du système (même un seul cas d'utilisation) et que vous vous retrouvez dans l'obligation de présenter des excuses d'un sous-système, c'est le problème. Quel comportement rendrait le reste du système plus simple (peu importe la difficulté de son implémentation par rapport à ce qui existe actuellement). Le sous-système classique à réécrire est un sous-système qui pollue tous les autres sous-systèmes avec sa sémantique et son implémentation. "Oh, vous devez grogner les valeurs avant de les introduire dans le sous-système froo, puis vous les redébloquez à nouveau lorsque vous obtenez la sortie du froo. Peut-être que toutes les valeurs devraient être grozées à la lecture de l'utilisateur et du stockage, Et le reste du système est faux? Cela devient plus excitant quand il y a deux ou plusieurs grozifications différentes.
Passez une semaine en équipe à éliminer les avertissements afin que les problèmes réels soient visibles.
Reformatez tout le code à la norme de codage.
Assurez-vous que votre système de contrôle de version est lié à votre système de suivi des bogues. Cela signifie que les modifications futures sont agréables et responsables, et que vous pouvez déterminer POURQUOI.
Faites de l'archéologie. Trouvez les documents de conception d'origine et relisez-les. Ils se trouvent peut-être sur ce vieux PC situé dans un coin du bureau, dans l’espace de travail abandonné ou dans le classeur que personne n’ouvre jamais.
Republiez les documents de conception sur un wiki. Cela aide à institutionnaliser la connaissance.
Rédigez des procédures de type liste de contrôle pour les versions et les versions. Cela évite aux personnes d'avoir à réfléchir pour pouvoir se concentrer sur la résolution de problèmes. Automatisez les constructions autant que possible.
Essayez l'intégration continue . Plus tôt le projet échouera, moins le projet aura de temps à perdre.
Si votre chef d'équipe ne fait pas ces choses, eh bien c'est mauvais pour l'entreprise.
Essayez de vous assurer que tous les nouveaux codes reçoivent les tests unitaires appropriés avec une couverture mesurée. Donc, le problème ne peut pas être pire.
Essayez de tester à l'unité certains des anciens bits qui ne le sont pas. Cela aide à réduire la peur du changement.
Automatisez votre test d'intégration et de régression si vous le pouvez. Au moins avoir une liste de contrôle. Les pilotes sont intelligents, gagnent beaucoup et utilisent des listes de contrôle. Ils ont également bousillé assez rarement.
la source
Lisez puis relisez Code Complete de Steve McConnell. C'est comme une bible d'une bonne écriture de logiciel, de la conception initiale du projet à une seule ligne de code et tout le reste. Ce que j’aime le plus, c’est qu’elle repose sur des décennies de données solides; ce n'est pas seulement le prochain meilleur style de codage.
la source
Je suis venu l'appeler "l'effet Winchester Mystery House". Comme à la maison, tout a commencé assez facilement, mais au fil des ans, de nombreux travailleurs ont ajouté de nombreuses fonctionnalités étranges sans plan d'ensemble que personne ne comprend vraiment plus. Pourquoi cet escalier ne mène-t-il nulle part et pourquoi cette porte ne s'ouvre-t-elle que dans un sens? Qui sait?
La façon de limiter cet effet est de commencer par une bonne conception, suffisamment souple pour permettre une expansion. Plusieurs suggestions ont déjà été proposées à ce sujet.
Mais souvent, vous assumez un travail où les dégâts ont déjà été causés et il est trop tard pour une bonne conception sans effectuer une nouvelle conception et une réécriture coûteuses et potentiellement risquées. Dans ces situations, il est préférable d'essayer de trouver des moyens de limiter le chaos tout en l'acceptant dans une certaine mesure. Cela peut gêner votre conception: tout doit passer par une classe de «gestionnaire» unique, laide et singulière, ou la couche d'accès aux données est étroitement liée à l'interface utilisateur, mais apprenez à vous en occuper. Codez de manière défensive dans ce cadre et essayez de vous attendre à l'inattendu lorsque des «fantômes» du passé des programmeurs apparaissent.
la source
Le refactoring de code et les tests unitaires vont parfaitement bien. Mais comme ce projet de longue haleine est compliqué, cela signifie que la direction ne fait pas tout ce qui est en son pouvoir pour nettoyer la pourriture. L’équipe est tenue d’introduire des hacks, car une personne n’alloue pas assez de ressources pour former les gens et analyser le problème / la demande.
Maintenir un projet de longue haleine est autant une responsabilité du chef de projet qu'un développeur individuel.
Les gens n'introduisent pas de piratage parce qu'ils l'aiment bien; ils sont forcés par les circonstances.
la source
Je veux juste placer une question non technique et une approche (peut-être) pragmatique.
Si votre responsable ne s'intéresse pas à la qualité technique (code gérable, architecture simple, infrastructure fiable, etc.), il devient difficile d'améliorer le projet. Dans ce cas, il est nécessaire d’éduquer ledit responsable et de convaincre d’investir dans la maintenabilité et le traitement de la dette technique .
Si vous rêvez de la qualité de code trouvée dans ces livres, vous avez également besoin d'un chef soucieux de ce problème.
Ou si vous voulez juste apprivoiser un "projet Frankenstein", voici mes conseils:
Selon mon expérience, la programmation est plutôt entropique qu'émergente (du moins dans le paradigme structuré par impératif populaire). Quand les gens écrivent du code pour "juste travailler", la tendance est de perdre son organisation. Maintenant, organiser un code nécessite du temps, parfois beaucoup plus que de le faire fonctionner.
Au-delà de la mise en œuvre des fonctionnalités et des corrections de bugs, prenez le temps de nettoyer le code.
la source
J'ai été surpris de constater qu'aucune des nombreuses réponses ne mettait en évidence l'évidence: faire en sorte que le logiciel se compose de nombreuses petites bibliothèques indépendantes. Avec de nombreuses petites bibliothèques, vous pouvez construire un logiciel volumineux et complexe. Si les exigences changent, vous n'avez pas besoin de jeter toute la base de code ou de chercher comment modifier une base de code qui klaxonne pour faire autre chose que ce qu'elle est en train de faire. Vous décidez simplement quelles bibliothèques sont toujours pertinentes après la modification des exigences et comment les combiner pour obtenir les nouvelles fonctionnalités.
Utilisez toutes les techniques de programmation utilisées dans ces bibliothèques pour en faciliter l’utilisation. Notez que, par exemple, tout langage non orienté objet prenant en charge les pointeurs de fonction prend en charge la programmation orientée objet (OOP). Donc, par exemple en C, vous pouvez faire de la programmation orientée objet.
Vous pouvez même envisager de partager ces petites bibliothèques indépendantes entre plusieurs projets (les sous-modules git sont vos amis).
Il va sans dire que chaque petite bibliothèque indépendante devrait faire l’objet de tests unitaires. Si une bibliothèque particulière n'est pas testable par unité, vous faites quelque chose de mal.
Si vous utilisez C ou C ++ et que vous n'aimez pas l'idée de disposer de nombreux petits fichiers .so, vous pouvez lier toutes les bibliothèques ensemble dans un fichier .so plus volumineux, ou vous pouvez également créer des liens statiques. La même chose est vraie pour Java, changez simplement .so en .jar.
la source
Simple: réduisez les coûts de maintenance de la majeure partie de votre code à zéro jusqu'à ce que vous disposiez d'un nombre de pièces mobiles maintenable. Un code qui n'a jamais besoin d'être modifié n'entraîne aucun coût de maintenance. Je recommande de faire en sorte que le code ait vraiment un coût de maintenance nul , sans essayer de le réduire par rapport à de nombreuses itérations de refactoring petites et difficiles. Faites que ça coûte zéro tout de suite.
D'accord, il est vrai que c'est beaucoup, beaucoup plus difficile qu'il n'y paraît. Mais ce n'est pas difficile de commencer. Vous pouvez prendre une partie de la base de code, la tester, construire une belle interface par-dessus si la conception de l'interface est un désordre, et commencer à développer les parties de la base de code qui sont fiables, stables (comme s'il manquait de raisons de changer), rétrécir les pièces qui ne sont pas fiables et instables. Les bases de code qui se sentent comme un cauchemar à entretenir ne distinguent souvent pas les pièces mobiles qui doivent être changées, les pièces qui ne le sont pas, car tout est considéré comme peu fiable et susceptible de changer.
En fait, je recommande de séparer complètement l’organisation de votre base de code en parties "stables" et "instables", les parties stables étant un énorme PITA à reconstruire et à modifier (ce qui est une bonne chose, car elles ne devraient pas être nécessaires). être modifiés et reconstruits s’ils appartiennent vraiment à la section "stable").
Ce n'est pas la taille d'une base de code qui rend la maintenabilité difficile. C'est la taille de la base de code qui doit être maintenue. Je dépend de millions de lignes de code chaque fois que j'utilise l'API du système d'exploitation. Mais cela ne contribue pas aux coûts de maintenance de mon produit, car je n'ai pas à maintenir le code source du système d'exploitation. Je viens d'utiliser le code et ça marche. Le code que j'utilise simplement et que je n'ai jamais entretenu n'entraîne aucun frais d'entretien de ma part.
la source