Pour une éthique de l’informaticien : l’utilisateur au centre du système d’informations

Bouygues Telecom, pour ses 20 ans, a lancé une campagne de publicité dont le slogan est « We love technology ». Cette accroche anglophone posée, le sous-texte est un chef d’oeuvre de littérature publicitaire : « De notre premier portable à nos derniers Snapchats, de notre premier coup d’œil à nos mails le matin, à nos derniers SMS envoyés, de nos premières vidéos à succès à nos dernières séries préférées en replay… Si la technologie a pris tant de place dans nos vies, c’est sans doute parce qu’on l’aime. Et comme chez Bouygues Telecom nous partageons cette passion avec vous, nous sommes heureux grâce à notre 4G, nos forfaits et nos Box, de la rendre accessible à tous. ». Passons sobrement sur l’énumération d’outils offerts via les offres Bouygues Telecom de la première phrase et la promesse publicitaire de la dernière phrase – chez nous vous aurez les meilleurs technologies et pour pas cher, venez venez – pour nous concentrer sur le slogan et son explication.

Le rédacteur nous sauve de la crétinerie fondamentale de déclarer notre flamme à notre téléphone ou à notre modem par l’emploi de la paradoxale locution « sans doute », dont le rôle est justement d’en introduire un, de doute. Mais le plus intéressant est à mon sens l’utilisation du terme anglais « technology » et en parallèle l’emploi du singulier pour le mot français « technologie » ; dans la langue française, celui-ci a trois acceptions :

  • la science et l’étude des techniques et des outils en général
  • l’ensemble des termes propres à un art qui n’est plus très usité
  • un champ technique particulier : la technologie médicale, la technologie du moteur à explosion, etc. On parlera alors plutôt des technologies

En anglais, on n’en retrouve que deux : d’une part, le premier sens français, et d’autre part l’ensemble de toutes les techniques d’une culture ou d’un peuple. Comme Bouygues Telecom ne parle pas d’un amour immodéré de ses clients pour la science étudiant les techniques, et qu’aucun champ particulier n’est précisé, le singulier français « technologie » doit être compris comme un anglicisme signifiant les techniques et outils rassemblés en un tout. Une question vient alors immédiatement à l’esprit : en dehors de l’idée aberrante qu’on puisse aimer un ensemble aussi gigantesque et disparate, cela a-t-il un sens de considérer l’ensemble des techniques comme un tout cohérent ? Entre en scène le sociologue français Jacques Ellul.

L’avènement de la Technique

Dans « Le système technicien », en 1977, il décrivit l’émergence de la Technique – terme qui a dans son oeuvre le même sens que le « technology » anglais – comme l’ensemble des techniques précédemment séparées mais désormais unies par l’informatique. C’est une entité sans but particulier mais en perpétuel accroissement, qui structure nos modes de pensée et nos façons de vivre, mais qui ne possède ni morale, ni éthique propres. L’intuition géniale de ce grand penseur, étrangement méconnu en France, est d’avoir vu que l’informatique jouerait le rôle de cerveau organisant le travail des divers champs technologiques qui eux existent depuis qu’homo sapiens s’est différencié mais sont toujours restés dépendants d’une orchestration humaine. Les exemples récents de projets d’usines entièrement automatisées, sans aucune intervention humaine, combinant machines et intelligences artificielles faibles, prouvent qu’une version concrète de sa vision abstraite et sociologique d’un système technicien autonome se rapproche :

Je ne partage pas toutes les idées de Jacques Ellul – en particulier, il compare la Technique à un cancer de la société de par sa propension à croître apparemment sans limites et à phagocyter son hôte, mais au contraire des cellules cancéreuses qui utilisent toute leur énergie à la multiplication et ne remplissent plus leurs tâches habituelles, les éléments individuels constituant la Technique rendent globalement les services qu’on attend d’eux – mais sa réflexion me semble centrale dans la constitution d’une éthique pour le concepteur de système que je suis, sujet qui n’a jamais été abordé pendant ma formation, et qui me parait totalement absent des préoccupations quotidiennes des ingénieurs.

Ethique et conception des systèmes informatiques

La réflexion éthique sur le sens de la technique a un intérêt philosophique et sociologique évident pour l’ingénieur, dont on se demande bien pourquoi elle serait la seule profession qui ne s’interrogerait pas sur sa place et son rôle dans la société. Dans cet article, je vais tenter de démontrer qu’elle a également un intérêt plus pragmatique dans la pratique quotidienne du métier.

