Comment surmonter la programmation par hasard? [fermé]

24

Dans le livre The Pragmatic Programmer , les auteurs mentionnent le concept de programmation par coïncidence . Il explique ce que c'est, pourquoi il est causé, quels sont les dangers que vous pouvez rencontrer et il se compare à un champ de mines terrestres en temps de guerre.

Avez-vous déjà regardé de vieux films de guerre en noir et blanc? Le soldat fatigué sort prudemment de la brosse. Il y a une clairière à venir: y a-t-il des mines terrestres ou est-il sûr de traverser? Rien n'indique qu'il s'agit d'un champ de mines - pas de panneaux, de barbelés ou de cratères. Le soldat pousse le sol devant lui avec sa baïonnette et grimace, s'attendant à une explosion. Il n'y en a pas. Il parcourt donc péniblement le champ pendant un moment, poussant et poussant au fur et à mesure. Finalement, convaincu que le terrain est sûr, il se redresse et marche fièrement en avant, pour être ensuite mis en pièces.

Les sondes initiales du soldat pour les mines n'ont rien révélé, mais ce n'était que de la chance. Il a été conduit à une fausse conclusion - avec des résultats désastreux.

En tant que développeurs, nous travaillons également dans les champs de mines. Il y a des centaines de pièges qui n'attendent que de nous attraper chaque jour. Rappelant l'histoire du soldat, nous devons nous garder de tirer de fausses conclusions. Nous devons éviter de programmer par coïncidence - en se fondant sur la chance et les succès accidentels - en faveur d'une programmation délibérée ...

Mais je ne suis pas vraiment satisfait de la façon dont ils décrivent le problème "comment le surmonter". Oui, il faut penser à l'avance avant d'écrire le code, mais comment pratiquer cela? La seule chose que je peux penser est en ajoutant des fonctionnalités aux projets Open Source existants, où vous devez avoir des connaissances à la fois sur "ce que je fais maintenant" et sur "Comment les autres morceaux de code fonctionnent", et ce n'est pas le cas lorsque vous écrivez vos propres projets.

MODIFIER:

un résumé de vos messages:

  • Ne devinez pas votre prochain mouvement, prouvez qu'il est correct
  • Test unitaire et refactoriser autant que possible, si nécessaire
  • Ajouter des fonctionnalités - compiler - tester souvent
  • Si vous ne pouvez pas expliquer le code à un noob, vous programmez probablement par hasard.

BTW, il est difficile d'accepter une réponse, c'est vraiment difficile. Toutes les réponses sont vraiment géniales :)

py_script
la source
6
Cela aiderait les personnes qui n'auraient pas lu le livre depuis longtemps à avoir un lien comme pragprog.com/the-pragmatic-programmer/extracts/coincidence .
btilly
À moins que vous ayez une carrière de programmeur très courte (ou que vous soyez une entreprise individuelle), vous rencontrerez probablement un code qui vous semble étrangement familier et un peu de code plus tard, le sou tombe: c'est le vôtre. Ce n'est pas seulement une considération Open Source ...
Robbie Dee
@Robbie Dee. pouvez-vous clarifier un peu plus? Je ne te comprends pas. En effet, j'ai une courte carrière de programmeur et c'est la raison du tag junior-programmer.
py_script
2
@py_script Je faisais juste remarquer que vous pouvez tout aussi facilement trouver votre propre code des années plus tard (et être déconcerté par celui-ci) que quelqu'un d'autre. Donc, si vous commencez avec de bonnes habitudes, cela vous rapportera des dividendes plus tard.
Robbie Dee

Réponses:

26

Vous n'avez pas besoin de penser à l'avenir, d'être simplement très clair sur ce qui a été fait et d'être très clair sur ce que vous faites en ce moment.

Les sous-programmes doivent dire ce qu'ils font, faire ce qu'ils disent et ne pas avoir de dépendances cachées. Ensuite, quelqu'un qui les appelle peut plus facilement raisonner sur ce qu'ils vont faire.

