Débutant

Les développeurs sont les plus mauvais juges de code

Tout d’abord, mes excuses pour un tel titre, le but n’est bien évidemment pas d’offenser qui que ce soit, mais simplement d’attirer un peu votre attention.

Maintenant que vous êtes ici, vous vous demandez peut-être ce que je vais bien pouvoir faire de cette attention… Et bien je vais vous parler d’un des problèmes que j’estime des plus fondamentaux dans notre industrie : on se complait dans notre zone de confort.

Et plus spécifiquement comme nous sommes habitués à déchiffrer le code des autres au travers des mots-clés natifs de nos langages de programmation, il est naturel de reproduire ce schéma lorsque nous écrivons le code à notre tour.

Mais avant de mettre en lumière quelques-unes de ces mauvaises habitudes, voyons en quoi cela peut en valoir la peine.

Pourquoi écrire du code compréhensible est important

Le coût de la maintenance

Le grand facteur de décision, quelle que soit l’industrie, est l’argent. Qu’il s’agisse d’acheter un produit externe, d’adopter une nouvelle technologie ou bien de revoir l’organisation interne, l’idée est d’être plus efficace pour au final, gagner du temps ou de l’argent.

Le sujet d’aujourd’hui ne fait pas exception à la règle. Il ne s’agit pas d’écrire du bon code compréhensible parce que c’est beau. On veut le faire parce que cela fait gagner du temps et de l’argent.

On trouve encore aujourd’hui très simplement des études concernant le coût de la maintenance faites durant les années 80 et 90, qui s’inquiètent de la capacité des systèmes à grossir de façon constante. À l’époque, les estimations faites évaluaient le coût de la maintenance entre 40 et 80 pourcent du coût total du système.

Depuis lors, le besoin en développeurs a incroyablement progressé. Les cabinets de conseil sont devenus la norme, augmentant la rotation au sein des équipes. L’accroissement de l’arrivée de nouvelles technologies semble ne pas avoir de limites.
Exprimé autrement, il y a peu de chances que le coût de la maintenance ait aujourd’hui baissé. Il est très fréquent d’avoir des systèmes comportant une logique métier compliquée et que ces systèmes tournent depuis une, voire deux décennies. Les nouveaux arrivants doivent alors appréhender à la fois la complexité métier, mais également la complexité technique du système.

Nous sommes ici face à l’un des paradoxes de notre industrie : alors que les technologies évoluant devraient nous permettre d’en faire de moins en moins, d’être de plus en plus concis et expressif, nous tentons en parallèle d’automatiser de plus en plus de processus avec de plus en plus d’outils, augmentant la complexité technique du système.

Les systèmes métier essentiels qui doivent vivre longtemps et les choix techologiques associés font de la maintenance un point critique sur lequel les entreprises qui veulent rester compétitives doivent constament s’améliorer.

Costs

La maintenance implique de lire du code

Maintenant qu’il est clair que de nos jours, la maintenance est une énorme part du coût total du système, il ne reste qu’à faire le lien avec l’activité qui consiste à lire du code.

Cela ne devrait pas être trop difficile pour quiconque ayant eu un peu d’expérience dans le domaine, mais pour ceux qui souhaiteraient tout de même une justification sur ce point, je vais énumérer les deux principales activités composant la maintenance que sont l’évolution métier et la correction d’erreurs.

Ces deux activités nécessitent de lire du code de manière intensive.

Maintenance

La correction d’erreur est la plus évidente. On commence par se faire une représentation mentale du comportement que le code devrait avoir, puis, on confronte cette représentation avec le message d’erreur, le journal, ou toute autre information venant décrire ce qui se passe réellement.
Si le code n’est pas suffisamment explicite, on peut même être amené à faire du debug. Ce qui revient simplement à lire très très attentivement le code tandis qu’une machine nous livre l’état du système.

L’évolution métier elle aussi nécessite de lire du code car nous attendons généralement une analyse indiquant quelle partie du code devrait évoluer, quels composants devraient être ajoutés, devenir publics, etc. Nous devrions pouvoir en théorie compter sur la documentation existante pour cette analyse, mais en réalité le peut-on vraiment ?

Si vous avez travaillé afin d’obtenir une documentation vivante, votre code est sans doute déjà suffisamment travaillé. Mais si ce n’est pas le cas, votre analyse ne peut que s’appuyer sur le code pour être exacte. De mon expérience, le premier réflexe des développeurs dans ce cas est de plonger dans le code et de le lire.