De fait, l’informatique est souvent définie et comprise plus comme un ensemble de technologies que comme une science, et cela a des conséquences évidentes sur la formation des informaticiens : celle-ci est la plupart du temps très technique, se concentrant sur les paradigmes de programmation et d’architecture – programmation fonctionnelle, objet, impérative ou déclarative, déploiement proche du métal ou en machine virtuelle, architecture locale ou distribuée, serveur physique ou machine virtuelle, sans même parler des langages eux-mêmes – et oubliant les aspects conceptuels du métier – ce qu’est une information, pourquoi et comment on la manipule et on la présente, et le sens qu’elle a pour les utilisateurs.

On retrouve évidemment ensuite cette tendance technoïde en entreprise, et dans l’environnement actuel où quasiment tous les postes de travail sont équipés d’ordinateurs – à part celui des chefs pour accentuer leur différence – et où tous les processus de l’entreprise font intervenir l’informatique comme un élément central, les techniciens informatiques se voient conférer un sentiment de toute puissance dont le costume IBM, le consultant SAP et le créateur de startup maître des algorithmes furent tour à tour des symboles. Bien sûr, les programmeurs ne sont pas des robots codeurs, ils ont un avis sur les interfaces utilisateur et la meilleure façon de modéliser les processus, étant eux-mêmes utilisateurs ; mais si vous faites vos applications suivant vos critères de technicien spécialiste, et que vous cédez à la tendance facile de considérer que les utilisateurs doivent s’adapter à l’outil informatique et pas l’inverse ou qu’un logiciel pas franchement adapté soit toujours meilleur que pas de logiciel du tout, il y a de bonnes chances pour que vos créations ne conviennent qu’à vous et vos collègues, qui constituez une portion assez faible du marché. La prise en compte des desiderata des utilisateurs et de leur façon d’exercer leur activité doit donc être une préoccupation constante des programmeurs.

WP_20160608_12_51_32_Pro
Mais puisque je te dis que SAP est incontournable pour ta PME artisanale !

Les préoccupations éthiques mises en avant par Jacques Ellul rejoignent cette réflexion : si nous experts techniques concevons les outils que nous créons comme devant s’imposer aux utilisateurs, voire même les transformer et les améliorer comme le promeuvent les transhumanistes – vous êtes les inadaptés, amis primates – plutôt que de les assister dans leurs tâches, son système technicien aveugle et amoral ne peut qu’advenir. Cette réflexion n’est bien entendu pas très originale ; les méthodes agiles qui se structurent depuis le début du siècle reconnaissent le rôle central de l’utilisateur, jusqu’à l’intégrer formellement dans l’équipe de développement. Cependant, un aspect du problème, au cœur de ma définition de la science informatique, me semble rester dans l’angle mort des méthodes accessibles aux programmeurs, bien qu’il soit étudié sous diverses formes par les chercheurs : la différence entre une donnée et une information, entre un chiffre brut et une donnée contextualisée.

Les frontières du système d’informations

De manière classique, on définit un programme comme des entrées, un algorithme, et des sorties, ce qui se traduit dans l’esprit des développeurs comme les entrées que je récupère de mon interface utilisateur, le traitement défini dans les spécifications, et les sorties que j’affiche dans mon interface utilisateur. En fonction des sensibilités aux questions d’ergonomie, ils travailleront plus ou moins celle de leur logiciel, mais rares sont ceux qui considéreront comme moi que l’interface utilisateur ne constitue pas la frontière du système d’informations, et c’est une source majeure d’échecs pour les projets informatiques.

En effet, quel que soit le soin apporté à l’interface utilisateur, la qualité de l’enchaînement des écrans et des contrôles de saisie, la précision et la pertinence des messages d’alerte et d’erreurs, si la modélisation du processus n’a pas pris en compte la façon dont est obtenue la donnée par l’utilisateur du système avant d’être saisie et son adéquation avec ce qui est attendu, ou encore la façon dont sera interprétée et utilisée dans le monde réel une information affichée, les risques d’erreur sont multiples et aboutissent à rendre le système inopérant voire délétère :

  • en écriture, l’utilisateur n’a pas l’information souhaitée et l’invente ou la déduit selon des règles personnelles inconnues du concepteur
  • pire, il croit de bonne foi l’avoir, mais se trompe sur ce qui est demandé
  • la précision de la donnée n’est pas adéquate, que ce soit insuffisant ou superfétatoire ; ce risque est particulièrement pregnant pour le calcul scientifique dont les optimisations dépendent lourdement de la précision attendue
  • en lecture, les mêmes écueils existent pour la réutilisation d’une information affichée, que ce soit par une incompréhension de ce qu’elle signifie ou du fonctionnement de l’algorithme qui l’a générée

