CodeMiner

Imaginez-vous en train de développer une application. Vous avez beaucoup avancé, la plupart de vos fonctionnalités sont exploitables, encore quelques menu détails à rajouter et vous êtes prêt pour la gloire. Fatigué et heureux comme un gamin ayant monté un château de carte aussi haut que lui, vous remettez cette touche finale au lendemain. Mais ce jour-là, c'est le drame. La moindre modification, ajout ou refactoring que vous apportez entraîne fatalement une série de bugs incontrôlables. Vous commencez alors à corriger ces bugs les uns après les autres, mais ceux-ci se révèlent être les têtes d'une hydre repoussant après les avoir coupées, vous obligeant à abandonner et à reprendre une version suffisamment ancienne de votre programme pour "repartir sur de meilleurs bases". Que s'est-il passé ? Qu'est-ce qui a changé dans la structure du logiciel pour arriver à cette impasse ?
La fabrication d'un logiciel quelle qu'il soit fait appel à des méthodes de représentation de son fonctionnement avant, pendant et après la phase de développement. Qu'il soit élaboré selon un cycle en V ou en extreme programming, les architectes des projets industriels et les ingénieurs-développeurs des projets à plus petite échelle ont besoin d'avoir une représentation abstraite du programme pour faire les bons choix de structure, respecter une architecture intiale ou s'orienter pour apporter des modifications. On parle ainsi de "localiser" les zones du programme nécessitant une intervention, ou que telle partie à changer est à telle endroit par rapport aux autres parties. Cette vue générale sur la structure est fournie par des schémas de fonctionnement qui synthétisent et réduisent les informations dans un formalisme de description, dont les plus célèbres sont l'UML (Unified Modeling Language) et le Merise francophone. Ils permettent entre autre de représenter les interactions entre composants, classes ou packages du logiciel pour mettre au point une logique de fonctionnement. Le découpage en différentes classes et packages a ainsi pour objectif, entre autre, d'en isoler des modules dont le couplage minimal leur assure des "rôles" différents et complémentaires. Cette isolation des variables et fonctions permet de clarifier la logique de fonctionnement, limiter les dépendances et ainsi augmenter considérablement la durée du cycle de développement au bout duquel toute tentative d'évolution provoque une régression. Ce n'est ni plus ni moins qu'une méthodologie pour réduire et maîtriser les interactions foctionnelles entre variables, regroupées dans des ensembles qui font sens pour l'individu. On parle en effet de "complexité" d'un programme non sans raison, car le produit de ces interactions inscrites dans le code source entraîne à un moment une perte de la vue synoptique sur celui-ci, et donc sa maîtrise.
Malgré ces précautions, l'architecte et le développeur vont finir par ne plus avoir conscience du fonctionnement détaillé du programme sur lequel ils travaillent, car elles ne font que retarder l'avènement d'un trop grand nombre d'interactions, pas les empêcher. Une "distance", au sens de D.Norman sur la Théorie de l'action, se créée ainsi entre le modèle de conception et son implémentation. Le reverse engineering UML ne peut alors dessiner qu'une vue globale sur la structure, mais il n'est pas possible de la lire et encore moins de se la réapproprier dans le détail. La mise en place d'interfaces de fonctions en langage objet ou de fichiers d'en-tête en programmation impérative prend en compte cet effet boite noire qui implique de faire confiance à la description de la fonction en langage naturel. On fait alors deux types de supposition, d'une part sur ce qu'elle fait réellement, et d'autre part sur les réactions en chaîne qu'elle peut impliquer. C'est à ce moment là que les tests unitaires et les tests de non régression prennent toute leur valeur : seule une évaluation systématique des fonctions permet de s'assurer qu'elles semblent faire toujours ce pourquoi elles ont été conçues, mais en contrepartie il faut accepter de ne vérifier qu'un comportement "visible" (ce qui entre et ce qui sort de la fonction).
Et quand bien même les tests unitaires sont établis avec rigueur, un aspect du développement logiciel nous échappe toujours depuis la nuit des temps informatiques : pourquoi un programme possède-t-il une fin de cycle de développement, par delà le fait que ses créateurs ne peuvent plus appréhender son fonctionnement ? En d'autres termes, il arrive un moment où toute modification du code source, nous semblant même mineure, provoque des bugs en cascade sans qu'il soit possible de les anticiper. C'est comme si le système, en évoluant au cours du développement, atteignait un état critique, à la limite de l'instabilité. Ce système semble encore être une boite noire, pourtant en torturant un peu le programme nous pourrions nous rendre compte que certaines modifications affectent plus durement son fonctionnement que d'autres, allant du simple bug d'affichage à un crash total. Se pourrait-t-il que le risque de provoquer des bugs ne soit pas uniforme ? Existerait-il des "zones" du programme plus vulnérables que d'autres ? Le fait que certaines variables et fonctions soient plus réutilisées que d'autres est une bonne piste, car cela implique qu'un problème apparaissant sur eux aurait de plus grandes répercussions dans le reste du programme. Ainsi, pourquoi ne pas dessiner un graphe des dépendances, non pas entre composants de haut niveau comme les classes, mais directement entre variables et fonctions ? Qui est utilisé par qui ? Il n'existe actuellement aucun outil pour identifier ces éventuelles zones à risque, ni de connaître dans la pratique les dépendances de chaque variable du code source : on ne peut donc avoir qu'une connaissance "à priori" et grossière de la structure d'un programme, qui elle échappe à l'intelligibilité dans son niveau de détail le plus fin.
A quoi ressemblerait un graphe des relations fonctionnelles entre variables ? Ce système complexe ainsi modélisé possède-t-il des lois et des patternsspécifiques ? Nous proposons de développer des outils complémentaires aux outils de modélisation existants pour faire du reverse engineering non destructif sur le programme, c'est à dire effectuer des modélisations de sa structure sans perte d'information. L'objectif, au delà de l'étude des logiciels, est de mettre à disposition des équipes de conception et de développement les moyens de se réapproprier la complexité des systèmes qu'ils construisent et de les explorer. Pourquoi de tels graphes ? Parce que l'état d'une variable change par l'action d'une "fonction" (au sens large du terme), nous permettant ainsi de représenter des variables sous forme de noeud, et les fonctions sous forme de liens d'un graphe. Il deviendrait ainsi possible de repérer les interactions fonctionnelles altérant le modèle structurel que l'on s'est donné initialement, particulièrement en programmation par événements et lors du couplage du programme avec une interface utilisateur. Le mapping des contextes fonctionnels permettrait d'autre part de tracer les "chemins" des données, et ainsi isoler les zones à forte dépendance susceptibles d'aggréger des bugs en cascade, ou à l'inverse les zones dont les autres dépendent le plus, et dont les modifications nécessitent la plus grande prudence. Il s'agit enfin de proposer un outil complémentaire aux debuggers "next-step" et aux tests unitaires, incapables de repérer les erreurs d'architecture et d'interface utilisateur.
D'autres idées :
- construire un graphe qui évolue au cours du développement à partir des données d'un SVN avec, comme pour de la vidéo, pouvoir mettre en pause, faire des avances rapides, revenir en arrière...
- générer des graphes à la volée mettant en surbrillance les ajouts, modifications et suppressions pour vérifier rapidement la légitimité des mises à jour et tracker les violation d'architecture.
- tracker l'activation des zones, fonctions et variables du logiciel en cours d'exécution et d'en faire des animations ; on aurait les boutons démarrer/arrêter la capture du fonctionnement lors des actions de l'utilisateur pour ensuite voir ces films au ralenti, et ainsi isoler les bugs de manière visuelle.
Informations sur le projet
>
Support utilisateur
| Wiki | Blog 
- Vous devez vous identifier ou créer un compte pour écrire des commentaires