Certains pourraient me répondre qu’une connaissance globale du code et donc de son architecture est souvent suffisante pour produire une conception préliminaire qui permettrait au développeur de commencer à implémenter la nouvelle fonctionnalité et c’est vrai, je ne le conteste pas.

Mais toute la nuance est dans les attributs attachés aux mots. La conception est belle et bien préliminaire. Pas plus.

“Aucun plan de bataille ne survit au premier contact avec l’ennemi.”

La conception finale est donc du ressort de l’implémenteur, qui ne la finalisera qu’après s’être confronté avec le code existant et donc l’avoir lu et compris. Le plan initial est souvent incompatible avec la réalité ; cela peut être à cause des choix et des adaptations locales qui ont été faits tout au long de la vie de l’application.

Et si vous connaissez par cœur toutes ces subtilités, c’est que vous avez nécessairement lu le code énormément, si ce n’est écrit vous-même.

Écrire et lire le code est avant tout un transfert de connaissance

Combattre les mauvaises habitudes

Si vous êtes toujours en train de lire cet article, j’ai pour espoir que c’est parce que vous êtes convaincus qu’il est important que le code soit parfaitement clair sur ce qu’il fait.
Mais peut-être estimez-vous que votre code est déjà bon. Certes, je ne vous connais pas personnellement, mais si vous ne découvrez le sujet du clean code que maintenant, je suis prêt à prendre le pari qu’il reste de la place pour vous améliorer.

Mais avant tout, je voudrais vous donner la règle fondamentale que je tente de suivre lorsque je pense en matière de lisibilité :

Il faut communiquer l’intention

C’est sans doute la règle la plus essentielle à propos de la lisibilité du code. Et pourtant, elle est très souvent délaissée. C’est aussi sans doute l’une des plus difficiles à satisfaire.

Nous allons explorer quelques biais que nous expérimentons lorsque nous écrivons du code et qui nous empêchent de nous améliorer.

Enlightened Brain-bulb

On ne pense pas long terme

Une compréhension rapide doit être une propriété du système

Le premier biais auquel je m’attaque est assez simple à appréhender : lorsque l’on vient de finir d’écrire le code, on le comprend.

Mais il s’agit souvent là d’un état des choses très temporaire.
Il est fréquent de revenir six mois plus tard sur son propre code et de se creuser les méninges à tenter de le lire de nouveau.

Au moment où vous en avez fini avec le code, que vous avez travaillé quelques heures voire quelques jours sur le problème, vous avez eu le temps de vous familiariser avec le métier, vous êtes capable d’expliquer si besoin car tout est encore très frais.

Il s’agit ici d’une vue court terme des choses.

Une fois le code terminé, il faut s’assurer qu’il faut un temps d’un ordre de grandeur inférieur pour lire le code qu’il n’en a fallu pour l’écrire.
S’il faut de nouveau plusieurs jours de lecture pour comprendre le fonctionnement d’une partie du code, sa maintenance risque de coûter cher.

Le problème réside dans le fait que l’on laisse le code pourrir.
Dans les premières fonctionnalités, aucun effort spécifique n’est fait. La base de code grandit et les méthodes grossissent. Les fonctionnalités se mixent et les corrections commencent à prendre des jours plutôt que des heures.
Ce scénario est très commun et est directement lié au manque de discipline de l’équipe dans le maintien du fait que le code puisse être compris rapidement.

Des efforts continus doivent être faits pour assurer une compréhension rapide

Pour cela, assurez-vous d’exprimer le besoin métier au plus haut niveau d’abstraction et séparez avec soin votre code en petites méthodes.

Nous avons appris un faux langage

Voici un autre biais que nous partageons entre programmeurs, nous avons appris un langage commun. Certains le pratiquent depuis des années et y sont très familiers. Certains sont encore en phase d’apprentissage.

Pour ces derniers, ce que j’entends par apprentissage est la capacité à manipuler les outils bas niveau que nous procurent les langages et à en faire bon usage. Ce n’est généralement pas ici que se situe le problème.

Le problème vient du fait que nous n’apprenons pas que ces outils bas niveau appartiennent aux couches d’abstraction basses du système.

Pour ceux dont le langage de code est déjà dans leur zone de confort, il est désormais relativement aisé de décrypter n’importe quelle implémentation de bas niveau. Les boucles for, les try/catch et les if imbriqués sont déjà très familiers.

Dès lors, si l’on exclut la phase d’apprentissage car temporaire, et que nous sommes d’accord sur le fait que nous sommes capables de nous lire et de nous relire, il ne devrait pas y avoir de problème, si ?

Alors pourquoi est-il si difficile de déchiffrer ce que l’autre a voulu faire dans son code ?

incomprehension