Évitez l'état global. (Variables, singletons, etc.) Plus vous devez avoir d'états dans votre tête pour comprendre ce que font les choses, plus il est difficile de comprendre ce qui est censé se produire et de trouver les cas limites.

Écrire des tests unitaires. Les tests unitaires sont parfaits pour capturer le comportement réel du code que vous venez d'écrire, plutôt que le comportement idéal que vous espérez trouver.

Raccourcissez votre cycle d'édition / compilation / test. Lorsque vous ajoutez un gros morceau de code et testez mal, il y a de fortes chances qu'il se comporte différemment de ce que vous pensez. Ensuite, vous le "corrigez" avec des changements aléatoires, et vous avez la bonne réponse pour le moment, mais vous ne savez pas comment cela s'est réellement produit. Vous programmez maintenant par hasard. Mais lorsque vous ajoutez 5 lignes, puis testez, les chances que vous obteniez la bonne réponse, car cela fonctionne comme si vous pensiez que cela fonctionne, sont bien meilleures. Je peux dire par expérience que 5 morceaux de 10 lignes chacun, testés individuellement, est une bête très différente de 50 lignes de code testées en même temps.

Refactor impitoyablement. Plusieurs fois, j'ai repéré un refactorisateur qui rendra mon code un peu plus simple mais prendra un tas de travail que je ne voulais pas faire. Après avoir commencé à m'attaquer délibérément à ces refacteurs en tant que priorité, j'ai constaté que cela se rentabilise généralement en un mois. Mais notez la clé, les refacteurs sur lesquels je me concentre sont ceux qui rendent la vie quotidienne plus simple, et non ceux qui répondent à une esthétique arbitraire meilleure ou plus générale. Ces refactors avec lesquels j'ai appris à être beaucoup plus prudent.

Aucune de ces choses ne nécessite une planification préalable. Mais ils facilitent tous la compréhension de votre code existant et facilitent donc la mise en œuvre délibérée de votre prochain petit morceau.