La leçon à tirer de ces réflexions est que les utilisateurs font partie intégrante du système d’information ; il est donc fondamental de s’enquérir de leur façon de concevoir leur métier, les processus auxquels ils participent et les tâches qu’ils accomplissent, mais également connaitre et partager le vocabulaire qu’ils emploient pour désigner les entités appelées à être manipulées par le système. Une fois la mise en production effectuée, il faut contrôler régulièrement la façon dont le logiciel est employé, y compris en dehors de toute phase d’évolution, et pas uniquement en analysant des statistiques d’utilisation, mais également en regardant les utilisateurs s’en servir. Je me souviens au début de ma carrière d’un client allant chercher dans le code source HTML de la page une information qui était accessible en un clic à l’aide d’une fonctionnalité documentée !

Conclusion

Collègues développeurs, architectes et consultants, si la place centrale qu’occupe l’informatique dans le monde d’aujourd’hui ne suffit pas à vous convaincre de l’importance d’une réflexion sur l’éthique de votre profession et l’impact social voire anthropologique de votre industrie, j’espère vous avoir convaincu que vous en préoccuper vous permettra d’être plus efficace et apprécié de vos clients sur le long terme, et gardez à l’esprit qu’aussi imparfaits et incontrôlables qu’ils soient, en attendant l’avènement de Skynet ou d’Omnius, les utilisateurs humains sont des rouages indispensables de vos systèmes d’information que vous devez placer en tête de vos préoccupations lors de la conception, du développement et de la maintenance de vos logiciels.

Analyser les erreurs

Comme tout ce qui n’est pas testé ne fonctionne pas, tout développeur a forcément des erreurs à corriger dans son code ; je laisse la démonstration de cette assertion au lecteur. D’après mon expérience, la capacité à bien utiliser les outils à sa disposition pour analyser les erreurs dans son programme est un des facteurs ayant le plus d’impact sur la rapidité des développeurs à livrer une application opérationnelle, d’autant plus pour les applications webs qui sont constituées de nombreuses couches de code interagissant les unes avec les autres. Sans prétendre à l’exhaustivité, cet article va vous donner les quelques pistes à explorer pour accélérer le processus d’analyse.

RTFL! (Read the Fucking Logs!)

De très nombreuses bibliothèques, frameworks et autres environnements d’exécution comme les navigateurs internet ou les machines virtuelles – et même les systèmes d’exploitation eux-mêmes – ont la politesse de créer des journaux dans lesquels on peut trouver des informations sur l’exécution des programmes. Rien n’est plus exaspérant pour un référent technique comme moi que de voir arriver un développeur coincé, de lui demander « que vois-tu dans les logs ? » et de s’entendre répondre « je ne sais pas, je n’ai pas regardé… ». Voici une liste de journaux utiles.

Notre framework utilise log4net de l’Apache Software Foundation pour journaliser ses opérations ; par exemple, en mode ERROR, toutes les requêtes SQL provoquant une exception sont écrites dans le journal, et en mode DEBUG, toutes les requêtes y sont écrites. Si vous souhaitez ajouter une journalisation à vos programmes, je vous conseille log4net et ses équivalents sur d’autres plateformes, l’ancêtre log4j, log4php, etc. qui sont peu invasifs, performants, flexibles et possèdent de nombreuses méthodes d’écriture (fichier, journal d’évènement sous Windows, email, console, bases de données, trace ASP.NET, socket, etc.).

PHP peut afficher les erreurs dans la sortie HTML, ce qui est une très mauvaise idée pour des raisons de sécurité ; du coup, les administrateurs désactivent généralement complètement la journalisation des erreurs, alors qu’elles peuvent être très facilement envoyées vers un fichier précis ou vers les journaux système. Quand je débarque dans un code PHP inconnu, je commence généralement par activer la sortie des erreurs, et là c’est festival avant même d’avoir commencé à corriger les problèmes qu’on m’avait soumis !

