4 points par GN⁺ 2023-12-09 | 1 commentaires | Partager sur WhatsApp
  • À travers l’expérience d’une réécriture du code de redimensionnement d’un éditeur graphique centrée sur la suppression des doublons, finalement annulée, l’article montre qu’une obsession du Clean Code peut nuire à la facilité réelle de changement
  • L’implémentation existante répétait des formules pour chaque poignée de Rectangle, Oval, Header et TextBlock ; la nouvelle implémentation cherchait à éliminer les doublons en séparant directions et formes pour les combiner
  • Juste après le refactoring, la taille du code avait été réduite de moitié et les points de modification regroupés à un seul endroit, mais par la suite, si des comportements spécifiques par forme et par poignée devenaient nécessaires, l’abstraction aurait au contraire pu devenir un frein
  • Le problème le plus important n’était pas le jugement technique, mais le fait d’avoir directement commité sur master sans consulter le collègue qui avait écrit le code d’origine, ce qui peut nuire à la construction de la confiance dans l’équipe
  • La « propreté » n’est pas un objectif, mais seulement un outil pour gérer des systèmes complexes ; plus que l’apparence du code, ce qui compte est la capacité de l’équipe à réagir au changement ensemble

La répétition dans le code de redimensionnement d’un éditeur graphique

  • Le canevas d’un éditeur graphique contenait du code permettant de redimensionner des formes comme des rectangles et des ellipses à l’aide de petites poignées situées sur les bords
  • L’implémentation fonctionnait, mais les formules calculant la position et la taille étaient répétées pour chaque forme et chaque direction de poignée
    • Rectangle avait des méthodes comme resizeTopLeft, resizeTopRight, resizeBottomLeft, resizeBottomRight
    • Oval avait des méthodes comme resizeLeft, resizeRight, resizeTop, resizeBottom
    • Header et TextBlock avaient aussi chacun leurs propres méthodes de redimensionnement
  • Quand l’utilisateur maintenait Shift, il fallait aussi gérer la conservation des proportions pendant le redimensionnement, ce qui ajoutait encore plus de formules

Une abstraction pour supprimer les doublons

  • Les répétitions semblaient se répartir selon deux axes
    • Elles se répétaient entre poignées de même direction : Oval.resizeLeft() et Header.resizeLeft() correspondent toutes deux à l’action de faire glisser la poignée gauche
    • Elles se répétaient entre méthodes d’une même forme : les différentes méthodes de Oval manipulent toutes une ellipse
  • La nouvelle structure divisait le code en Directions et Shapes afin de combiner les comportements par direction et les comportements par forme
    • Directions contenait top, left, bottom, right
    • Shapes contenait Oval, Rectangle
    • createHandle et createBox combinaient poignées et formes
  • Avec cette approche, on créait des configurations de poignées comme fourCorners, fourSides, twoSides, puis on les utilisait pour créer Rectangle, Oval, Header, TextBlock

L’illusion juste après le refactoring

  • Après le refactoring, la taille totale du code avait été réduite de moitié et les doublons semblaient avoir disparu
  • On pensait qu’en cas de changement du comportement d’une direction ou d’une forme donnée, il suffirait de modifier un seul endroit plutôt que plusieurs méthodes
  • Tard dans la nuit, le changement a été commité sur master, avec l’impression d’avoir proprement démêlé le code désordonné d’un collègue

Le problème révélé le lendemain

  • Le lendemain, le responsable a demandé d’annuler le changement ; à l’époque, comme le nouveau code semblait plus propre que l’ancien, c’était difficile à accepter
  • Avec le recul, ce jugement était correct pour deux raisons
    • Le code avait été réécrit et commité sans en parler à la personne qui avait écrit le code d’origine
    • En échange d’une réduction des doublons, on avait sacrifié la facilité d’évolution face aux changements de besoins
  • Une équipe d’ingénierie saine doit continuellement construire la confiance, et une réécriture massive du code d’un collègue peut porter un coup important à la collaboration