btilly
la source
Merci, vraiment une bonne réponse. Je pense que la partie sur le cycle est vraiment précieuse. Pouvez-vous me donner des exemples de refactorisation que vous devriez faire, mais qui prendront beaucoup de temps à mettre en œuvre et qui peuvent décourager quelqu'un?
py_script
1
J'ai plein d'exemples. Dans un projet C ++, j'ai créé des classes sans méthodes de stringification. Une fois que je les ai créés, le débogage est devenu plus facile et le développement s'est accéléré. Dans un projet Perl, nous avions un hachage de configuration où chaque développeur avait sa propre copie modifiée de chaque nouvelle configuration. L'ajout de paramètres de configuration était pénible car vous deviez modifier la configuration de chaque développeur. J'ai écrit un système de modèles pour les hachages, et c'est devenu plus facile. Dans un système de rapports, j'ai ajouté une fonctionnalité pour afficher les résultats intermédiaires. Mon développement s'est accéléré, et j'ai même reçu un rapport de bug utilisateur ...
btilly
1
où le département des finances avait tracé ma logique et trouvé la requête exacte sur laquelle je me suis trompé et quel était mon bug. (J'écrasais accidentellement des lignes en double avec l' UNIONendroit dont j'avais besoin UNION ALL.) Et ainsi de suite.
btilly
1
Dans un mois? Je trouve généralement que chaque fois que je touche du code, je pense qu'il devrait être refactorisé, mais ne l'ai pas refactorisé, cela prend presque autant de temps qu'il le ferait pour le refactoriser.
Amy Blankenship
1
@AmyBlankenship Oui. Dans un mois. Parfois ridiculement à l'intérieur, parfois non. Le fichier de configuration que j'ai mentionné ci-dessus est un exemple de "parfois non". Il m'a fallu quelques jours pour écrire, documenter et tester un nouveau moteur de modèle. Réécrivez ensuite la configuration existante pour l'utiliser, et soyez beaucoup plus court, mais produisez la même structure de données exacte. (C'était la partie la plus difficile du projet.) Donc ... jours perdus, sans aucun résultat visible. Pourtant, l'effort a porté ses fruits en un peu moins d'un mois, mais porte intérêt depuis.
btilly
41

Il se résume à peu près à ne pas deviner . Surtout les nouveaux programmeurs le font, mais j'ai vu des vétérans le faire aussi, car ils pensent que cela fait gagner du temps à la recherche. Quelque chose ne fonctionne pas, vous ajoutez donc un +1ou un -1, changez un trueen un falseou vice versa, réorganisez certaines instructions, ajoutez ou modifiez des délais, modifiez les priorités des threads et d'autres petites transformations, testez essentiellement des permutations aléatoires jusqu'à ce que cela fonctionne.

Cela s'applique principalement à la modification du code existant, mais est également un facteur dans le nouveau code, car personne ne part vraiment de zéro. Vous vous basez toujours sur des bibliothèques, des systèmes d'exploitation ou au moins des architectures de processeur standard.

En d'autres termes, vous n'avez pas terminé tant que vous ne savez pas pourquoi votre correctif fonctionne. Le chemin que vous empruntez pour y arriver importe peu. Même des permutations aléatoires peuvent parfois être utiles pour réduire un bogue difficile à diagnostiquer, tant que vous prenez le temps par la suite de vous demander: "D'accord, changer vrai en faux l'a corrigé, mais pourquoi?"

Karl Bielefeldt
la source
1
Excellent point +1. Cela ne s'applique nulle part plus que les corrections de code ...
Robbie Dee
C'est vrai pour moi aussi, malheureusement. Pour être honnête, il y a parfois des difficultés objectives comme le manque de documentation. Aujourd'hui, je corrigeais un peu mon code, et je suis venu au fait que je ne savais pas à quoi un paramètre est utile, faute de documentation. Nous savons simplement que c'est un chiffre.
py_script
J'admet. Lorsque vous faites face au syndrome du cure-dent penché, il peut être plus facile d'empiler les \ s jusqu'à ce qu'il fonctionne que de déterminer le nombre de couches d'évasion
auxquelles
16

Le commentaire le plus effrayant que j'ai jamais rencontré dans un programme était

Ne touche pas ça. Ça marche. Nous ne savons pas comment ni pourquoi, mais cela fonctionne. 1

et c'est effrayant juste parce qu'il reconnaît que le morceau de code était le résultat d'une programmation par coïncidence .

Pour éviter la programmation par coïncidence, vous devriez être en mesure d'expliquer (à un collègue, vous-même ou un canard en caoutchouc ) exactement ce que fait le code et pourquoi il fonctionne. Les puces pour la programmation délibérément aident principalement à progresser vers cet objectif de pouvoir expliquer le code.


1 Pour le contexte, ce commentaire est apparu dans le code qui gère les changements de contexte dans un système d'exploitation primitif. Le code était déjà en production depuis plusieurs années lorsque je l'ai rencontré.

Bart van Ingen Schenau
la source
2
cela s'appelle également le codage du poulet vaudou c2.com/cgi/wiki?VoodooChickenCoding
moinsSeven
1
Même si le codeur pense que c'est vrai, ce genre de commentaire est extrêmement inutile. Le code était peut-être complexe, mais si vous lisiez le code autrement, vous pourriez penser qu'il était simple. Tout ce que le commentaire fait, c'est augmenter la paranoïa!
Robbie Dee
3
Cela peut également se produire lors de la maintenance d'une ancienne base de code, dans un langage avec lequel la plupart des développeurs actuels ne sont pas très à l'aise.
pcurry
Le canard en caoutchouc n'est donc pas uniquement destiné au débogage. Bien ... Je pense que nous travaillons sur la même entreprise, nous avons de nombreux commentaires comme celui-ci: P
py_script
il y a des situations où un correctif fonctionne simplement en raison d'un problème dans une API, et il n'y a pas de correctif meilleur et logique. Le débogage de certaines bibliothèques tierces compilées peut aller aussi loin que le débogage du noyau. Et même si vous avez trouvé le problème, après des heures de débogage, vous ne pouvez pas faire grand-chose. Vous abordez donc le problème différemment. Vous adoptez le modèle "boîte noire" qui vous oblige à programmer par coïncidence. Vous jouez avec le comportement étrange de la boîte noire et si vous parvenez à le faire fonctionner comme vous le souhaitez, GREAT, ajoutez un commentaire avec "magic do not touch" et passez à autre chose.
Radu Simionescu
7

Pour les nouveaux programmeurs, la partie la plus importante pour surmonter cela est de vraiment comprendre ce qu'ils font.

Dans de nombreux domaines, lorsque vous ne savez pas comment faire quelque chose, vous vous contentez d'essais et d'erreurs. Essayer quelque chose; si ça marche, super, sinon, essayez autre chose.

En programmation, en particulier lorsque vous utilisez un langage qui a le concept de comportement indéfini (comme C ou C ++), cette approche ne fonctionne tout simplement pas, car le succès n'est plus une décision booléenne. Vous pouvez avoir des choses qui "fonctionnent", qui fonctionnent parfois, qui fonctionnent pour certaines entrées mais pas pour d'autres.

J'ai parfois enseigné de nouveaux programmeurs, et la tendance à essayer de taper des choses aléatoires pour voir si cela fonctionne est courante. Ils tapaient une ligne, puis se tournaient vers moi et me demandaient: "Est-ce que cela fonctionnerait de cette façon?" alors qu'il était clair qu'ils n'avaient absolument aucune idée si c'était possible.

La leçon à retenir est qu'en tant que nouveau programmeur, vous devez vraiment être en mesure d'expliquer ce que fait votre code. Vous devez apprendre à lire le code, pas seulement à l'écrire.

(Bien sûr, cela s'applique également aux programmeurs chevronnés, mais mon expérience ici a été principalement avec de nouveaux débutants complets.)

Sebastian Redl
la source
<< Vous devez apprendre à lire le code, pas seulement à l'écrire. >> Donc, à propos de ma question initiale, pensez-vous que cela m'aidera à ajouter des fonctionnalités sur des projets open source?
py_script
2

Il est beaucoup trop facile de coder, tester et corriger "sur la ligne de course". Vous avez des fonctionnalités qui, étant donné X & Y, produisent Z. Mais que se passe-t-il si X est corrompu et Y n'est pas disponible? Et si vous ne parvenez pas à sortir Z? Gardez constamment à l'esprit ce qui peut mal tourner et notez-le pour le cycle de test.

Gardez vos routines courtes et descriptives - le meilleur code nécessite peu (le cas échéant) de commentaires.

Les noms de méthode, de classe et de variable significatifs contribuent grandement à la lisibilité.

Si vous rencontrez une odeur de code, arrêtez. Il est peu probable que cette odeur disparaisse et un petit effort maintenant pourrait vous faire économiser énormément de chagrin plus tard.

Incluez des tests dans votre processus de développement. Je préconiserais l'utilisation du BDD plutôt que du TDD car cela vous oblige à décrire ce que vous visez à réaliser plutôt que de vous fier aveuglément à une série de tests qui pourraient vous donner un faux sentiment de sécurité.

Résistez à l'envie d'ajouter des fonctionnalités intéressantes (sauf s'il s'agit de votre propre projet pour animaux de compagnie). Tout code supplémentaire doit être conçu, écrit, testé et maintenu. Si ce n'est pas requis par le client / l'entreprise - vous risquez de devenir un énorme drain sur vos ressources.

Robbie Dee
la source