Plusieurs raisons :

  • Il est difficile de garder quelque chose simple lorsque que tout est conçu au plus bas niveau.
  • Il y a toujours une part importante de développeurs en phase d’apprentissage à cause de la demande croissante.
  • Nous pensons que nos langages de programmation, aussi expressifs soient-ils, sont réellement des langages

Il y a sans doute un tas d’autres raisons mais je vais ici me contenter à développer cette dernière.

Nos langages ne possèdent en réalité qu’un nombre très limité de mots-clés qui leur sont propres. Tout le reste du vocabulaire, des verbes, et globalement de ce qui compte pour comprendre l’intention n’existe pas nativement.
Le langage métier doit être réinventé dans chaque projet.

De plus, l’usage du langage de programmation est souvent très personnel.

Vous devez donc consciemment faire en sorte que le code que vous créez possède suffisamment d’éléments pour pouvoir recréer le langage métier de manière expressive. C’est une des raisons pour lesquelles le DDD et son langage omniprésent sont aussi appréciés une fois qu’on a commencé à y goûter. Il met l’équipe en accord sur le vocabulaire à apprendre et sur ce qui est lisible.

Nous aimons suivre les modèles

comfort zone

S’il est bien une chose que nous apprenons à aimer, ce sont nos manières de faire. Nous découvrons un modèle et suivons ensuite ses concepts dans tous les projets rencontrés.

Prenons l’exemple de l’architecture en couche. Il s’agit simplement d’un outil avec ses forces et ses faiblesses, et l’adopter, c’est choisir d’amener avec lui les deux.
Mais il a été si dominant que je rencontre des développeurs qui n’ont été en contact qu’avec ce modèle, et sont aujourd’hui prisonniers de certains schémas de pensées que ce modèle amène avec lui.
On nous a tellement familiarisés avec cette couche de ‘service’ que c’est aujourd’hui un terme que je rencontre fréquemment.
L’architecture en elle-même n’a rien à voir avec l’utilisation que l’on en fait. Il ne s’agit que d’un outil. Ce qu’il faut combattre, c’est la zone de confort dans laquelle nous aimons paresser.

Ce que je vois, ce sont des ProductService. Ce que je vois, ce sont des classes anémiques. Ce que je vois, c’est beaucoup de couplage. Ce que je vois, c’est peu de cohésion.

Mais ce n’est pas l’architecture qui amène tout cela.

C’est nous.

Nous sommes si habitué au terme de service qu’il vient naturellement s’imbriquer dans nos schémas de pensée et de conception, quand bien même il n’apporte aucune indication réelle sur la responsabilité de la classe.

Il ne s’agit ici que d’un exemple du vrai problème mais il se décline de nombreuses façons. Chaque fois que je rencontre IAccount ou AccountImpl, il s’agit d’un pattern que nous suivons, et qui nous empêche de nous arrêter et de réfléchir à un meilleur nom. Chaque fois que l’on appelle une méthode applyChange ou fillProperties, on ne révèle pas clairement son intention. On oblige le lecteur à un effort supplémentaire pour comprendre le comportement sous-jacent.

On ne doit considérer les méthodes que comme des outils que l’on choisit d’utiliser

Conclusion

J’espère que vous vous êtes un peu reconnus dans cet article, car j’espère qu’il vous sera utile. Prendre conscience de ses propres travers est le meilleur moyen de commencer à les mettre de côté, pour tenter de changer les choses.

C’est ce que j’aime dans cette communauté craft, elle tente de revenir à l’essentiel de ce qui fait du bon travail, comme nommer correctement ou faire de notre code une œuvre lisible, qui sont les premières choses que l’on apprend en tant que développeur. Mais qui sont aussi les premières choses que l’on oublie.

© SOAT
Toute reproduction interdite sans autorisation de l’auteur

Pour aller plus loin

Nombre de vue : 1177