Le coût d’une mauvaise abstraction

  • Par la suite, de nombreux cas particuliers et comportements spécifiques sont devenus nécessaires pour plusieurs poignées de plusieurs formes
  • Dans la nouvelle structure abstraite, prendre en charge ces besoins aurait pu rendre le code plusieurs fois plus complexe
  • À l’inverse, l’ancienne structure « désordonnée » séparait chaque méthode, ce qui rendait ce type de changement relativement facile à appliquer
  • Supprimer les doublons n’est pas gratuit : on peut gagner une capacité tout en en perdant une autre

Repenser la « propreté »

  • Beaucoup de développeurs peuvent passer par une période d’obsession pour le « Clean Code » et la suppression des doublons
  • Quand on manque de confiance dans le code que l’on écrit, il est facile d’associer sa fierté à des éléments mesurables comme des règles de lint strictes, des conventions de nommage, une structure de fichiers ou l’absence de doublons
  • Lorsqu’on apprend à créer des abstractions, on peut être tenté, chaque fois que l’on voit du code répétitif, de croire que l’abstraction est une vertu et de l’appliquer
  • Il faut réfléchir en profondeur au lien entre le sentiment de « propreté » ou de « saleté » et les résultats réels en ingénierie
  • Ce qui compte, ce n’est pas seulement l’apparence du code, mais aussi la manière dont le code évolue et se modifie avec les personnes de l’équipe

Quand il faut lâcher le Clean Code

  • Le Clean Code n’est pas un objectif, mais une tentative de comprendre des systèmes complexes
  • Il peut servir de mécanisme de défense pour s’orienter lorsqu’on n’est pas sûr de l’impact qu’un changement aura dans une base de code
  • Au début, il peut être agréable de simplifier du code complexe en extrayant des fonctions ou en refactorant des classes
  • Mais il ne faut pas s’arrêter là et devenir un « fanatique du Clean Code »
  • Laissez le Clean Code vous guider, mais à un certain moment, il faut savoir le lâcher