Sous Windows, le journal d’événements est une mine d’or d’informations pour le débogage. Les services du système d’exploitation y écrivent toutes leurs erreurs, par exemple IIS ou le service de relais SMTP qui y met les messages d’erreur des serveurs SMTP distants. La machine virtuelle .NET y journalise les exceptions non interceptées avec la pile d’appels au moment de l’erreur. L’onglet « Sécurité » contient les erreurs d’authentification et, encore plus intéressant, les traces des audits d’accès aux ressources. Ce dernier outil, assez méconnu, est un don du ciel quand vous ne comprenez pas un problème d’accès à un fichier ; en effet, plus les problématiques de sécurité se font prégnantes, plus les systèmes d’exploitation ont tendance à faire exécuter les programmes par des profils à faibles autorisations, voire à ne pas charger totalement les profils – une spécialité de Windows. Si vous souhaitez comprendre pourquoi vous n’avez pas accès à tel ou tel fichier, vous devez effectuer les opérations suivantes :

  • cibler la ressource
  • activer l’audit sur la ressource (pour un fichier ou un répertoire, aller dans ses propriétés, onglet Sécurité, cliquer sur Avancé puis aller dans l’onglet Audit et activer les audits utiles)
  • activer l’audit sur la machine ; pour cela, aller dans Stratégie de sécurité locale dans les Outils d’administration, déplier Stratégies locales puis Stratégies d’audit, et activer les échecs et/ou les réussites sur la catégorie qui vous intéresse
  • réitérer la tentative d’accès
  • lire le journal Sécurité, dans lequel vous saurez quel utilisateur a tenté avec ou sans succès à la ressource, depuis quel programme, etc.
  • une fois les problèmes réglés, désactiver les audits pour éviter de graver le journal pour le plaisir

Les systèmes de gestion de bases de données relationnels peuvent, en fonction de leur configuration, écrire des traces des requêtes erronées – configuration par défaut de PostgreSQL – ou des requêtes coûteuses – configuration par défaut de MySQL. Ils supportent tous l’instruction EXPLAIN donnant le plan d’exécution, ont des outils de trace avancés, tiers ou natifs, capables de proposer des index pour accélérer les requêtes.

Les navigateurs internet modernes ont des outils pour les développeurs qui auraient fait baver le petit François à ses débuts de développeur web en 1997 : console avec toutes les erreurs, suivi de variables javascript, débogueur avec points d’arrêt et instruction d’entrée dans le débogueur (debugger), liste de règles CSS appliquées à un élément et possibilité de tester des modifications sur icelles, liste des ressources chargées avec source du chargement, temps d’attente du premier octet, temps de téléchargement, en-têtes HTTP en entrée et en sortie, remis en forme indentée des fichiers javascript compressés, manipulation du code HTML, simulation de résolutions tierces et d’interfaces utilisateur tactiles…

Les débogueurs modernes peuvent prendre la main sur un programme déjà lancé, sur la machine locale ou même sur une machine distante, en s’attachant à ce programme, en dehors de leur capacité bien connue de lancer un programme directement en mode débogage.

Et même quand vous n’avez pas accès à la machine, localement ou à distance, vous pouvez demander à l’utilisateur de faire un export mémoire exploitable par votre débogueur, par exemple avec procdump et Visual Studio pour Windows, natif ou .NET.

En bref, avant quoi que ce soit d’autre, RTFL!

Le mille-feuilles de l’informatique distribuée

Très longtemps, les programmes informatiques se sont exécutés sur une machine unique, sans collaboration avec d’autres ; au pire, on avait affaire à un serveur tout-puissant et un terminal d’affichage stupide.

Aujourd’hui, avec les développement des réseaux, le gros des applications d’entreprise et même grands public distribuent les tâches à plusieurs ordinateurs collaborant pour rendre le service attendu. C’est en particulier le cas du développement web, où vous avez au minimum un serveur manipulant des données de référence pour les rendre intelligibles et modifiables par un client exécutant l’interface utilisateur dans un navigateur web. Le cas général  comprend côté serveur des systèmes de stockage de données ayant leur propre langage, SQL ou non, un serveur web pour gérer les connexions réseau, une plateforme logicielle intégrée au serveur web pour tout lier, et côté client du code HTML et CSS définissant l’apparence des écrans et du code javascript pour gérer l’interaction avec l’utilisateur.