COMMENTAIRES 5 commentaires

  1. Lamiroté dit :

    Très intéressant cet article. Grand Bravo.

  2. Steeven Ramaheridianina dit :

    Très bon article sur l’importance de la lisibilité du code !
    Dans la même intention, si le sujet vous intéresse, Laura Savino à fait une excellente présentation au NCraft l’année dernière.
    Je ne vais pas mettre de lien direct, mais tapez sur Google “lisibilité ncraft” et vous trouverez 😉.

    Pour aller plus loin et aider vos pairs, pourriez vous écrire un second article avec des exemples ? ☺️

    Sinon Thomas Pierrain, Bruno Boucard et Jérémy Grodziski ont présenté quelques fois une session live coding “Du DDD dans mon legacy” que vous pouvez trouver facilement sur YouTube.

    Merci pour le partage de votre pensée !

  3. Dorian Bussi dit :

    Merci pour ces retours !

    La keynote de Laura Savino était en effet très intéressante. J’avais adoré le parallèle entre la poésie et le code : le deux peuvent être difficile à déchiffrer et cacher leur sens lors d’une première lecture. Il faut souvent s’y reprendre à plusieurs fois pour en extraire l’essence.

    Pour la question concernant un second article avec des exemples, c’était en effet l’idée de départ mais comme nous le disent souvent nos chers coachs agiles, il faut avant tout chercher le “why?”. C’est donc ce que j’ai voulu partager avec l’article actuel. Une second peut donc arriver plus tard.
    Mais si vous le souhaitez, vous pouvez trouver dès à présent, des exemples dans la ref. card nommage sur la page des publications SOAT :
    https://www.soat.fr/partageons/publications

    Sinon les vidéo d’uncle bob sont des très bonnes bases pour se faire une idée des chose à embrasser et des choses à éviter.

  4. Ludo dit :

    Hello, article avec un timing raisonnant, j’écris en ce moment là dessus, je partage les analyses mais diffère sur interpretations, experiences et “conclusion”.

    1. Pour compléter le paragraphe sur “la maintenance logicielle”
    (en français, on trouvera moins de données, donc “software maintenance” sous “software engineering” offre une meilleurs densité de résultats).

    > Je trouve que l’intro n’insiste pas assez sur la définition car le sens commun trompe (pas que) les devs, elle est perçue “réparation ou entretien” alors qu’en réalité: c’est TOUTES interventions concernant un programme APRES PREMIERE mise en PRODUCTION.
    Cela inclue l’ops par exemple comme un changement d’environnement et également le process de re-engineering (refonte from scratch en raison d’un cout de maintenance evolutive trop élevé, cf classification des maintenances).

    > Donc à l’aire du lean, agile, sacralisée par un MVP et une pluie d’itérations rapides, on ne fait quasiment QUE de la maintenance.

    > C’est la définition formelle de la norme ISO/IEC/IEEE 12207, révision 2017 de ISO/IEC 14764 introduite en 2010 sur la maintenance logicielle. Pour avoir une norme iso introduite en 2010 signifie que le problème est connu, étudié et compris pour être encadré clairement il y a déjà 8 ans. Les normes peuvent avoir l’effet de barbiturique, mais elle vise l’optimisation des coûts de maintenance logicielle en identifiant très explicitement les facteurs & opérations. Le Département IT étant majoritairement un centre de cout, il est pertinent d’en prendre connaissance.

    > Les études très sérieuses par des organisations variées (d’IBM, en passant par des SSII allant jusqu’à la NASA) pondèrent ces critères empiriquement et les plus déterminant sont toujours les mêmes, peu importe la structure.

    > Enfin pour la stats des coûts de maintenance, effectivement cela varie selon les études, j’ai plus souvent aperçu un chiffre grossier de 50% en 1999, mais le monitoring des croissances observées dans les années qui suivent (échantillon plus faible) s’accordaient sur du + de 80% vers 2005-2008. (si tu as encore des sources, je suis très intéressé par le type de structure ou d’échantillon qui avaient déjà mesurés du 80% pré-2000!)

    2. Les mauvaises habitudes sont en amont de la responsabilité du dev.
    Autant j’adhère à l’ensemble partagé, mais je situe le problème encore plus en amont.
    Le titre de l’article, un peu provoc évidemment, me dérange légèrement.

    On évangélise DDD afin de parler la même langue, c’est génial, c’est effectivement la bonne direction.
    Avant, c’était la ruée vers agile, les user stories, les cycles courts, les meetings cross-équipes, pour rapprocher les acteurs et réduire les disproportions des couts et erreurs.

    MAIS dans la majorité des cas, le “business” pose très souvent la même question à la technique: “combien va couter la tache X?” .
    Et même si lean/agiles est implanté, le poker planning va (parfois/souvent) servir à faire fondre la spec business dans une sous-enchères type: “présentation du scope initial, chiffrage L, réduction du scope, rechiffrage M, réduction du scope, rechiffrage S, validation.”
    La perception de la tech comme centre de cout donne une execution des workflows de méthodologie pour aller à l’inverse de leurs principes.

    Je l’identifie comme la première cause des problèmes d’alignement biz/tech et donc cout/valeur.

    Malgré les outils ou méthodologies, le business déporte systématiquement (implicitement) du risque sur le chiffrage tech, sans vraiment jamais transmettre clairement la valeur ou projection de valeur (en amont, et bien pire, en aval également).
    Soit: “les devs sont très désintéressés de la valeur de leur code”.

    Ironie ou indice: Le terme ROI, même en français, place la valeur en premier et le cout ensuite.
    Les principes lean & agiles imposent au business d’estimer, de mesurer ou de pouvoir mesurer rapidement via expériences aux couts minimes.
    Ma réalité constagée? après l’étude de marché pré-MVP, je n’ai rarement (jamais?) vu ces approches standardisées sur les itérations suivantes post-MVP, donc la maintenance.
    La confirmation de traction/monétisation initiale du MVP transforment la conscience/confiance du business et la maintenance se fait à l’intuition, réservant les tests, expériences & mesures à des cas très particuliers ou très “innovant”.

    Remplacer la forme interrogative: “Combien ça coute d’ajouter/modifier X sur mon projet?”
    Par la forme imperative: “Ajouter/Modifier X sur mon projet, cela devrait ajouter Y (unité de valeur) en Z (unité de temps)”.
    La marge d’erreur tacite (ou spécifique) est acceptée dans les 2 formes. Cependant le second cas ne transfer aucun risque, injustifié à ce stade, sur le départment tech.

    On EST aligné:
    1. le business prend un risque en amont sur la valeur, ce n’est pas à la tech de se planter sur un chiffrage des couts (il y a des exceptions).
    2. on connait la marge d’erreur du business qui nous donne une très bonne info sur le niveau de maturité du modèle que l’on va ensuite coder et donc le niveau de maintenabilité/dette acceptable.

    TL;DR: on trouve des solutions “tech” ou “projet” superbes pour se ré-aligner, mais ne devrait-on pas éviter de se “désaligner”?

    Note rapide, l’évolution en gestion de projet est similaire: challenge le classique Project Manager (PM) responsable du couts/temps
    par le Product Owner (PO) qui étend la responsabilité du PM à la performance/valeur (donc bien R et I de ROI).

    Merci pour l’article

  5. Dorian Bussi dit :

    Merci Ludo pour votre commentaire !

    J’aurai du mal à rebondir sur tous les points que vous soulevez, les sujets sont nombreux.

    Tout d’abord concernant les sources, j’ai effectué la recherche suivante :

    https://scholar.google.fr/scholar?q=software+%22cost+of+maintenance%22&hl=fr&as_sdt=0&as_vis=1&oi=scholart&sa=X&ved=0ahUKEwjc0ue0wMHZAhXoDMAKHcjpAaoQgQMIJjAA

    Le principal papier que j’ai lu est le suivant :
    https://go.galegroup.com/ps/i.do?p=AONE&sw=w&u=googlescholar&v=2.1&it=r&id=GALE%7CA14635559&sid=classroomWidget&asid=4225daf8

    Il date de 1993 et s’intéresse en partie au sujet de surcoût induit par le fait qu’une grande partie du code à l’époque n’a pas été écrit selon les principes de “structured programming”.

    Néanmoins, pour l’écriture de cet article j’avais simplement besoin d’un ordre de grandeur afin d’illustrer l’importance que le coût de maintenance peut avoir.

    En effet, je ne définis pas précisément ce que j’entends par maintenance dans l’article. Je sais que le terme est souvent mal compris et mal utilisé mais c’est un choix délibéré afin de ne pas dévier du but de l’article.

    Je suis globalement d’accord avec tous les points que vous soulevez, notamment qu’il y a beaucoup d’autres raisons au coût de la maintenance : désalignement des valeurs, hiérarchies opaques, mauvaises exécution des méthodes.
    Ces problèmes sont sans aucune doute les premières causes de beaucoup de problèmes, j’en conviens tout à fait.

    Mon but était juste de soulever et de mettre en lumière des point qui, s’ils sont moins impactant que ceux cités précédemment, existent néanmoins, sont moins représentés dans les différents articles que je lis.
    Pourtant ce sont des points que je rencontre dans ma vie de développeur en permanence. Ce sont des points qui sont corrigeables par les développeurs et pour les développeurs.

    Les points que vous citez sont très valides et j’essaie de faire ma part pour changer améliorer ces points dans me missions clients. Mais ce sont des points d’organisation et de culture d’entreprise, ils sont d’une responsabilité partagée.

    Les points que j’évoque sont d’une responsabilité d’équipe ou même personnelle et peuvent être améliorée via un effort et un laps de temps de plusieurs ordres de grandeur plus faibles que des problèmes structurels.
    Et c’est bien de ceux là dont j’avais envie de parler et de mettre en avant !

AJOUTER UN COMMENTAIRE