3 points par GN⁺ 2023-07-21 | 1 commentaires | Partager sur WhatsApp
  • Même une structure ou un code qui semblait à l’origine mineur ou temporaire peut, avec le temps, jouer un rôle de support du système ; avant toute modification, il faut donc vérifier les dépendances actuelles
  • Chesterton's Fence est un principe utile qui consiste à comprendre pourquoi quelque chose a été mis en place avant de le supprimer, mais se limiter à l’intention initiale peut faire manquer les rôles apparus ensuite
  • Lors de travaux dans la salle de bain d’une maison, un montant vertical qui semblait n’être qu’un obstacle soutenait en réalité une partie de la charge du deuxième étage, après que son rôle initial de cloison de placard eut disparu à cause de modifications structurelles incorrectes
  • Dans les systèmes informatiques complexes aussi, l’historique des changements et les documents de conception ne sont qu’un point de départ ; il faut également voir comment un composant est actuellement intégré au système
  • Lorsqu’on supprime ou modifie un ancien composant, ne pas vérifier à la fois la raison historique de sa conception et son rôle caché actuel peut provoquer des pannes inattendues

Ce que Chesterton's Fence peut manquer

  • Chesterton's Fence défend l’idée qu’avant de modifier ou de supprimer quelque chose, il faut d’abord comprendre pourquoi cela a été créé
  • Qu’il s’agisse d’une clôture ou d’une porte, tout objet fabriqué par l’être humain a probablement existé pour une raison jugée utile par quelqu’un
  • Même une conception qui semble totalement dénuée de sens peut cacher un aspect du problème que la personne souhaitant la modifier n’a pas vu
  • Cependant, cette approche peut amener à se concentrer uniquement sur le rôle voulu par le créateur initial, et à négliger les nouvelles dépendances apparues par la suite

Un support de charge accidentel découvert pendant des travaux

  • Il y a quelques années, lors de la rénovation d’une salle de bain, un montant vertical gênait les travaux
  • Ce montant faisait à l’origine partie d’une cloison de placard, et ce rôle de séparation ne semblait plus nécessaire
  • En suivant uniquement la logique de Chesterton's Fence, on aurait pu penser qu’il était possible de le retirer, mais avec le temps il était en fait devenu un élément porteur
    • À la suite d’autres modifications structurelles incorrectes, ce montant contribuait à soutenir le deuxième étage de la maison
  • Il faut vérifier non seulement pourquoi quelque chose a été créé au départ, mais aussi quels rôles supplémentaires cela a fini par assumer

La leçon pour les systèmes informatiques complexes

  • Le même problème se répète lorsqu’on modifie des systèmes informatiques complexes
  • Examiner l’historique des changements, lire les documents de conception initiaux et comprendre pourquoi un composant a été conçu ainsi reste utile
  • Mais pour agir en sécurité, il faut aussi regarder comment ce composant est aujourd’hui connecté et utilisé dans le système
  • Avec le temps, un composant peut facilement assumer des fonctions annexes différentes de son objectif initial
  • Une modification sûre commence par la vérification conjointe de l’intention de conception passée et du rôle réel actuel