En cas de bogue, la couche dans laquelle il survient est rarement difficile à trouver, même si certaines erreurs réseau peuvent être un peu plus vicieuses – comme les validations de certificats SSL. Par contre, il peut s’avérer nettement plus compliqué de déterminer dans quelle couche la correction doit être effectuée : si la reptation jusqu’au client d’une donnée stockée dans une base relationnelle provoque une erreur dans le code javascript, par exemple une « ‘ » non échappée dans une chaîne de caractères, l’erreur se manifeste dans le code javascript exécuté sur le poste du client, alors que la correction à apporter doit l’être dans le code serveur le générant – et pas dans la base, dont les données doivent être agnostiques de la façon dont elles seront utilisées.

Il n’existe pas de règle s’appliquant à toutes les architectures distribuées pour débusquer ce genre d’erreurs, mais en être conscient est déjà un début de solution.

Utiliser les moteurs de recherche

Quand j’ai commencé à programmer, internet n’existait pas pour le grand public ; nous n’avions comme seules ressources que la documentation du langage – 700 pages sur papier bible pour Turbo Pascal 5.5 – et les articles de magazines et livres que nous pouvions trouver en français, autant dire pas grand chose.

Aujourd’hui, il est rare de tomber sur un problème dont personne n’a publié la solution sur internet, mais encore faut-il savoir comment chercher.

Première règle : chercher en anglais. On peut hurler à l’hégémonie anglo-saxonne autant que l’on voudra – j’écris en français volontairement pour créer du contenu francophone sur l’informatique – malgré ses 275 millions de locuteurs, notre langue est loin d’être la plus présente sur internet. Si votre message d’erreur est en français – ou dans n’importe quelle autre langue – traduisez-le du mieux que vous pouvez ; les systèmes de suggestion automatiques peuvent vous aider, et certains services existent pour certaines plateformes. Petit à petit, à force de parcourir les documentations et forums en anglais, vous acquerrez le vocabulaire ad hoc. En désespoir de cause, pour des plateformes traduisant les messages d’erreur en utilisant les paramètres régionaux comme .NET ou Java, vous pouvez forcer la langue d’exécution du programme pour obtenir le message d’erreur en anglais.

Deuxième règle : même si vous cherchez sur un site précis, n’utilisez pas la recherche intégrée du site, utilisez votre moteur de recherche préféré et sa fonctionnalité de limitation à un nom de domaine. Google ou Bing indexent mieux les sites que les sites eux-mêmes, et ils supportent la notation site:nomdedomaine.tld. De toutes façons, les meilleures ressources en dehors des documentations officielles, comme stackoverflow.com ou codeproject.com, sont par nécessité très bien construits pour les moteurs de recherche.

Troisième règle : si le message d’erreur ne donne pas de résultat, passer aux noms des fonctions employées assortis de mots clés correspondant à l’erreur et du langage de programmation ou la plateforme – .NET et Java par exemple ont tendance à avoir des noms d’objets assez semblables – et ça vous évite aussi les pages visant le grand public plutôt que les développeurs – les utilisateurs aussi voient des messages d’erreur. Les noms de méthode dans la pile d’appel peuvent aider à cibler plus précisément la recherche.

Enfin, si jamais vous résolvez un problème sans en passer par internet, ou après une longue recherche et un croisement de plusieurs ressources, rendez la solution disponible pour les copains !

Mono, Postgresql et UTF-8

Nous avons déployé récemment notre première application métier sur une plateforme Linux / Apache / Mono / Postgresql sur un serveur tiers, et nous avons eu un problème pas très facile à diagnostiquer et dont je vais partager la solution au cas où cela serve à quelqu’un.

La base de données postgresql était bien configurée en UTF-8, le paramètre serveur « client_encoding » était bien configuré en UTF-8, mais le site persistait à nous afficher les accents de la base avec des « ? », et de même, à l’enregistrement, les accents se transformaient en « ? ». Nous avons regardé la documentation de Npgsql, le driver .NET Postgresql, où nous avons trouvé un paramètre « Encoding » mais déclaré obsolète et comme valant toujours unicode. Nous avons fait exécuter « SHOW client_encoding » qui doit renvoyer la valeur du paramètre pour la session, et avons reçu « UTF-8 ».