1 commentaires

 
GN⁺ 2023-12-09
Avis de Hacker News
  • La duplication de code peut parfois être acceptable, mais cela ne prouve pas pour autant que le Clean Code soit mauvais
    On a l’impression que le refactoring est allé un peu trop loin ; se contenter d’extraire « 10 lignes de maths répétées » dans une fonction aurait été plus propre
    Le collègue qui n’a pas transformé ces 10 lignes en fonction n’a pas non plus fait du bon travail, donc à sa place j’aurais refusé la PR, mais je ne l’aurais pas réécrite moi-même
    Réécrire la PR de quelqu’un d’autre est une façon rapide et insultante de fermer toute discussion, et on ignore souvent le contexte dans lequel le code a été écrit
    S’il n’y a pas de standards de codage, il faut relire et refuser la PR ; si on laisse passer des commits au motif que « le Clean Code n’est pas important », on se retrouve en quelques mois avec une codebase sur laquelle plus personne n’a envie de travailler
    Ce qu’il faut en retenir ici, ce n’est pas le Clean Code, mais la communication et la considération

    • Réécrire directement du code fonctionnel écrit par quelqu’un d’autre n’est pas seulement une insulte envers son auteur ; c’est aussi un comportement non professionnel, car on perd du temps à modifier du code dont quelqu’un d’autre assure la maintenance
      Cela prive aussi l’auteur initial de l’occasion d’apprendre concrètement en quoi du code de faible qualité fait perdre du temps
      Même si l’approche Clean Code est meilleure, le point central ici n’est pas le code, mais le fait d’avoir gaspillé son propre temps sans fournir de feedback utile
      La meilleure issue aurait été que l’auteur initial hausse son niveau d’exigence, tandis que l’autre personne se consacre à d’autres tâches qui avaient réellement besoin d’être reprises. Autrement dit, la bonne réponse était la revue de code
    • La leçon ressemble plutôt à « n’appliquons pas des règles à l’excès sans tenir compte des compromis propres au contexte », et c’est une leçon qui prend du temps à apprendre
    • Après plus de 20 ans, je constate que l’idée « c’est nul parce que 10 lignes répétées n’ont pas été extraites dans une fonction » vient généralement de personnes qui se focalisent sur les mauvaises choses dans une codebase
      Ces personnes finissent souvent par résoudre des non-problèmes au lieu des vrais problèmes, tout en créant de futurs problèmes
      Lors d’une première implémentation, il vaut mieux ne pas abstraire un tout petit morceau de code de ce type, de l’ordre de 10 lignes. Un code qui fait exactement cette chose-là sera facile à modifier plus tard, et abstraire 10 lignes pour un bénéfice non démontré n’a pas grand intérêt
      Clean Code n’a aucune importance, et la plupart des règles ou « principes » du même genre non plus. Ce sont plutôt des choses auxquelles s’accrochent les gens qui veulent justifier l’accumulation, au fil du temps, d’une structure en nid d’abeille dans la codebase
      Au final, on obtient facilement du code trop abstrait, difficile à modifier, lent et difficile à comprendre
      S’il faut un principe, il suffit de veiller à exprimer clairement et de façon facilement modifiable ce que le programme fait réellement : des transformations de données
      La plupart des principes SOLID n’aident pas à voir ce que sont ces transformations de données ni comment elles se produisent ; ils peuvent même gêner cette compréhension, et sont donc globalement peu utiles pour comprendre de vrais programmes
    • Je trouve que la formulation de l’article d’origine n’est pas bonne
      À voir le refactoring proposé, ces 10 lignes n’étaient en fait pas identiques, seulement similaires
      La formule pour redimensionner le coin supérieur gauche d’un rectangle et celle pour modifier le coin inférieur droit d’une ellipse sont différentes. En surface elles se ressemblent, mais à certains endroits il y a par exemple un - au lieu d’un +
      Dans ce cas, la complexité se trouve dans la géométrie elle-même ; l’« abstraire » ne fait que déplacer le désordre ailleurs, sans le faire disparaître fondamentalement
      Il pourrait exister une abstraction mathématique ingénieuse, comme un système numérique spécial, mais si elle n’est pas intégrée au langage, le coût d’implémenter des opérations spéciales avec des entiers de base risque fort de dépasser les bénéfices obtenus
  • Le « Clean Code » a besoin d’un rebranding
    En plus de 25 ans de carrière, j’ai vu une opposition sans fin aux design patterns, à l’abstraction, à la suppression de duplication, etc.
    Les raisons invoquées sont toujours les mêmes : l’abstraction rend le code plus complexe, on n’a pas le temps d’écrire du code propre, tout cela n’est qu’affaire de goût et d’opinion
    Mais l’objectif du Clean Code est de rendre le code plus simple et plus maintenable quand les exigences changent
    La valeur du logiciel tient au fait qu’il peut évoluer avec le temps. Sinon, on utiliserait des circuits fixes, bien plus faciles et moins chers à implémenter et à maintenir
    Si le refactoring n’a pas atteint cet objectif, et si le manager peut expliquer de manière convaincante qu’il est devenu plus difficile à maintenir face à de futurs changements d’exigences, alors ce code n’était pas « clean »
    Le fait de ne pas en avoir discuté avec l’auteur initial relève des procédures et de la courtoisie, pas du Clean Code. Agir de manière impolie et unilatérale ne fait pas disparaître la valeur du Clean Code

    • Je pense que l’opposition vise les prescriptions trop absolues
      Le célèbre livre Clean Code présente « l’absence de duplication » comme une condition d’un système bien conçu, et décrit la duplication comme l’ennemi principal du travail supplémentaire, du risque supplémentaire et de la complexité inutile
      Selon cette définition, supprimer la duplication devient synonyme de simplification, et c’est faux
      Supprimer la duplication, c’est introduire une dépendance. Si cette dépendance modélise bien le problème, elle peut devenir une bonne abstraction et une simplification ; sinon, ce n’est qu’une compression déguisée en abstraction
      À titre de référence, cette citation vient de la mention, dans Clean Code, du Simple Design de Kent Beck
    • « Clean Code » s’emploie dans deux sens
      L’un désigne le sentiment subjectif qu’un code est bon ; l’autre le fait de suivre le livre Clean Code et les recommandations associées
      De plus en plus de gens estiment que ces deux choses divergent largement. Suivre certaines recommandations du livre peut même produire du code qui n’a rien de propre
      Dans un monde idéal, tout le monde voudrait écrire du code propre, mais de moins en moins de personnes ont le sentiment que Clean Code est une bonne façon d’atteindre cet objectif
      En particulier, l’excès d’abstraction et la suppression de duplication sont devenus, pour beaucoup, des sources de code désordonné
    • Ce que j’ai appris dans l’industrie, c’est que même le code écrit de la manière la plus propre ne tient pas longtemps face aux exigences produit
      Parfois, la dette technique est encodée dans les exigences produit elles-mêmes ; on ne peut pas la corriger par du refactoring ou des bonnes pratiques, seulement réduire la douleur
    • Cela ressemble à un raisonnement du type « pas un vrai Écossais »
      L’objectif du Clean Code et les résultats de sa mise en pratique sont très différents. Les gens prédisent généralement mal l’état futur du code, même avec les exigences sous les yeux
      Le Clean Code invite à poser la terrible question « et si ? », qui devient facilement une boîte de Pandore repoussant indéfiniment la sortie du produit
      Il vaut presque toujours mieux coder en fonction de ce que l’on sait, et laisser quelqu’un qui a de l’expérience du domaine guider l’architecture
    • Pour dresser une liste d’alternatives : la plupart des abstractions que nous créons sont mauvaises, y compris généralement celles que nous pensons bonnes
      Beaucoup de bonnes abstractions se trouvent déjà dans des bibliothèques. Malheureusement, bien sûr, ce n’est pas toujours le cas
      Les mauvaises abstractions dévorent du temps ; et comme le code sert aussi à communiquer avec les autres membres de l’équipe, la façon dont ceux-ci reçoivent cette communication compte bien plus que ma propre manière de penser
  • Un collègue avait écrit beaucoup de code par copier-coller, et vous l’avez refactorisé après le commit
    Le collègue s’est plaint à votre supérieur, celui-ci vous a réprimandé, et vous en avez en quelque sorte conclu que, la prochaine fois, vous laisseriez du bazar dans la codebase
    Il y a bien une leçon à en tirer, mais cela ne veut pas dire que le copier-coller vaut mieux que l’écriture d’une fonction
    Le refactoring aurait dû être proposé comme une modification séparée, en taguant l’auteur initial pour obtenir sa review
    Cela dit, le fait que le collègue soit passé par le supérieur au lieu de venir vous en parler directement, et que le supérieur vous ait demandé de revenir en arrière, est un assez mauvais signal

    • À sa place, j’aurais envoyé un message privé Slack au collègue du genre : « Ces fonctions répètent chacune 10 lignes de maths ; est-ce que ce ne serait pas mieux de les extraire dans une fonction séparée ? »
      Les réponses possibles seraient « Non, parce que… » ou, plus rarement, « Oui, en effet, je le ferai demain »
      Sans bruit, tout le monde évite le problème, et quelqu’un peut éventuellement apprendre quelque chose
    • S’il s’agit de copier-coller paresseux, utiliser une fonction pour les répétitions, autrement dit abstraire, est préférable du point de vue DRY
      Mais l’auteur a ajouté un contexte important. Il a échangé la possibilité de changements de besoins contre une réduction de la duplication, puis plus tard il a fallu beaucoup de cas particuliers et de comportements propres à chaque poignée de forme
      Cela montre la différence entre DRY accidentel et DRY essentiel
      Les bibliothèques A et B semblent souvent cohérentes entre elles, ce qui conduit à du code identique, mais en réalité cela peut n’être qu’une coïncidence
      Même si un wrapper autour de lib A et un wrapper autour de lib B contiennent le même code, s’ils encapsulent des choses totalement différentes, les abstraire donnera un mauvais résultat
      C’est pareil pour « 15 ». Si « toutes les listes doivent toujours afficher 15 éléments », alors abstraire 15 est correct ; mais si ce sont séparément « les 15 meilleurs morceaux de la page d’accueil » et « les 15 commentaires par défaut sous une chanson », les regrouper autour de ce 15 est un plus mauvais choix
    • Le plus gros signal d’alarme, c’est d’avoir modifié la contribution d’un collègue sans aucune communication
      Le deuxième signal d’alarme, c’est que le refactoring lui-même n’est pas non plus un bon refactoring. Il manque de modularité, reste couplé, et ne fait que réduire un peu la duplication
    • Taguer l’auteur initial peut aussi créer des problèmes
      Cela peut sonner comme : « Ton code n’était pas terrible, alors je l’ai réécrit. Un problème ? »
      La meilleure solution serait probablement de demander des explications sous forme de « review tardive », de proposer une solution, et d’espérer que l’auteur initial ne l’ignore pas simplement
    • À la fin de l’article, il y avait un passage important : lors de changements ultérieurs dans la codebase, il a fallu traiter spécialement les poignées selon les formes, ce qui était difficile dans la structure refactorisée mais facile dans le code répétitif
      Le point clé est de reconnaître que le code changera à l’avenir. Les exigences changent, et le code concerné aussi
      Si l’on crée une abstraction qui limite cette capacité de changement, on nuit à la capacité de son futur soi à adapter la codebase
      En particulier, on s’attache émotionnellement aux abstractions dans lesquelles on a investi beaucoup de temps et d’efforts, et on finit parfois par perdre encore plus de temps à faire entrer les changements dans une solution élégante
      KISS est la bonne approche
  • Ce cas ressemble au problème « personne n’aime les gens qui se la jouent »
    Cela arrive souvent, surtout quand un développeur junior n’a pas encore appris que le travail ne se passe pas seulement entre lui et l’ordinateur, mais entre lui, l’ordinateur et d’autres personnes
    Une version plus propre aurait très probablement été acceptée si une vraie discussion avait eu lieu avec l’auteur initial et qu’un meilleur moyen avait été convenu
    Le titre de l’article aurait plutôt dû être J’ai appris à travailler en équipe que « Adieu, Clean Code »

    • L’expression « personne n’aime les gens qui se la jouent » saisit bien l’environnement de développement
      Le code plus complexe a été perçu comme une menace, et peut-être comme un concurrent à faire rentrer dans le rang via la chaîne hiérarchique
      Si vous êtes junior, il faut comprendre que cette phrase dépend du contexte. Dans beaucoup d’entreprises, la simplicité compte plus que la flexibilité, on évite les fonctionnalités de langage déroutantes, et on préfère les approches qui demandent de longues heures devant le clavier, ainsi que les langages qui s’y prêtent
      À l’inverse, une philosophie qui valorise l’apprentissage continu, le craftsmanship logiciel et la réflexion sur les processus entre totalement en conflit avec ce genre d’environnement
      Les tentatives de s’améliorer ne sont pas populaires auprès des gens qui ne grandissent pas
      Les deux sont des choix de vie valables. Le logiciel peut être un endroit commode où travailler 7 heures puis rentrer chez soi, comme un monde d’exploration dont les seules limites sont l’imagination et l’intelligence
      Si vous voulez découvrir de nouvelles choses, méfiez-vous de l’idée de passer des décennies avec des personnes qui craignent l’inconnu
      En plus court : si vos incitations ne sont pas alignées avec celles de vos collègues, envisagez de partir
    • Je trouve que le titre convient
      Cet article a des raisons de rester apprécié longtemps, et il a ouvert les yeux de nombreux développeurs qui tombaient dans le même piège
      Même en travaillant seul, on peut surconcevoir une abstraction élégante et se faire mordre par elle plus tard
    • Ce genre de problème est généralement plus souvent commis par des seniors grisés par le pouvoir que par des juniors
  • Le problème est que les changements sont committés directement dans la branche main sans review
    Toute modification de code devrait faire l’objet d’une review par les pairs, que ce soit via du pair programming ou une PR traditionnelle, afin d’éviter ce type d’escalade
    Le supérieur était un mauvais manager. Au lieu de réprimander, il aurait dû remarquer qu’il manquait un bon processus
    Dans une équipe avec une culture de code review, même si une nouvelle recrue trop enthousiaste tentait ce genre de chose, un collègue aurait dit « merci, mais ce n’est pas ça » ou « bonne idée, mais essayons plutôt comme ceci »
    À part ça, je pense qu’il ne faut pas refactoriser du code sans bonne raison. Même du mauvais code peut être du code qu’on n’a presque jamais besoin de modifier
    Le meilleur moment pour refactoriser, c’est quand on doit changer le comportement et qu’on se rend compte que la structure existante rend cela trop difficile
    Malheureusement, ici, il semble que la mauvaise leçon ait été retenue

    • La review de code après merge est aussi parfaitement acceptable, et même meilleure à certains égards
  • Je ne comprends pas pourquoi tout le monde ne parle que de ce qu’il aurait dû faire à la place. C’est une leçon tout à fait légitime
    En finance, on manipule en permanence des produits plus ou moins similaires, sans être exactement les mêmes. Les options sur actions et les options de change sont toutes deux des options, ont un prix d’exercice et sont valorisées avec une certaine version de Black-Scholes, mais en pratique elles sont très différentes
    Pour une nouvelle personne dans l’équipe, la tentation est forte de vouloir mutualiser beaucoup de comportements, et nous nous retrouvons souvent dans la position du supérieur de l’article original, à dire à de jeunes développeurs de se calmer et de garder les choses séparées
    Il ne s’agit pas de dire adieu au clean code, mais de dire qu’il faut éviter la sur-abstraction pour pouvoir réellement conserver un code propre

    • L’exemple de la finance est juste, mais ici on traite de géométrie
      Ce que font des poignées génériques, ce sont des transformations affines — translation, rotation, mise à l’échelle, réflexion — et, dans le cas d’une mise à l’échelle uniforme, une transformation de similitude. Il est garanti que cela fonctionne pour n’importe quelle forme
      Le vrai problème est qu’il ne faut pas abstraire ce que l’on ne comprend pas suffisamment. Surtout si l’on ne sait pas quelles possibilités apparaîtront à l’avenir
      Par exemple, si l’on pense que les ovales se limitent aux cercles et aux ellipses, on risque d’être assez surpris à l’arrivée du premier « vrai » ovale
    • Je comprends l’idée du billet, mais le titre peut facilement se lire comme une excuse pour justifier du code réellement brouillon
      Comme l’auteur ne distingue pas vraiment le code brouillon du code « non clean », les autres essaient de faire cette distinction
    • On voit assez bien que tout le monde ici, sans contexte en dehors de quelques lignes de pseudocode, a l’impression d’en savoir plus que l’équipe de l’époque et que l’auteur
      DRY a de la valeur, mais l’abstraction a un coût. C’est toujours un compromis
  • L’une des raisons pour lesquelles j’aime des langages comme Haskell, c’est que le maximalisme de l’abstraction n’y est pas situé au niveau du codage, mais au niveau du langage
    Tout le monde est d’accord sur ce qu’un Applicative doit faire. Le défi se déplace plus à droite dans le pipeline qui va de l’idée au code : trouver l’Applicative adapté au problème
    La plupart des design patterns orientés objet ont, dans ce genre d’environnement, des équivalents naturels et bien définis, ce qui aide à réduire les abstractions propres à chaque projet
    Pour les débutants, le coût initial d’apprentissage du langage est élevé, et il arrive souvent qu’en ouvrant du code Haskell d’un autre projet on tombe sur une douzaine d’extensions du compilateur jamais vues qu’il faut comprendre
    Mais comme ces extensions du compilateur peuvent ensuite être réutilisées dans tous les projets, j’y vois tout de même un gain net
    Pour les experts, l’inconvénient est qu’il faut un doctorat pour avoir une idée de la quantité de RAM que cats va consommer

    • Si l’on sait ce que font Applicative et Monad, c’est parce qu’il existe des lois mathématiques
      Lorsqu’un code utilise Applicative ou Monad, ces lois permettent d’en connaître le comportement
      Les design patterns orientés objet n’ont pas de telles lois. Ce sont des concepts ou des patterns définis de manière lâche, ouverts aux interprétations personnelles et aux personnalisations, mais sans lois concrètes qui disent précisément ce qu’ils sont ni quel comportement ou quelle sortie il faut en attendre
    • En tant qu’utilisateur de Haskell, je suis d’accord pour dire que les capacités d’abstraction du langage sont excellentes
      Cela dit, pour l’exemple du billet de blog, je dois dire que suivre la voie du clean code m’a vraiment souvent amené à me tirer une balle dans le pied pour l’avenir
      Si, plus tard, il faut étudier l’abstraction pour la comprendre à nouveau, alors cette abstraction n’est pas bénéfique
    • L’article original aurait sans doute gagné à utiliser davantage de principes orientés objet, sauf si l’équipe les rejetait globalement. Si c’était le cas, je pense que j’irais travailler ailleurs
      Là où l’orientation objet s’applique le plus naturellement, c’est dans les widgets GUI et l’implémentation d’éditeurs
      La règle qui unifie les autres règles de l’orienté objet, c’est ne demandez pas, donnez des ordres
      Dans cet exemple, les poignées seraient partagées de manière connue, et il y aurait des points de contrôle propres à chaque forme. Quand une poignée de boîte change, on peut appeler updateControlPoints(changedHandle), et quand un point de contrôle change, on peut appeler updateBoxHandles(changedControlPoint)
      D’autres séparations propres seraient possibles, mais c’est ainsi que je ferais
    • On peut être d’accord sur ce qu’un Applicative doit faire
      Mais le choix de la bibliothèque de parsing, de Data.Text.Strict ou Data.Text.Lazy, du degré d’usage du style point-free, de . et >>> ou d’autres combinateurs, ainsi que des extensions du langage à utiliser, fait beaucoup moins consensus
      J’aime Haskell, mais il rencontre des problèmes similaires à ceux des autres langages. Les gens ne parviennent vraiment pas à s’accorder sur la meilleure façon de faire les choses
    • En allant un cran plus loin, il suffit d’utiliser Rust avec des types affines
      On obtient le système de types de Haskell et des performances prévisibles
  • Il aurait suffi de déplacer la partie mathématique répétée dans une fonction séparée, puis de l’appeler depuis les fonctions de redimensionnement
    Si les fonctions de redimensionnement elles-mêmes semblaient répétitives, c’est parce qu’elles constituaient une interface, et le refactoring qui les supprimait était donc une erreur
    Mais les calculs effectués par ces fonctions étaient une cible tout à fait valable pour nettoyer le code

    • Oui. Cela paraît être la direction évidente, et c’est généralement ce que je fais aussi
      On se retrouve au final avec plein de petites fonctions pures, mais ce n’est pas un problème, car les tests et le refactoring deviennent faciles
    • Surtout lorsqu’il s’agit de refactoring, il vaut généralement mieux, quand on travaille en équipe, procéder par étapes
      Il existe de nombreuses façons et de nombreux patterns pour résoudre un même problème, mais démolir une interface existante utilisée par d’autres n’est pas un bon point de départ
      L’avantage de l’abstraction est de pouvoir rendre l’intérieur plus flexible sans nuire au reste
      Une bonne première étape consiste, comme indiqué, à créer en dessous des fonctions mathématiques plus flexibles tout en laissant le reste intact
      Ensuite, on peut discuter plus en profondeur avec l’équipe de la manière de communiquer autour d’une interface fournissant une façade claire, tout en abstraisant l’implémentation
  • Ce qui est décrit ici ressemble à une mauvaise abstraction
    Le vrai problème, c’est que les gens conçoivent à partir de seulement deux ou trois exemples, sans vérifier si c’est suffisamment général pour accueillir des usages futurs.

    • Une approche particulièrement saine consiste à n’abstraire les parties communes qu’en fonctions très simples
      Il faut abstraire du bas vers le haut, pas du haut vers le bas
      Il n’est pas nécessaire de créer un point unique où tout se passe et qui gère 2^n combinaisons
      On peut tout de même réduire la répétition, pouvoir modifier à un seul endroit, et conserver la capacité de faire des ajustements sur mesure quand c’est nécessaire
    • Le problème, dans la réalité, c’est que personne ne sait, au moment de l’écrire, qu’il utilise une mauvaise abstraction. Sinon, il ne l’utiliserait pas
      Dire simplement « évitez les mauvaises abstractions » ou « prévoyez les usages futurs » ne suffit donc pas
      En général, personne ne peut prédire l’avenir
      Les abstractions doivent être utilisées avec une extrême prudence, même quand elles semblent justes. Mieux vaut adopter un scepticisme radical
    • Le problème de la version « clean », c’est qu’elle est entièrement couplée
      Dans le code de calcul, toucher à une chose affecte tout le reste
      À un moment donné, il devient plus simple de laisser un peu de code dupliqué, d’indiquer les similarités par des indices dans la structure du code ou par des commentaires, puis, avec le temps, de faire ressortir dans les commentaires les différences essentielles
      L’approche Clean Code pour ce type de code gère mal l’évolution des exigences
      On finit facilement avec un tas de fonctions utilitaires contenant plusieurs chemins de code selon l’endroit d’où elles sont appelées
      Parfois, on peut abstraire cela ; certains l’auront peut-être déjà remarqué, mais la phrase précédente décrivait le polymorphisme orienté objet
      Mais comment connaître à l’avance les exigences exactes ? Tenter de faire du « clean code » à un stade aussi précoce ne mène qu’à une mauvaise abstraction ou à une surconception
      La bonne approche consiste à ne rien faire, à noter les problèmes possibles, puis à attendre d’en savoir davantage sur le domaine du problème
    • J’apprécie de plus en plus l’idée de découvrir les patterns au fil des changements : chercher des patterns dans les nouvelles fonctionnalités et les corrections de bugs
      Du code un peu dupliqué mais facile à comprendre, comme dans l’exemple d’origine, ne m’inquiète pas vraiment
      En revanche, si je dois corriger le même problème à trois endroits, ou ajouter le code d’une nouvelle fonctionnalité à trois endroits, là je m’inquiète
      C’est à ce moment-là que je commence à me demander s’il est possible, d’une manière ou d’une autre, de pousser le code à modifier ou à ajouter vers un emplacement commun
    • C’est juste
      Les gens lisent sur les design patterns ou DRY et, comme ils sont encore en train d’apprendre, les appliquent généralement de travers, puis en rejettent la faute sur les textes d’origine
  • Rob Pike a dit un jour que l’une des raisons pour lesquelles Go est devenu ainsi était que « de petites copies valent mieux que de petites dépendances »
    Plus je programme, plus je comprends ce que cela veut dire
    La répétition en elle-même n’est pas le problème. Ce qui compte, c’est de comprendre l’unité que l’on est en train de lire et que l’on devra peut-être modifier
    DRY signifie souvent ajouter une abstraction supplémentaire, et si cette abstraction n’est pas suffisamment orthogonale, on a aggravé la situation, car il faut désormais raisonner sur deux unités au lieu d’une