Peter Naur sur la compréhension théorique et la pérennité du logiciel : un enseignement pour éviter la dette technique ?

Il est assez fréquent de lire un article de blog ou un post sur les réseaux sociaux abordant le concept de dette technique. Ce concept, bien que connu de la plupart des développeurs de logiciels est en réalité assez difficile à définir de manière précise.

La dette technique est la conséquence de mauvais choix de développement

La définition de dette technique que l’on retrouve le plus souvent désigne les problèmes et les difficultés émanant de choix qui visaient à faciliter à court terme certains développements antérieurs. Ramené au concept de dette, c’est l’accumulation des décisions faisant primer la simplicité dans le but de résoudre un problème à un moment donné.

L’existence de dette technique est de plus en plus reconnue et répandu dans l’industrie informatique. Les développeurs sont avertis que s’ils ne remboursent pas rapidement cette dette, c’est-à-dire s’ils ne procèdent pas à une réécriture rapide du code, ils risquent de se retrouver dans l’incapacité de le maintenir. Cependant, sachant qu’un logiciel est rarement statique et que certaines évolutions nécessitent parfois de repenser le code, il est tentant d’associer toute difficulté rencontrée à de la dette technique. Mais nous savons qu’il est plus facile de constater une erreur une fois que celle-ci s’est produite plutôt que de prévoir les conséquences de nos actions sur l’avenir. En fin de compte, ce que nous appelons dette technique est soit le résultat d’un manque de gestion des risques, soit une mauvaise façon de mener le développement logiciel.

Selon la définition consensuelle de la dette technique, le montant de cette dette résulte de mauvais choix de développement. Ainsi, il est important de distinguer les cas où les ingénieurs sont conscients des défauts qu’ils génèrent, de ceux où ils les ignorent. Cette nuance rend le concept de dette technique imprécis, car cette dette mélange des défauts assumés par l’équipe de développement avec d’autres défauts de conception plus ou moins latents.

La posture qu’adoptent les ingénieurs face au code et au développement logiciel reflète leur niveau de connaissance du code et du problème auquel il répond. Ainsi, les informations dont ils disposent au moment de prendre une décision influencent l’évolution du projet.

Comprendre un problème avant de coder : l’importance de la théorie

À l’origine de tout projet de développement logiciel figure une problématique métier traduite en un ensemble d’objectifs fonctionnels. L’enjeu de la programmation réside dans le développement d’une compréhension du problème qui est donné. C’est-à-dire, être capable de concevoir un programme qui rend compte d’une part significative d’une activité du monde réel. Par conséquent, le programme doit donc être conçu pour s’adapter aux évolutions. Dans « Programming as Theory Building« , Peter Naur souligne la nécessité de construire une représentation théorique du problème et il affirme qu’il est essentiel qu’elle soit partagée par les programmeurs de l’équipe pour assurer le bon maintien du logiciel.

La construction théorique fournit un cadre conceptuel pour comprendre comment les choses fonctionnent et les expliquer. En d’autres termes, la théorie est un modèle mental qui aide le développeur à organiser et résumer ses connaissances. Peter Naur affirme que l’intelligence nous donne la capacité d’accomplir des choses intelligemment, mais plus encore, elle nous permet de les expliquer et de répondre à des questions à leur sujet. Toutefois, la seule compréhension de la théorie n’est pas suffisante. Il est nécessaire de comprendre également comment elle s’applique concrètement au monde. Cela est essentiel pour être en mesure de reconnaître et d’appliquer cette théorie à des cas similaires. Les bonnes pratiques de programmation aident les développeurs dans ce sens, mais elles n’ont pas vocation à résoudre un manque de compréhension ou de modélisation du problème. Si elles peuvent aider à faire des choix de conception, leur utilisation dépend avant tout d’une bonne compréhension des enjeux du problème considéré.

Les limites du code face à la richesse de la pensée humaine

Sans prendre trop de risques, il est possible de dire qu’un grand nombre d’activités humaines, même les activités plus communes reposent sur une théorie. Repasser une chemise, cultiver des légumes, voyager, etc sont autant d’activités qui font appel à la théorie. Bien qu’il soit parfois difficile, voire impossible de l’expliquer, nous sommes capables de reconnaître des similitudes entres les différentes situations qui se présentent à nous. Le caractère le plus important ici ne réside pas seulement dans la compréhension des aspects les plus généraux et abstraits de la théorie, mais aussi dans la compréhension de sa logique d’application et de la façon dont elle nous amène à entrer en interaction avec le monde réel. Sur cette question, Peter Naur met en avant toute la difficulté de formaliser l’entièreté de cette construction théorique dans le code, au moyen d’un langage de programmation.