Finalement, nous avons trouvé une documentation Npgsql chez Mono qui conseillait d’utiliser le paramètre Encoding avec la valeur UNICODE si on avait des problèmes avec UTF-8 (attention, UNICODE en respectant les majuscules !). Et effectivement, en rajoutant Encoding=UNICODE dans la chaîne de connexion, les « ? » sont redevenus des caractères accentués.

Résumons : la base est en UTF-8, la valeur par défaut pour le codage de caractères de la connexion est UTF-8, la connexion est bien en UTF-8, mais le driver Npgsql embarqué par Mono s’embête à saloper toutes les chaînes avec une option déclarée comme obsolète par les développeurs de Npgsql eux-mêmes… Merci pour ce moment !

SharpZipLib, Winzip et Office Open XML

Nous utilisons l’excellente bibliothèque native .NET SharpZipLib pour compresser et décompresser ; elle gère les formats tar, bzip2, gz et tar, fournit des implémentations bas niveau surchargeant Stream et plus haut niveau pour des tâches standards comme décompresser directement un zip dans un répertoire.

Dans le cadre de la mise en oeuvre d’un export xlsx pour notre contrôle tableau, j’ai naturellement utilisé SharpZipLib pour compresser les fichiers – xlsx est en ensemble de fichiers XML zippés. Après quelques erreurs venant du format du fichier XML inclus, qu’Excel me reportait clairement (erreur ligne x colonne y dans le fichier z), Excel continua à me dire qu’il avait du corriger mon fichier mais sans m’indiquer aucune erreur. Ouvert avec winrar, le fichier semblait parfaitement conforme ; pire, comparé à la version PHP, le contenu du zip était au caractère près le même, et pourtant Excel ne disait rien sur le fichier généré par la version PHP.

Je me suis donc naturellement tourné vers l’utilitaire de compression ; après quelques tripatouillages d’options, j’ai trouvé la bonne : UseZip64. SharpZipLib supporte Zip64, qui permet d’avoir des fichiers de plus de 4Go inclus dans le zip. Apparemment, il est connu que cette option est mal supportée par de vieilles versions de Winzip et par l’utilitaire de décompression inclus dans l’explorateur de fichiers de Windows XP ; j’ajoute à la liste au moins Excel, je n’ai pas testé avec Word ou PowerPoint mais j’imagine que ça doit être pareil. Pour que ça fonctionne, j’ai du modifier mon code de la façon suivante :

using(ZipOutputStream zos = new ZipOutpuStream(s)) {
  zos.UseZip64 = UseZip64.Off;
  ...
}

D’après la documentation, la valeur par défaut est Dynamic qui devrait déterminer si ça sert à quelque chose d’activer Zip64 ; dans mon cas, aucun fichier ne dépasse les 100ko, et pourtant il m’a activé Zip64, donc je ne sais pas trop quels sont les critères employés mais j’ai du désactiver complètement Zip64 pour qu’Excel soit content. Si ça peut servir à quelqu’un…

Ce qui n’est pas testé ne fonctionne pas

Lors de mes recherches sur la proposition d’apprendre le code à l’école du Conseil National du Numérique, j’ai retrouvé plusieurs fois l’idée selon laquelle la programmation pouvait être une activité formatrice au sens où elle promouvait un processus de création par essai – erreur où l’erreur n’était pas considérée comme une sanction définitive mais comme une étape naturelle participant à la construction de la solution. Cette idée est également très régulièrement citée comme un des facteurs de la réussite de la Silicon Valley, où un échec n’est pas considéré comme rédhibitoire, mais comme une expérience valorisable comme une autre.

Encore faut-il que les programmeurs s’intéressent au fait que leur programme soit erroné, et d’après mon expérience, c’est souvent là que le bât blesse.

C’est ballot, mais la moindre modification du code doit être testée !

Je vais reprendre un exemple récent arrivé à un de mes développeurs – si il se reconnait, rien de personnel, c’est pour l’exemple – sur une fonction plpgsql ; ça, c’est la première version qui marche (simplifiée à l’extrême bien sûr) :

CREATE OR REPLACE FUNCTION MAFONCTION(V_ID INT, V_MONPARAMETRE BOOLEAN) RETURNS INT AS $$
DECLARE
 V_RESULT INT:=1;
BEGIN
 UPDATE MATABLE SET MAVALEUR=V_MONPARAMETRE WHERE ID=V_ID;
 RETURN V_RESULT;
END;
$$ LANGUAGE plpgsql;

