Comment garder un logiciel grand et complexe maintenable au fil des ans?

156

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?

chrmue
la source
9
recommande vivement des livres: «Software Project Survival Guide» de Steve McConnell, «Rapid Development» de Steve McConnell, «Refactoring» de Martin Fowler
Imran Omar Bukhsh le
15
... et 'Clean Code' de Oncle Bob;) (Robert C. Martin)
Gandalf
34
N’est-ce pas là une question qui a engendré plusieurs décennies de lourdes lectures et de cours complets dans les universités?
Détly
17
@ Eric Yin - Je ne suis pas d'accord sur les commentaires. Pour moi, ils sont une odeur de code et dans les projets à long terme, les projets ont tendance à faire plus de mal que de bien car ils sont inévitablement obsolètes et deviennent trompeurs.
JohnFx
8
@ Eric Yin: viser un code auto-documenté. N'utilisez les commentaires d'intention que s'ils améliorent la compréhension.
Mitch Wheat

Réponses:

138

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:

  1. Rester simple.
  2. Rester simple. Cela vaut particulièrement pour l'architecture, la "grande image". Si les développeurs ont du mal à obtenir la grande image, ils sont vont coder contre. Alors simplifiez l’architecture pour que tous les développeurs l’obtiennent. Si l'architecture doit être moins que simple, les développeurs doivent être formés pour comprendre cette architecture. S'ils ne l'intériorisent pas, ils ne devraient pas y coder.
  3. Viser un faible couplage et une grande cohésion . Assurez-vous que tous les membres de l'équipe comprennent cette idée. Dans un projet constitué de pièces cohésives faiblement couplées, si certaines pièces deviennent des dégâts incontrôlables, vous pouvez simplement débrancher et réécrire cette pièce. C'est plus difficile ou presque impossible si le couplage est serré.
  4. Être cohérent. Les normes à suivre importent peu, mais veuillez respecter certaines normes. Dans une équipe, tout le monde devrait suivre les mêmes normes de parcours. Par ailleurs, il est facile de s’attacher trop aux normes et d’oublier le reste: comprenez bien que, bien que les normes soient utiles, elles ne sont qu’une petite partie de la fabrication d’un bon code. Ne faites pas un grand nombre de celui-ci.
  5. Les revues de code peuvent être utiles pour obliger une équipe à travailler de manière cohérente.
  6. Assurez-vous que tous les outils (IDE, compilateurs, contrôle de version, systèmes de compilation, générateurs de documentation, bibliothèques, ordinateurs , chaises , environnement global, etc.) sont bien entretenus afin que les développeurs ne perdent pas leur temps avec des problèmes secondaires tels que pour lutter contre les conflits de versions de fichiers de projets, les mises à jour de Windows, le bruit et tout ce qui est banal mais irritant. Le fait de perdre à plusieurs reprises un temps considérable avec des éléments aussi inintéressants abaisse le moral, ce qui au moins n'améliorera pas la qualité du code. Dans une grande équipe, il pourrait y avoir un ou plusieurs gars dont la tâche principale est de maintenir les outils de développement.
  7. Lorsque vous prenez des décisions technologiques, réfléchissez à ce qu’il faudrait pour changer de technologie; quelles décisions sont irréversibles et qui ne le sont pas. Évaluez les décisions irréversibles avec une extrême prudence. Par exemple, si vous décidez d'écrire le projet en Java , c'est une décision à peu près irréversible. Si vous décidez d'utiliser un format binaire auto-bouilli pour les fichiers de données, c'est également une décision assez irréversible (une fois que le code est dans la nature et que vous devez continuer à supporter ce format). Mais les couleurs de l'interface graphique peuvent être facilement ajustées, des fonctionnalités initialement laissées peuvent être ajoutées ultérieurement, afin de ne pas trop stresser.
Joonas Pulakka
la source
8
Ce sont d'excellents points. Je dois admettre que j'ai du mal à "garder les choses simples". Cela semble vouloir dire différentes choses pour différentes personnes dans différents contextes, ce qui rend "simple" plutôt complexe (mais j'ai alors une tendance naturelle à compliquer les choses).
Kramii
3
Je suis parfaitement d'accord avec vos points, en particulier "KIS". Mais je vois une tendance à ce que de plus en plus de développeurs (plus jeunes?) Utilisent des structures assez complexes pour décrire même les contextes les plus simples.
chrmue
10
@chrmue: Voir "comment écrire Factorial en Java" ;-)
Joonas Pulakka
8
Umm, et "8. Restez simple".
Dawood ibn Kareem
7
# 6 est digne d'un +10.
Jim G.
55

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.