1 commentaires

 
GN⁺ 2023-07-21
Commentaires sur Hacker News
  • J’ai l’impression qu’on a enfin un nom pour désigner ça
    Je fais beaucoup de support sur les systèmes de contrôle, et il n’est pas rare que du code PLC qui manipule un équipement physique de façon particulière crée involontairement des problèmes
    Je me surprends souvent à répéter : « Chaque fois qu’on corrige un problème électrique ou mécanique avec du logiciel, un gremlin naît. »
    Même quand j’identifie la cause racine d’un bug ou une limitation programmée que j’aimerais supprimer, je refuse toujours de le faire tant que je n’ai pas compris pourquoi ce code existe. Aucun code n’a été ajouté sans raison, donc il faut d’abord comprendre pourquoi ce timer ou cette redéfinition est nécessaire
    Parfois, c’est une bonne nouvelle : le code résolvait un problème qui a déjà disparu. Mais bien souvent, c’est aussi du code ajouté pour éviter un incident, et le but d’origine s’est perdu avec le renouvellement des équipes. Sans documentation, on devient extrêmement prudent avant d’annuler le travail de quelqu’un d’autre
    Il y a aussi une question de confiance envers ses collègues. En général, on ne fait pas les choses sans raison, donc s’il y a du code, il faut supposer qu’il a un but et croire qu’il a été suffisamment examiné au départ. Si cette confiance s’effondre, toute prise de décision devient difficile

    • C’est pour ça que je laisse toujours un commentaire sur les lignes de code qui ne sont pas totalement évidentes, pour expliquer pourquoi
      Si la raison concerne une interaction avec quelque chose en dehors de la codebase — système d’exploitation, système de fichiers, base de données, endpoint HTTP, matériel — j’ajoute un commentaire à 100 %, sauf si c’est juste un appel d’API ou de bibliothèque banal
      Si j’ai mis un sleep à cause d’une limitation de débit d’un autre service, j’indique qui l’impose, la valeur limite telle qu’on la connaît à ce moment-là, et si je ne la connais pas exactement, j’écris que c’est une « estimation, mais que ça semble fonctionner », ainsi que la façon dont le système peut se comporter quand on dépasse cette limite
      Si j’utilise une base de données pour quelque chose qui pourrait sembler assez trivial pour un système de fichiers, mais qu’en réalité l’accès au système de fichiers de la manière nécessaire dans cet environnement épuise les ressources sous charge à cause d’appels système qui s’emballent, je le documente aussi
      Les contournements d’un bug dans une bibliothèque très utilisée qu’Ubuntu ne corrige pas dans une release LTS méritent aussi un commentaire. J’ai souvent écrit des commentaires du type : « Je sais que ce n’est pas terrible, mais voilà pourquoi »
      Même quand j’écris du code qui ne passera probablement pas bien à grande échelle, simplement parce que ça m’agace de le rendre scalable tout de suite et que, selon les prévisions actuelles, ce ne sera pas nécessaire, je le commente aussi. C’est peut-être surtout pour protéger mon ego, mais ça donne quelque chose comme : « Oui, je sais que tout le fichier est lu en mémoire, mais c’est un traitement batch rare et prévisible, et le fichier est censé rester petit. Si vous manquez de mémoire, commencez par regarder ici. Si vous voulez passer à des appels on-demand, réécrivez cette partie. »
    • Ça ressemble simplement à une logique classique de la clôture de Chesterton, non ?
      Le point de l’article, c’est que cela ne suffit pas en soi. Il faut aussi comprendre ce qui a été construit en supposant que ce code est là
    • Le commentaire le plus inquiétant que j’aie vu au fond d’un PLC Allen Bradley, c’était celui-ci
      « Je ne sais pas pourquoi ce rung est nécessaire, mais supprimez-le et vérifiez vous-même. »
      Je n’y ai pas touché, et je n’ai pas vérifié non plus
    • Il y a aussi un aspect culturel dans tout ça
      Historiquement, les ingénieurs électriciens et mécaniciens ont eu tendance à prendre le logiciel moins au sérieux que les systèmes électriques et mécaniques, et le résultat, c’est que dans des cultures d’ingénierie dominées par les EE/ME, on obtient facilement du code catastrophique
      Même chez des gens officiellement Professional Engineer, on voit encore souvent un niveau d’ingénierie logicielle inadmissiblement immature
    • Je pense que je ne referai plus jamais de travail sur PLC
      Le code sans documentation, ce n’est même pas le pire ; en général, c’est du matériel sur mesure vieux de 30 ans, au point qu’on n’a même pas le schéma électrique de l’équipement sur lequel on travaille
  • Il m’est arrivé quelque chose de similaire
    Il y a quelques années, j’ai acheté une vieille maison, et depuis les années 1960 les précédents propriétaires avaient fait eux-mêmes la plupart des travaux
    Une gouttière en zinc avait probablement fui pendant des décennies et endommagé une partie de la structure du toit, et le toit était soutenu par des panneaux de bois fixés dans les années 1970 pour recouvrir l’intérieur. Autrement dit, ces panneaux de bois reprenaient réellement la charge
    J’ai découvert bien d’autres choses dans cette maison. Par exemple, à un endroit au bord du toit, comme il manquait de la largeur de tuiles, au lieu d’en acheter d’autres ils avaient comblé avec du ciment et des morceaux de pot en céramique cassé

    • Si on remonte au début des années 1900, on posait le bardage en diagonale, ce qui empêchait fortement les bâtiments de se déformer
      Aujourd’hui, ce rôle dans les séismes et les tempêtes repose sur le placoplâtre et le contreplaqué
      Quand on voit une maison reconstruite en ne gardant que l’ossature, on remarque parfois quelques contreventements ajoutés. Ce n’est pas pour empêcher les murs de s’effondrer, mais pour conserver l’équerrage et la planéité jusqu’à ce que les murs soient remontés
    • Mon garage donne exactement cette impression
      Au premier regard, on dirait que quelqu’un a percuté la porte du garage et l’a gravement déformée, mais en regardant de plus près on voit que le toit tient à peine grâce au rail où la porte est fixée, et qu’on est à deux doigts de la catastrophe
      Au début, je voulais simplement remplacer la porte du garage en rallongeant les extrémités des chevrons, puisque quelqu’un avait déjà fait pareil de l’autre côté et que, si ça avait marché, ça devait aller ; maintenant, j’ai l’impression qu’il va falloir refaire tout le toit
      Ce qui m’inquiète vraiment, c’est le câblage suspect dans toute la cave. Il y a un mélange de fils relativement récents, de vieux fils gainés de tissu, et de ruban isolant reliant le tout. Heureusement, aucun des fils ne semble porteur
    • On n’est plus très loin de la peinture porteuse
    • J’ai vu une maison avec d’énormes dégâts de termites, et l’entrepreneur appelait ça du stucco structurel
  • On dirait que ce billet, comme presque tous les commentaires, passe à côté du vrai problème. Le cœur du sujet, c’est le manque de tests
    Le logiciel, contrairement à tous les autres moyens de production, permet de tester concrètement les modifications avant qu’elles n’affectent le réel
    S’il y a de bons tests, peu importe l’intention initiale, ou qu’une fonctionnalité ait trouvé un nouvel usage ou de nouveaux utilisateurs. On corrige, on lance les tests, et ils disent si la correction est bonne
    Avec de bons tests, pas besoin d’archéologie logicielle, de vétérans chevronnés qui connaissent chaque fissure, de prodiges capables de modéliser des systèmes complexes dans leur tête, de documents d’exigences exhaustifs, ni de systèmes de déploiement prudents qui transforment certains groupes d’utilisateurs en cobayes
    Avec de bons tests, on pourrait même modifier le système au hasard et s’arrêter dès qu’une amélioration apparaît. C’est exactement ainsi que Google a expliqué que l’IA avait « développé » des améliorations de tri
    Et pourtant, les développeurs de tests sont payés moins de la moitié, les départements de test sont relativement petits, la QA est coincée dans des plannings fixes et serrés, et il n’existe presque pas de héros techniques issus de la QA. Sans doute parce que cela ressemble à un travail dérivé et réactif

    • Même si les tests vérifient le comportement pour lequel le code a été conçu, il arrive que d’autres systèmes dépendent du comportement que le code a réellement
      On peut supprimer du code inutilisé et ses tests, alors qu’en réalité il est encore utilisé
      Après un changement, un test échoue, mais comme le test est fragile on l’adapte à la nouvelle situation, avant de découvrir que quelque chose dépendait de l’ancien comportement
      Les tests sont excellents, et dans des systèmes assez autonomes ils peuvent suffire à eux seuls. Mais dans des systèmes plus vastes, il faut parfois aussi de la télémétrie ou des déploiements progressifs
    • Les tests ont une portée précise. Celle dont le code est censé répondre, pas forcément l’ensemble de ce qu’on attend désormais de lui après des mois ou des années d’usage
      Au départ, le code servait à calculer la TVA d’une liste d’achats, puis il a peu à peu servi à forcer l’actualisation du cache TVA par catégorie de produit, et il peut se retrouver appelé dans des contextes jamais envisagés au début
      C’est pareil pour les commentaires. Ils traitent de l’intention initiale et des effets de bord, mais pas de l’endroit où cette méthode ou cette classe est utilisée bien plus tard, ni de ce qu’elle finit réellement par faire
      Dans un monde idéal, les commentaires seraient mis à jour quand le monde autour change, mais en pratique cela n’arrive presque jamais, sauf si le code interne change lui aussi
    • Quand on a travaillé sur des projets qui durent très longtemps, on voit bien que les tests ou une QA correctement financée ne suffisent pas à empêcher les problèmes organisationnels
      En général, les tests pourrissent. On dirait qu’eux aussi ont une date de péremption, et au bout d’un moment certains commencent à mourir
      Les problèmes de dépendances, les changements d’attentes côté API, les mises à jour de sécurité, l’expiration des comptes et des identifiants, les changements d’endpoint ou d’état des machines se mélangent, et les résultats des tests ne disent plus vraiment si le programme est correct
      La valeur métier marginale de réparer un test cassé en particulier est généralement très faible, donc on le désactive souvent complètement, ou bien on le force à « réussir » alors qu’il devrait signaler une erreur
      Répétez cela sur 10 ou 20 ans, et très vite on sépare les « tests auxquels on fait vraiment confiance » des « tests qu’on n’a pas le temps de corriger ou de nettoyer »
      Savoir quels tests sont bons ou mauvais devient une connaissance tribale qui disparaît avec les changements de poste et de rôle, et à un moment donné, cet amas de « tests qui mentent en disant que ça marche » et de « tests dont on ne cherche même plus à savoir si l’échec dit vrai » se met lui-même, par accident, à porter la charge
    • Le début donnait l’impression de répéter un optimisme à la test-driven development, puis ça bascule soudain vers les départements de test, ce qui manque de cohérence
      Il vaudrait mieux dire que les programmeurs devraient écrire les tests, les conserver avec le code et les exécuter automatiquement dans le processus de build
      Mais même une vraie test-driven development ne remplace ni une bonne conception ni de bonnes pratiques. Même une spécification très simple ne peut pas être remplacée par des tests
      Si la spécification dit seulement que f(S) renvoie la chaîne concaténée avec elle-même, il est difficile de vérifier que f est correcte à l’aide de seuls tests évidents traitant f comme une boîte noire. La spécification formelle compte aussi
      On peut sonder quelques points, mais si une seule valeur erronée peut être fatale, les tests ne le montreront pas
      On peut tourner en dérision l’archéologie logicielle, les vétérans chevronnés, les prodiges qui modélisent le système dans leur tête, les documents d’exigences exhaustifs et les systèmes de déploiement qui prennent certains utilisateurs pour des cobayes, mais toutes ces réponses existent parce que le logiciel est difficile. Et le logiciel est vraiment difficile
    • La distinction entre développeurs de tests, départements de test et équipes QA relève déjà du luxe dans la grande majorité des organisations logicielles
      En général, les équipes logiciel doivent assumer directement la qualité de leur travail, et ne peuvent pas refiler le problème à une autre partie de l’organisation
  • Je comprends qu’un montant auparavant non essentiel puisse ensuite se retrouver à porter une charge, mais d’après mon expérience, cela ressemble à un signe de conception paresseuse
    Au moins, quand on construit du logiciel, on peut voir qu’on est en train de faire porter une partie de la maison par un montant décoratif, et si on choisit simplement de faire avec au lieu de construire une meilleure nouvelle structure, l’équipe de développement risque d’être assez déprimée plus tard
    J’approuve l’article, mais il vaut bien mieux travailler quelque part où l’on peut espérer ne pas découvrir ce genre de choses trop souvent

    • Sur ce que vous avez construit, vous pouvez peut-être le savoir, mais dans ma carrière j’ai bien plus souvent eu affaire à ce que d’autres avaient construit, puis à le refaire
      L’idée de l’article n’est pas tant de dire qu’il ne faut pas utiliser de montant décoratif comme élément porteur, mais plutôt de reconnaître que quelqu’un a pu le faire avant votre arrivée
      C’est une position encore plus conservatrice que l’interprétation de base de la clôture de Chesterton, et même cette interprétation de base est rejetée par beaucoup comme étant excessivement restrictive
      Personnellement, cet article me parle. En termes logiciels, il m’est vraiment arrivé de supprimer une moulure « décorative » et de voir le plafond s’effondrer sur ma tête
    • Est-ce toujours de la paresse au mauvais sens du terme ? En logiciel, il n’y a pas de frontière nette entre « conçu pour porter une charge » et « conçu pour fixer du placoplâtre »
      Le fait qu’un système soit robuste ou dangereusement impossible à faire évoluer dépend du contexte
      On peut toujours faire des expériences de pensée du genre : « Et si l’équipe commerciale doublait et prenait 100 % du marché, à condition qu’on vende et qu’on onboarde les clients aussi vite que possible ? », et dans ces conditions il peut être acceptable d’utiliser une base de données comme file de messages
      Si cela a rendu la vie pénible à l’équipe de développement, alors c’était une erreur. Le système est devenu difficile à maintenir ou un enfer opérationnel
      Mais utiliser des montants décoratifs logiciels comme éléments porteurs ne mène pas nécessairement à ce résultat. Il existe aussi beaucoup de systèmes qui remplissent discrètement et heureusement leur rôle, tout en ayant économisé plusieurs mois de travail sur une « vraie » solution
    • Disons qu’on écrit du code de manière défensive. On ajoute dans une fonction le traitement des entrées invalides
      Le reste de la base de code n’envoie jamais d’entrées invalides, donc cette branche est du code mort et ne porte aucune charge
      Puis, à un moment, un bug apparaît et envoie des entrées invalides, et cette branche les traite consciencieusement et permet la reprise. À cet instant, elle devient une branche porteuse
    • Il m’est arrivé de penser qu’un service sans importance tournait très bien, puis de découvrir au moment d’un incident que, dans une situation qui n’aurait jamais dû avoir d’importance, une autre équipe avait commencé à en dépendre pour une fonction métier critique
    • Plus souvent encore, j’ai surtout vu que ce genre de chose arrive précisément parce qu’en réalité on ne le sait pas
  • Mon artefact « devenu porteur par accident » préféré parmi ceux que j’ai vus, c’était un sudo mal configuré
    sudo sans mot de passe était autorisé sur la commande find, ce qui permettait facilement d’exécuter du code arbitraire en root via -exec, et plusieurs scripts de support importants du produit avaient été écrits pour s’appuyer là-dessus
    En quelque sorte, une élévation de privilèges porteuse

  • J’ai rénové ma cuisine il y a quelques années
    À une extrémité de l’ancienne cuisine passait une grosse poutre, ajoutée lors d’une rénovation antérieure, avant que nous achetions la maison, afin d’ajouter un étage
    Pour agrandir la cuisine, il fallait l’enlever
    En ouvrant le plafond, nous avons constaté que cette poutre se trouvait environ 60 cm à droite de l’endroit où elle aurait dû soutenir le mur de l’étage
    Nous avons fini par corriger cela et déplacer la poutre dans le mur de l’étage, donc tout s’est bien terminé, mais quand j’ai demandé pourquoi elle était à cet endroit au départ, l’entrepreneur a répondu à peu près ceci
    « Il y avait une personne qui voulait bien faire et une autre qui s’en fichait. La qualité finit toujours réglée sur le plus bas paramètre »

  • Une chose dans laquelle le logiciel est meilleur que les systèmes physiques, c’est qu’on peut documenter beaucoup plus facilement l’intention dans le code avec des commentaires et des types
    Ce n’est pas parfait, surtout dans des langages dynamiques comme Python, mais c’est d’une grande aide
    L’équivalent métaphorique d’un montant porteur pourrait être un projet de hackathon dont personne n’imaginait qu’il finirait en production
    En pratique, une grande partie de notre travail consiste à bricoler quelque chose jusqu’à ce que ça fonctionne à peu près, puis à passer à la suite

    • Oui. C’est précisément pour ce type de problèmes que l’ingénierie système a été introduite dans l’aéronautique et la défense
      Les plans de maintenance doivent savoir quelles « charges » supportent chaque pièce ou sous-ensemble remplaçable
      Malheureusement, l’ingénierie système d’aujourd’hui s’est beaucoup éloignée de son objectif initial, mais c’était bien l’idée de départ
      L’une des raisons pour lesquelles les départements d’ingénierie système sont relativement affaiblis aujourd’hui est que la finance s’est invitée dans la planification de la maintenance. L’amortissement des stocks est impitoyable, et la question de « quoi garder en pièce de rechange » relève désormais rarement de l’ingénierie système, du moins d’après mon expérience
      Le résultat est prévisible, même si cela est partiellement compensé par le niveau très élevé du personnel de maintenance aéronautique. Par comparaison avec un réparateur de lave-linge, par exemple, ils sont remarquablement compétents
      Bien sûr, la finance aimerait aussi faire baisser ce niveau de quelques crans
    • Si c’est intentionnel, c’est facile à faire
      En revanche, je suis toujours surpris de voir à quelle fréquence un composant amont apparemment « décoratif » impose en réalité une limitation de débit, de sorte que si on le retire, le reste du système s’emballe
  • Cela me fait penser aux cas où des utilisateurs intègrent sans s’en rendre compte un bug logiciel dans leur flux de travail normal
    Résultat : quand on corrige le bug, le flux de travail se casse et cela provoque des plaintes

  • L’article dit : « Il était facile de voir pourquoi c’était là. Cela faisait partie d’une cloison de placard », mais ajoute qu’avec le temps c’est devenu porteur par accident, et qu’à cause d’une autre modification structurelle erronée, ce montant aidait désormais à soutenir le deuxième étage de la maison
    Pourtant, il n’est manifestement pas si facile de voir pourquoi il est là. Et je ne suis pas non plus convaincu par l’idée qu’il soit devenu porteur par accident
    Il me semble tout à fait possible qu’il ait été rendu porteur intentionnellement pour une raison qui vous paraît mauvaise, mais qui ne l’était pas pour les personnes de l’époque

    • On pouvait savoir pourquoi c’était là
      Simplement, savoir pourquoi c’était là au départ ne dit pas ce que cela fait maintenant
  • Un ancien postdoctorant en physique avec qui j’ai travaillé collait parfois ce genre d’étiquette sur des montages expérimentaux
    « Ne pas toucher. Danger caché. »
    Le laboratoire était plein de gens intelligents, habitués à regarder quelque chose et à conclure par eux-mêmes de façon raisonnable s’il était possible de le modifier
    Cette étiquette était un avertissement : il ne fallait pas se précipiter dans ce jugement