En relisant les spécifications, le développeur s’aperçoit que j’avais demandé que le deuxième paramètre soit optionnel et ait comme valeur par défaut false, et connaissant mal la syntaxe plpgsql, écrit le code suivant – négligeant de le tester, se disant qu’il n’y avait pas de raison que cela ne fonctionne pas :

CREATE OR REPLACE FUNCTION MAFONCTION(V_ID INT, V_MONPARAMETRE BOOLEAN) RETURNS INT AS $$
DECLARE
 V_RESULT INT:=1;
 V_MONPARAMETRE BOOLEAN:=false;
BEGIN
 UPDATE MATABLE SET MAVALEUR=V_MONPARAMETRE WHERE ID=V_ID;
 RETURN V_RESULT;
END;
$$ LANGUAGE plpgsql;

Pas de bol, il se trouve que les paramètres d’une fonction plpgsql peuvent être nommés, mais que ce n’est qu’une sorte de macro ; posgtresql va remplacer toutes les occurrences du nom choisi dans la fonction par le numéro du paramètre précédé d’un $ ; dans notre cas, postgresql ne le fera pas, puisqu’on a défini une variable dans la clause DECLARE qui écrase le paramètre en entrée, et postgresql ne fera ni erreur ni avertissement. Du coup, la valeur de V_MONPARAMETRE sera toujours false, quoi qu’on passe à la fonction. Le développeur est venu une fois fini le reste du travail demandé me dire que tout était terminé ; mes tests d’intégration n’ont bien sûr pas fonctionné, pas plus que mes tests unitaires. Le développeur était pourtant certain d’avoir vu sa fonction marcher, sans doute avant sa dernière modification qu’il considérait comme tellement triviale qu’il n’y avait pas besoin de la tester.

Un exemple historique… et autrement plus coûteux

Le premier vol d’Ariane 5 s’est achevé au bout de quelques dizaines de secondes, détruisant pour 370 millions de dollars de satellites. La source du problème ? Un module de calcul de poussée de la fusée, repris tel quel d’Ariane 4, dans lequel la poussée était stockée sur une variable d’une taille trop petite pour la nouvelle fusée, munie de moteurs plus puissants ; habituellement, en programmation, quand une variable numérique reçoit une valeur trop grande pour sa capacité de stockage, et en l’absence de vérification, la valeur passe à 0 (cas non signé) ou à la plus petite valeur négative (cas signé), donc en tout état de cause une valeur anormalement basse.

Du coup, la fusée s’est crue dans une position nécessitant une intervention immédiate et radicale du pilote automatique pour rectifier la trajectoire, intervention qui a exercé des contraintes telles sur la fusée qu’elle a commencé à se disloquer, aboutissant à l’enclenchement du mécanisme d’auto-destruction, et à un joli mais très coûteux feu d’artifice. Tout ça pour une variable stockée sur 8 bits au lieu de 9 nécessaires, dans un module repris à l’identique, mais… non testé dans les nouvelles conditions.

De ces deux exemples, on peut déduire une première règle : ne jamais oublier que quelle que soit la petitesse de la modification effectuée sur le code, ce qui n’est pas testé ne fonctionne pas.

Et deux tests valent mieux qu’un…

Une autre de mes activités, récemment en croissance, est de débusquer des trous de sécurité, que ce soit sur des sites nous appartenant ou que nous hébergeons ; rien de très évolué, plutôt du très classique : injection SQL permettant de s’identifier avec n’importe quel compte ou de consulter toute une base de données payante sans l’acheter, cross-site scripting lors de la soumission de données pour intégrer du code actif envoyant discrètement des données du site attaqué vers un tiers ou pour faire du hameçonnage, ce genre de choses. Le point commun de ces défauts de sécurité, également commun avec les classiques attaques buffer overflow à la base de la plupart des failles des systèmes d’exploitation ? Le système définit des entrées – par exemple, le premier paramètre doit être un entier, et le deuxième une chaîne de caractères de moins de 256 octets de long – mais ne vérifie pas que les valeurs passées respectent cette définition.

Tendance bien naturelle : quand on programme la fonction et les appels à la fonction puis que dans la foulée, on effectue les tests, il est assez logique de mettre en oeuvre des tests respectant les contraintes qu’on a soit même définies, elles sont assez fraîches dans notre esprit. De nombreuses façons d’éviter ces erreurs existent : faire écrire les tests par une autre équipe que l’équipe de développement est une parade classique, certains langages de programmation incluent ce type de contraintes dans la signature des fonctions comme Ada par exemple, etc. Mais aucune n’est plus efficace que d’être conscient de la problématique et de la mettre au cœur de ses pratiques !

