Nous avons vu dans le post précédent les violations les plus graves aux bonnes pratiques de programmation ABAP.
Ces défauts sont bloquants : le code correspondant ne peut aller en production tant qu’une correction n’est pas effectuée. Aucune exception n’est permise : tolérance zéro, car le risque est trop élevé de voir une transaction interrompue et l’utilisateur incapable d’effectuer le traitement souhaité.
Nous avons également paramétré notre Quality Profile ABAP de manière à identifier les défauts critiques, ceux-ci portant principalement sur la performance, et que nous allons voir dans cet article.
J’ai oublié de le préciser la dernière fois, mais mon analyse ABAP porte sur environ 700 fichiers représentant plus de 43 000 lignes dont environ 26 000 lignes de code (le reste est du commentaire). Ceci pour vous donner un peu une idée du nombre de défauts par rapport à la taille de l’application.
Les problèmes de performance relèvent principalement de bonnes pratiques non respectées en matière de traitements SQL. A ce titre, ils ne sont pas spécifiques au code ABAP. Ci-dessous, les plus nombreux :
Le SELECT * est une pratique à proscrire dans tous les langages, puisque cette requête ramène toutes les colonnes de la table. Si le nombre de lignes retournées est également important, alors la taille des données à transporter sur le réseau risque d’impacter la performance. De plus, si des colonnes sont ajoutées ultérieurement dans la table, les données correspondant à ces nouvelles colonnes seront dans le SELECT * alors qu’elles ne sont pas utilisées.
Un SELECT DISTINCT permet d’éviter les lignes dupliquées, lorsque vous avez plus d’une fois un même enregistrement. Le problème est que ce traitement est très lourd en termes de performance, surtout si certains des champs utilisés avec la clause DISTINCT ne sont pas indexés. De plus, il faut s’assurer que ce traitement soit utile car il arrive assez souvent qu’il n’y ait pas de lignes en double. Enfin, il est souvent possible d’éviter ce traitement en repensant et programmant différemment la clause WHERE.
D’une manière générale, il n’est pas recommandé d’accéder à une base de données à travers une boucle, surtout pour des traitements de mise à jour des données (INSERT, UPDATE, MODIFY, DELETE). Généralement, la boucle indique que l’on parcourt une table ou un ensemble d’enregistrements afin d’effectuer cette mise-à-jour. Le problème de performance peut s’avérer infime pour une nouvelle application avec une table contenant peu d’enregistrements, mais au fil du temps les données vont s’ajouter dans cette table, sa taille va grandir, la boucle sera de plus en plus longue et les problèmes de performance commenceront à apparaître.
Un SELECT sans condition, c’est-à-dire sans clause WHERE, ramène tous les enregistrements de la table. Il est souvent l’indication d’une erreur de programmation (oubli) ou d’une programmation incorrecte. Là encore, avec l’ajout au fil du temps de données dans la table, ce SELECT occasionnera un problème de performance.
Ah, nous avons quand même dans ces règles critiques une bonne pratique qui ne touche pas à la performance, mais à la stabilité de l’application. Une instruction CASE … ENDCASE permet d’effectuer différents traitements (branchements) selon les différentes valeurs d’une variable ou d’un champ. La clause OTHERS permet de prévoir tous les autres cas (toutes les autres valeurs) non traités spécifiquement par le CASE. C’est une simple question de gestion des erreurs : si un cas non prévu survient, l’absence de la clause OTHERS signifie qu’aucun traitement particulier ne sera effectué. Le programme continuera de s’exécuter, peut-être de manière erroné ou avec des valeurs incorrectes. Le risque est élevé que survienne une erreur et qu’elle ne soit pas traitée. De plus, cette erreur surviendra généralement bien plus loin dans le code, et donc la détection de son origine sera souvent assez longue et coûteuse en termes de maintenance.
Certains clients considèrent que cette ‘bad practice’ est un Blocker : je suis assez d’accord avec cela.
Si nous regardons les défaut suivants dans le code, on remarque que ceux-ci portent à nouveau sur la programmation SQL.
Un UPDATE ou un DELETE sans clause WHERE signifie une mise-à-jour ou une suppression de l’ensemble des lignes de la table. Mieux vaut s’assurer que c’est bien le traitement espéré et non pas une erreur !
SAP propose un système de buffer qui permet de lire une table directement depuis celui-ci et non pas depuis la base de données, dans un objectif de meilleure performance. Or des traitements tels qu’un JOIN ou des fonctions de calcul impliquant une agrégation (COUNT, AVG, MAX, MIN, etc.) on pour effet d’interdire l’utilisation de ce buffer. Et cela, le programmeur ne le sait pas toujours !
A fortiori, un traitement qui ‘bypasse’ le buffer – c’est-à-dire évite volontairement celui-ci – devra être sérieusement justifié.
Enfin, l’utilisation d’une clause LIKE ne permet pas, une fois de plus, de prédire le nombre d’enregistrements retournés, notamment lorsque la table croît au fil du temps. Et les queries imbriqués (SELECT … IN … SELECT) sont à éviter, dans tous les langages.
Il reste encore d’autres règles critiques que nous n’avons pas vu, tout simplement parce que je n’ai pas rencontré de défauts de ce type dans le code que j’ai analysé. Par exemple,
- Avoid SQL queries joining on too many tables
- Avoid using GROUP BY in queries
Mais l’objectif n’est pas de lister toutes les ‘bad practices’ possibles en matière de programmation SQL. Par contre, certaines violations critiques sont spécifiques à l’ABAP. Par exemple : « Avoid too many functions in function pool ».
Un ‘function pool’ est un groupe de fonctions utilisées (appelées) par un programme. Lors de cet appel, l’ensemble des fonctions sont montées en mémoire. Et ce pour la durée de vie complète du programme. Donc plus ce module comportera de fonctions, plus son temps de chargement en mémoire et l’espace pris dans celle-ci sera important, ce qui peut occasionner un problème de performance. SONAR liste automatiquement tous les function pools avec plus de 15 fonctions.
Nous avons également quelques ‘best practices’ qui ne concernent pas la performance mais, tout comme le ‘CASE … OTHERS’, présentent un risque pour la robustesse de l’application :
- Forbid use of INSERT/DELETE REPORT/TEXTPOOL
- Forbid use of GENERATE REPORT / SUBROUTINE POOL / DYNPRO
Ces instructions sont prohibées car réservées à SAP (parce qu’elles accèdent à des données du noyau qui peuvent disparaître dans les prochaines versions).
La dernière règle « Prevent use of EDITOR-CALLS » porte sur une instruction qui permet d’appeler l’éditeur de texte de SAP. Par contre, cette instruction ne permet pas de contrôler les droits de l’utilisateur, comme c’est le cas de tout appel à une transaction ABAP. Autrement dit, le programmeur peut donner accès en édition à quelqu’un qui n’a pas cet accès. Cette instruction est donc abandonnées, au profit d’autres permettant ce même traitement de manière plus contrôlée.
Qu’est ce qui justifie qu’une règle soit de type ‘Blocker’ ou ‘Critical’ ? Après tout, on pourrait considérer que certaines des violations rencontrées dans cet article sont suffisamment graves pour rejoindre la catégorie des défauts bloquants. Certes, mais la différence est qu’on n’admettra aucune exception pour un ‘Blocker’ alors que cela peut s’avérer le cas pour les les règles ‘Critical’. Bien sûr, cette exception devra toujours être justifiée, mais vous pouvez accepter que ce code passe en production le temps de décider si une correction doit être effectuée ou non.
Nous terminerons dans le prochain article la présentation des régles de notre Quality Profile SONAR pour ABAP.
Cette publication est également disponible en Leer este articulo en castellano : liste des langues séparées par une virgule, Read that post in english : dernière langue.