De part leur nature même, il est évident qu’il n’est pas possible d’exprimer toute la teneur d’une construction théorique abstraite à travers un langage formel. Bien qu’ils aient des avantages en informatique, comme le fait de lever toute ambiguïté pour la machine, les langages formels, ne permettent pas d’exprimer clairement l’intention du programmeur. Les langages de programmation, du fait qu’ils requièrent l’utilisation d’une syntaxe ou d’un paradigme particulier se positionnent différemment, mais le fossé entre la vision théorique du programmeur, généralement à un haut niveau d’abstraction et l’implémentation proposée dans le code, bien spécifique et concrète quant à elle, dénote la perte d’information et de clarté dans le processus d’écriture du code. De plus, l’algorithme le plus optimal en termes de temps de calculs, résulte rarement de l’expression la plus intuitive du code pour l’humain.

L’enjeu réside donc dans la capacité à traduire l’intention du programmeur, dans une logique déclarative par exemple, en se concentrant sur le résultat qui est attendu par le programme plutôt que sur la manière dont il doit être obtenu.

Au delà de permettre de comprendre pourquoi le programme est conçu de telle façon et comment il se raccorde au problème du monde réel auquel il propose de répondre, la construction théorique permet de répondre de manière constructive à toutes les modifications du code nécessaires pour qu’il soit en mesure de satisfaire aux changements que le monde connaît. Or, l’impossibilité de traduire la connaissance de cette construction théorique à l’aide d’un langage formel entraîne une implication directe des personnes en possession de cette connaissance.

Les ravages d’une vision productiviste de la programmation

L’utilisation d’un logiciel s’accompagne généralement de possibilités d’amélioration et favorise l’émergence de nouvelles idées. Le monde évolue de manière indépendante, ce qui implique que les fonctionnalités du logiciel doivent également évoluer. Il peut être tentant d’ajouter plus de flexibilité au programme afin d’anticiper ses évolutions futures. Cependant, augmenter la flexibilité du code nécessite de consacrer plus de temps à tester chaque fonctionnalité pour s’assurer qu’elles répondent aux attentes. Aucune organisation soucieuse de rentabilité à court terme n’a intérêt à complexifier ainsi le développement de ses logiciels en se basant sur un besoin présumé d’évolution du code.

Lorsque le développement d’un programme est axé sur des bénéfices à court terme, seule l’exécution du code est considérée comme ayant de la valeur dans la chaîne de production d’une entreprise. Cependant, Peter Naur affirme que les problèmes surviennent lorsque la programmation est perçue uniquement comme une activité de production de code, au détriment d’une tâche de construction théorique. En effet, si le lien étroit entre le code et la connaissance qu’il traduit n’est plus une priorité, il est inévitable que la connaissance des développeurs sur le programme lui-même se dégrade, puisque seul le code subsiste.

Dans un contexte idéal, le programmeur possédant à la fois une connaissance théorique du programme et une maîtrise de son implémentation, est en mesure de fournir des réponses claires sur les modifications à apporter pour obtenir un comportement différent et parvenir à un nouveau résultat. Ainsi, en confrontant sa vision théorique du programme, il peut déterminer le degré de similitude entre l’état actuel du programme et le besoin en termes d’évolution.

Néanmoins, il existe une multitude de solutions permettant d’atteindre un résultat donné. Un programmeur naïf peut donc facilement produire un code qui, bien que répondant aux exigences en termes de résultats, manque de cohérence, voire entre en contradiction avec les principes de conception théorique sous-jacents au programme. Sans les connaissances adéquates, il est alors possible pour un ou plusieurs programmeurs, travaillant de manière indépendante ou en équipe, de réaliser des modifications qui ne s’inscrivent pas dans un cadre théorique global, sans même s’en rendre compte.

Conclusion

La déconnexion entre le développement d’une compréhension théorique et l’implémentation du programme est bien souvent à l’origine des problèmes associés au concept de dette technique. L’accumulation de solutions opportunes à courts termes est précisément rendue possible du fait des lacunes, au niveau des connaissances théoriques des développeurs, à l’égard du programme. Ce déficit, conduit à une fragmentation du code en un patchwork de solutions inconsistantes d’un point de vue théorique, augmentant la complexité du code et creusant d’autant plus l’écart entre l’implémentation et sa conceptualisation initiale.

La perception de la programmation comme une simple tâche de production de code, plutôt que comme le développement d’une compréhension théorique d’un problème, a des répercussions profondes sur la nature même du métier de développeur. En limitant ses capacités d’abstraction et de raisonnement, le développeur se voit réduit à une tâche de moindre valeur, avec un risque accru d’être remplacé par un système automatique dès lors que sa créativité et ses compétences de résolution de problèmes ne seront plus requises. Au-delà, c’est une perte de contrôle des outils que nous avons conçus qui nous guette. Heureusement, il est encore possible de remédier au délitement des connaissances et de notre compréhension théorique du monde. Pour éviter cet affaiblissement des connaissances, il serait intéressant d’explorer l’usage des LLM et leur capacité à générer du texte dans un langage naturel, pour faciliter la diffusion et la transmission de la connaissance théorique au sein d’une équipe de développement. Cela faciliterait le maintien et l’évolution des logiciels, et peut-être même de construire un écosystème d’outils numériques en parfaite cohésion.