Tom Squires
la source
3
Point très important. J'ai repris un système hérité, de nombreuses classes, de nombreuses lignes de code, aucune documentation, aucun test unitaire. Après avoir créé avec diligence des tests unitaires pour tous les correctifs et améliorations de code, la conception du système a évolué pour devenir un état plus propre et plus facile à gérer. Et nous avons le "courage" de réécrire des parties essentielles (couvertes par des tests unitaires).
Sam Goldberg
40

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.

maple_shaft
la source
Très bon point, vous touchez le mille! C'est triste à dire, mais c'est exactement ce qui se passe ici. Et il semble impossible de changer les choses sans l'avance technique ...
chrmue
4
@ chrmue Je suppose que les choses vont bien, mais je commence à en avoir assez. À bien des égards, je souhaiterais être de nouveau développeur junior alors que je ne me rendais pas compte à quel point tout semble faux autour de moi. Il semble que je sois en avance sur ma crise de mi-carrière. Mais je suis décousu ... heureux d'aider.
maple_shaft
1
@Murph Pourquoi ne devriez-vous pas avoir un chef d'équipe omniscient pendant la phase de maintenance? Tous les chefs auxquels je m'attendais n’attendaient rien de moins d’un chef d’équipe, et quand j’étais chef d’équipe, je n’attendais rien de moins de moi-même.
maple_shaft
1
@maple_shaft car a) je ne présume pas qu'il y a un chef d'équipe dédié à ce projet et c'est plus ou moins la première exigence et b) je pense que vous devez comprendre la conception et la mise en œuvre (le tout) et c'est dur. D'un côté, nous affirmons tous que nous ne devrions pas avoir une seule base de connaissances sur nos projets et pourtant, nous disons ici que nous devons en avoir un? Cela ne compte pas?
Murph
2
@maple_shaft probablement que je sois un vieux programmeur grincheux (-: Mais il y a un problème en ce que c'est souvent le "style" d'implémentation qui doit être suivi profondément dans une base de code existante - et qui peut être étranger au codeur et au lead (pour beaucoup des raisons du monde réel).
Murph
18

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.

Kramii
la source
1
+1, en particulier pour "constituer une équipe capable de travailler ensemble et d'expédier un produit fonctionnel".
deworde
1
C’est une bonne idée d’avoir une personne responsable de l’architecture. Cependant, si cette responsabilité est souvent utilisée, vous avez une "odeur d'équipe": L'équipe doit naturellement en arriver à des conclusions communes, au lieu de compter sur une seule personne pour fournir les réponses. Pourquoi? La connaissance totale du projet est toujours partagée, le fait de le centrer sur une personne entraînera finalement de plus gros problèmes: seul son point de vue sera satisfait, ce qui coupera efficacement les ailes du reste de l'équipe. Embauchez plutôt les meilleures personnes et laissez-les travailler ensemble.
casper
1
@casper: Exactement. Vous exprimez ce que je pensais plutôt mieux que moi.
Kramii
18

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:

  1. Utilisez des normes pour la dénomination, le codage, les archivages, le flux de bogues, le flux de processus - pratiquement n'importe quoi.
  2. N'ayez pas peur de dire au revoir aux membres de l'équipe qui ne respectent pas les normes. Certains développeurs ne peuvent tout simplement pas travailler avec un ensemble de normes défini et deviendront des ennemis de la 5ème colonne sur le champ de bataille pour garder la base de code propre.
  3. N'hésitez pas à affecter des membres de l'équipe moins qualifiés à des tests sur de longues périodes.
  4. Utilisez tous les outils de votre arsenal pour éviter d’archiver du code pourri: il s’agit d’outils dédiés, ainsi que de tests unitaires pré-écrits qui testent les fichiers de construction, les fichiers de projet, la structure de répertoires, etc.
  5. Dans une équipe d'environ 5 à 8 membres, demandez à votre meilleur type de refactoriser presque constamment - en nettoyant les dégâts laissés par les autres. Même si vous trouvez les meilleurs spécialistes dans le domaine, vous aurez toujours un désordre - c'est inévitable, mais il peut être limité par la refactorisation constante.
  6. Écrivez et testez les tests unitaires - ne comptez PAS sur les tests unitaires pour maintenir le projet propre, ils ne le font pas.
  7. Discutez de tout. N'ayez pas peur de passer des heures à discuter de choses au sein de l'équipe. Cela disséminera les informations et supprimera l’une des causes profondes du code défectueux: confusion sur les technologies, objectifs, normes, etc.
  8. Soyez très prudent avec les consultants qui écrivent du code: leur code sera, presque par définition, le vrai matériel de merde.
  9. Faites les examens de préférence comme étape du processus avant l'enregistrement. N'ayez pas peur d'annuler les commits.
  10. N'utilisez jamais le principe d'ouverture / fermeture, sauf dans la dernière étape avant la libération: cela conduit simplement à laisser du code pourrissant.
  11. Chaque fois que vous rencontrez un problème, prenez le temps de bien le comprendre avant de mettre en œuvre une solution - la plupart des problèmes de code proviennent de la mise en œuvre de solutions à des problèmes qui ne sont pas entièrement compris.
  12. Utilisez les bonnes technologies. Celles-ci viendront souvent par lots et resteront fraîches: mieux vaut dépendre d'une version bêta d'un framework dont vous êtes assuré le support, que de dépendre de frameworks extrêmement stables, mais obsolètes, qui ne sont pas supportés.
  13. Embaucher les meilleures personnes.
  14. Mettez le reste de côté - vous ne dirigez pas un café.
  15. Si la direction n’est pas la meilleure des architectes et qu’elle s’immisce dans le processus de décision, trouvez un autre emploi.
casper
la source
1
Maintenant, que feriez-vous si vous deviez maintenir et améliorer une application VB6 cauchemardesque de 12 ans avec des centaines de formulaires et de classes tout en travaillant sur une réécriture en C # dans le «temps libre»?
Jfrankcarr
7
En désaccord avec # 3. Les tests ne doivent pas être considérés comme une punition pour les non-entraînés. En fait, cela devrait être fait par tous les développeurs, et tout aussi important que le codage et la conception! Si vous avez M. Non entraîné dans l'équipe, faites-le sortir de l'équipe pour un peu d'entraînement!.
NWS
1
"En désaccord avec # 3. Les tests ne doivent pas être considérés comme une punition pour ceux qui ne sont pas formés." - Cela ne devrait pas et ce n’est pas ce que j’ai écrit, laissez-moi vous expliquer: Le test est un bon moyen de permettre aux personnes qui ne le sont pas, mais de faire confiance pour valider des modifications afin d’intégrer le code. Les meilleurs testeurs que j'ai trouvés sont ceux qui aspirent à devenir des contributeurs de code et prouvent leur compétence en examinant le code, en exécutant le programme et en montrant la possibilité de corréler leurs résultats dans l'exécution avec le code source. Ce n'est pas une punition - sa formation.
casper
1
"Pas d'accord avec # 10 - cela aide à la qualité du code si c'est fait correctement" C'est absolument faux dans mon expérience professionnelle: le code verrouillé ne peut pas être refactoré, ce qui signifie qu'il restera dans son état actuel jusqu'à ce qu'il soit déverrouillé. Il est possible de vérifier que cet état fonctionne à un certain stade, mais à un stade ultérieur, cette vérification est un faux positif: tout le code doit être laissé ouvert pour le refactoring jusqu'à l'étape précédant le test final du système.
casper
3
@casper: IMHO, le principe d'ouverture / fermeture ne doit pas être compris comme "vous ne pouvez pas changer le code source", mais plutôt "concevez le code comme s'il était gelé". Assurez-vous qu'il est possible d'étendre le code en tant que nessisary sans exiger de modifications. Le résultat est intrinsèquement plus faiblement couplé et plus cohérent que le code moyen. Ce principe est également crucial lors du développement d’une bibliothèque destinée à être utilisée par des tiers, car ils ne peuvent pas simplement modifier votre code, vous avez donc besoin qu’elle soit correctement extensible.
Kevin Cathcart
12

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:

  • Développer une nouvelle fonctionnalité
  • Résoudre un problème

Accélérez grandement votre cycle de développement test-first en:

  • Refactoring pour convertir des modules de code en langage de script
  • Utilisez des machines de test rapides et basées sur le cloud

Code du refactor pour utiliser un couplage faible (d'unités à cohésion interne élevée) en:

  • Plus simple, (plus) fonctions (routines)
  • Modules
  • Objets (et classes ou prototypes)
  • Fonctions pures (sans effets secondaires)
  • Préférer la délégation à l'héritage
  • Calques (avec API)
  • Collections de petits programmes à but unique pouvant fonctionner ensemble

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.

MarkDBlackwell
la source
1
+1 Bonne première réponse, moyen idéal pour vous présenter à nous!
Yannis
11

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.

Joh
la source
9

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.

JohnFx
la source
8

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.

StuartLC
la source
7

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.

Rotsor
la source
2
Un autre nom pour ce que vous décrivez est la propriété d’être déterministe
Jinglesthula
6

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 .

james
la source
6

É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"

retard
la source
J'ai toujours trouvé que les commentaires sur les fonctions et les classes étaient utiles même s'il ne s'agissait que de séparer les segments de code aux emplacements de fonctions et de classes en utilisant la coloration syntaxique. Je mets très rarement des commentaires en code de fonction, mais j'écris une ligne pour chaque classe et fonction, telle que /** Gets the available times of a clinic practitioner on a specific date. **/ou /** Represents a clinic practitioner. **/.
Nick Bedford
5

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.

Giorgio
la source
Et si les exigences de l’entreprise exprimées en code de travail changent? Ne pas toucher à du code mal pondéré mais techniquement «fonctionnel» peut vous nuire à long terme, surtout quand vient le temps d'apporter les modifications nécessaires.
Jinglesthula
@jinglesthula: Ouvrir pour extension signifie que vous pouvez ajouter la nouvelle fonctionnalité en tant que nouvelle implémentation (par exemple, une classe) d'une interface existante. Bien sûr, le code mal structuré ne le permet pas: il devrait y avoir une abstraction comme une interface qui permet au code de "changer" en ajoutant du nouveau code au lieu de modifier le code existant.
Giorgio
5
  • 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.

Tim Williscroft
la source
4

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.

réduction
la source
3

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.

jfrankcarr
la source
2

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.

Peter Mortensen
la source
Forcé par les circonstances? Les gens introduisent des hacks car (a) ils ne savent pas mieux => ils ont besoin d'encadrement, (b) ils ne voient pas l'image plus grande => la communication, la documentation et la discipline sont nécessaires, (c) ils se pensent plus intelligents => c'est le plus gros obstacle à surmonter (d) contraint par les circonstances => les correctifs rapides sont corrects lorsque le temps presse, mais quelqu'un doit assumer la responsabilité et nettoyer le code par la suite. Toute autre "circonstance" est simplement BS . Il existe des exceptions à cette règle générale, mais les exceptions dites plus orthographiées sont "paresseuses".
JensG
2

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:

  • Organiser et simplifier
  • Raccourcir les fonctions
  • Privilégiez la lisibilité par rapport à l'efficacité (quand cela est acceptable, bien sûr, et certains gains d'efficacité sont trop minables pour être conservés)

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.

Eric.Void
la source
"... le projet est condamné" - d'après mon expérience, ce n'est pas nécessairement le cas. Alternative consiste à éduquer ledit responsable et à convaincre d’investir des efforts dans la maintenabilité et le traitement de la dette technique
gnat le
Désolé, je ne pouvais pas me retenir d'écrire cela, car j'avais déjà une expérience lorsque le responsable a ignoré tous mes conseils en matière de dette technique. Mais je pense que vous avez raison: environ un an après avoir abandonné ce projet, le responsable a perdu toute autorité sur l'équipe technique pour son incapacité à les gérer.
Eric.Void
1

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.

juhist
la source
Cela semble être un peu trop théorique de mon point de vue. Bien sûr, les projets que j'ai mentionnés dans ma question étaient constitués de plusieurs bibliothèques et modules. Mon expérience au cours des 26 dernières années de développement logiciel a
montré
0

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.

utilisateur204677
la source