Ces exemples nous permettent d’énoncer une deuxième règle : ne jamais faire confiance aux données envoyées par le client.

Si vous gardez ces deux règles présentes à l’esprit tout au long de vos développements, quels que soient les outils que vous utilisez, aussi protecteurs que vous pensiez qu’ils soient contre les fautes de programmation, vous aurez fait un grand pas vers la production de programmes conformes et sûrs.

SQLite et les types de données

Pour le meilleur et pour le pire, SQLite est l’unique sgbd embarqué sur les principales plateformes mobiles du marché – au moins, il est présent sur toutes, c’est déjà ça.

Pour quelqu’un d’habitué à un sgbdr comme Oracle ou Postgresql, le système de typage de SQLite peut être très déroutant et entraîner des bogues très difficiles à débusquer. Cet article va vous donner quelques trucs pour éviter les plus vicieux.

La documentation ne vous prend pas en traître : les colonnes en SQLite ne sont pas typées statiquement (cf https://www.sqlite.org/datatype3.html). Eux ont l’air de trouver ça génial, vous allez voir que ce n’est pas vraiment mon avis.

Concrètement, qu’est-ce-que ça change ? Quand vous insérez une valeur dans une colonne SQLite, SQLite décide de lui-même d’une « classe de stockage » (storage class) qui peut être NULL, INTEGER, REAL (en virgule flottante), TEXT et BLOB suivant les caractéristiques qu’il perçoit de la donnée que vous lui transmettez.

Mais alors, si SQLite décide du stockage lui-même, à quoi servent les types qui sont données dans les instructions CREATE TABLE ? La réponse est que ce ne sont pas des types, mais des « affinités de types » (type affinities), une sorte de préférence pour le stockage. Pour les exemples ci-dessous, j’emploierai la table suivante :

CREATE TABLE TEST_CRAPPY_TYPING_SYSTEM(
  t text,
  not_quite_float float
);

La première conséquence est que de ligne en ligne, la classe de stockage pour une même colonne peut varier ; par exemple :

INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test',500); -- 500 est stocké en INTEGER
INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test 2',500.5); -- 500.5 est stocké en REAL

La deuxième conséquence, encore plus rigolote, est que contrairement à l’exemple ci-dessus, la classe peut sembler complètement incompatible :

INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test 3','tralala'); -- ne fait pas d'erreur et est stocké en TEXT

Ce qui peut amener si on ne fait pas attention à des cas très vicieux :

INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test 4','4,567'); -- stocké sous forme de chaîne, car SQLite ne reconnait pas 4,567 comme un REAL !

Cas particulièrement vicieux car si vous faites :

SELECT not_quite_float * 100 FROM TEST_CRAPPY_TYPING_SYSTEM WHERE t='test 4';

Vous recevez un joli 400 – car SQLite fait un gros effort pour tenter de faire de votre chaîne un nombre, qui s’arrête lamentablement à la virgule ; du coup, vous avez l’impression qu’il y a bien quelque chose comme un nombre dans votre valeur, sauf que non. Ca fait un peu penser à MySQL et son absence d’erreurs sur les débordements numériques, sauf que SQLite a l’excuse de le faire exprès et de le documenter.

Maintenant, si vous exécutez le code C# suivant (j’omets la création de la connexion et toutes les fermetures propres de ressources) :

IDdCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT not_quite_float FROM TEST_CRAPPY_TYPING_SYSTEM";
IDataReader rs = cmd.ExecuteReader();
while(rs.Read()) {
  Console.WriteLine(rs.GetValue(0).GetType().FullName);
}

Vous récupérez Int32, Decimal, String, String. C’est un peu mieux que le driver OleDb Excel qui devine le type sur les premières lignes, puis renvoie NULL si il rencontre une valeur qui ne se conforme pas au type qu’il a décidé, mais il vaut mieux être prévenu.

Conclusion : si vous voulez de la cohérence de typage avec SQLite, do it yourself, et si vous avez des résultats étranges avec les types numériques, vérifiez bien ce que vous avez donné à manger à SQLite en insertion.