À quel point devrions-nous être sur la défensive?

11

Nous avons exécuté Pex sur du code, et il a montré de bonnes choses (enfin de mauvaises choses, mais en les montrant avant qu'il ne soit mis en production!).

Cependant, l'une des bonnes choses à propos de Pex est qu'elle n'arrête pas nécessairement d'essayer de trouver des problèmes.

Un domaine que nous avons trouvé est que lors de la transmission d'une chaîne, nous ne vérifions pas les chaînes vides.

Nous avons donc changé:

if (inputString == null)

à

if (string.IsNullOrEmpty(inputString)) // ***

Cela a résolu les problèmes initiaux. Mais ensuite, lorsque nous avons à nouveau exécuté Pex, il a décidé que:

inputString = "\0";

causait des problèmes. Puis

inputString = "\u0001";

Ce que nous avons décidé, c'est que les valeurs par défaut peuvent être utilisées si nous rencontrons // ***et que nous sommes heureux de voir l'exception causée par toute autre entrée impaire (et de la traiter).

Est-ce suffisant?

Peter K.
la source
Avez-vous vérifié si vous pouvez désactiver certains des avertissements? Je sais que JTest ferait cela aussi, mais c'était aussi une option pour désactiver certaines de ses recommandations. Il a fallu un certain temps pour modifier les paramètres pour obtenir un profil de test de code que nous avons aimé.
FrustratedWithFormsDesigner
@Frustrated: Oui, il y a certainement des ajustements au profil que nous pouvons faire et faisons. Soit dit en passant, Pex est un excellent outil. Les résultats qu'il a montrés ont simplement suscité la question.
Peter K.
Je n'ai pas de réponse pour vous, mais je voulais dire merci pour ce lien vers ce truc Pex; cela a l'air plutôt bien si c'est ce que je pense que c'est et créera automatiquement (?) des tests unitaires pour le code existant. Le code sur lequel je travaille est énorme et n'a pas de tests et de code très étroitement couplé, il est donc impossible de refactoriser quoi que ce soit.
Wayne Molina
@WayneM: Oui, c'est plutôt bien, même si je devais passer par quelques exemples pour comprendre ce qu'il faisait. Pour le code hérité, c'est génial. C'est encore mieux pour le nouveau code!
Peter K.

Réponses:

9

Trois questions devraient vous aider à déterminer le degré de défensive de votre codage.

Premièrement - Quelles sont les conséquences d'une mauvaise saisie qui passe? S'il s'agit d'un message d'erreur sur l'un des PC de votre développeur, ce n'est peut-être pas si critique d'être défensif. Cela pourrait-il entraîner des perturbations financières chez les clients, une perturbation des informations comptables IE? Est-ce un système en temps réel où des vies sont en danger? Dans le scénario vie / mort, il devrait probablement y avoir plus de code de validation et de gestion des erreurs que le code de fonctionnalité réel.

Deuxièmement, combien de personnes vont réutiliser cette fonction ou morceau de code? Juste toi? Votre département? Votre entreprise? Vos clients? Plus l'utilisation du code est large, plus il est défensif.

Troisièmement - quelle est la source de l'entrée que je valide? Si elle provient de l'utilisateur ou d'un site Web public, je serais super défensif. Si l'entrée provient toujours de votre code, soyez quelque peu défensif, mais ne passez pas trop de temps à faire des vérifications.

Il sera toujours possible d'ajouter plus de vérification et de validation des erreurs dans un système. Le fait est que le coût d'écriture et de maintenance de ce code l'emportera sur le coût des problèmes causés par des erreurs dans le code.

Bork Blatt
la source
6

Les utilisateurs sont méchants et tout ce qu'ils saisissent doit être vérifié avec la plus grande rigueur.

Tout ce qui est généré sans l'avantage de l'utilisateur, ou à partir de données pré-filtrées, ne devrait pas avoir besoin d'être vérifié au même niveau. Le problème ici est lorsque vous oubliez et utilisez ces méthodes sur des données incorrectes, car vous avez oublié que le code n'a pas été renforcé.

La seule chose que vous devez toujours vérifier est tout ce qui pourrait provoquer un débordement ou un crash. Peu importe à quel point cette méthode est enfouie et à quel point vous êtes sûr que cette condition ne pourra jamais se produire. Vous devez quand même le programmer pour apaiser Murphy.

Satanicpuppy
la source
2

Je serais aussi défensif que vous devez l'être. Un peu ambigu, je suppose que oui mais je vais essayer de l'expliquer.

Lorsque vous rectifiez une méthode, si cette méthode a des paramètres d'entrée, vous devez décider de ce que vous attendez de ces paramètres. Dans les situations et les emplacements d'une application, cela diffère. Par exemple, si une méthode ou un morceau de code accepte des données provenant d'une entrée utilisateur, vous voudrez couvrir toute la base de code et gérer toute entrée en conséquence, que ce soit via un message d'erreur ou une belle façon d'afficher des données inacceptables.

Si la méthode est une API exposée idem. Vous ne pouvez pas contrôler ce qui arrive, vous devez donc vous attendre à essayer de couvrir tous les aspects et à programmer en conséquence.

Pour les méthodes produites dans le moteur principal de votre projet, vous avez ici une décision à prendre. Dois-je supposer que les données qui arrivent ont été présélectionnées et validées avant leur arrivée ou dois-je effectuer les vérifications nécessaires. Je suppose que cela dépend du niveau conceptuel de la méthode et si c'est un endroit acceptable à vérifier. Donc, les choses que je pourrais considérer sont:

1) Est-ce le seul endroit où j'aurai besoin de faire cette vérification? Cette variable devra-t-elle être vérifiée dans de nombreux endroits différents pour cette condition? Si c'est le cas, puis-je faire le contrôle une fois plus haut dans la chaîne et ensuite assumer la validité par la suite

2) D'autres composants du système devraient-ils fonctionner avec les méthodes et les interfaces que j'écris? Si tel est le cas, vous pouvez contrôler par le biais des instructions d'assertion de débogage, des exceptions de débogage, des commentaires de méthode et de l'architecture générale du système le résultat dont vous avez besoin, ou les données doivent-elles être vérifiées.

3) Quels sont les résultats de l'échec à ce stade du code. Cela fera-t-il échouer tout cela? Toute erreur sera-t-elle détectée ailleurs et cette erreur sera-t-elle au moins détectée.

4) Est-il judicieux de mettre un chèque ici? Parfois, mettre un chèque sur un morceau de données corrompues possibles, bien que contribuer à résoudre le problème à ce stade et non à une erreur, pourrait aider à le couvrir. À ce moment-là, vous pourriez passer des heures à rechercher un problème différent pour découvrir que le problème réel était dû à une vérification valide des données dans la chaîne d'événements en cascade à celui signalé par l'utilisateur / développeur.

En général, je suis un programmeur défensif, mais je crois également qu'avec un TDD approfondi et des tests unitaires appropriés, vous pouvez mettre les contrôles dans le code aux niveaux requis et être sûr qu'il fonctionne comme il le devrait une fois qu'il arrive dans les sections de niveau inférieur. .

dreza
la source
1

J'ai passé des semaines auparavant sur des bugs qui auraient pu être détectés avec 5 minutes de travail supplémentaire comme ça à l'avance. Dans mon esprit, ce travail initial en vaut toujours la peine. Vous corrigerez éventuellement le bogue. La seule question est de savoir combien de temps cela vous prendra.

Une chose que ces types d'outils d'analyse révèlent souvent, ce sont des choses qui ne sont pas nécessairement des bogues, mais de mauvaises habitudes de programmation qui rendent les bogues plus susceptibles de se produire. Une telle habitude courante est l'initialisation variable. Parfois, une chaîne vide est une valeur parfaitement valide pour une variable. Dans ce cas, vous souhaitez configurer votre outil pour ne pas considérer qu'il s'agit d'une erreur dans cette instance particulière. Cependant, souvent une chaîne vide est pas une valeur valide pour une variable, mais les gens sont l' établissement de toute façon, parce que le compilateur se plaint s'il n'y a pas quelque chose là - dedans, ou votre langue initialise automatiquement à la chaîne vide si c'est valide ou non.

Cela frustre les gens car cela semble être un catch 22 sans solution valide, mais la solution est de refactoriser votre code afin qu'il n'ait pas besoin d'initialiser la variable jusqu'à ce qu'il y ait une valeur valide à y mettre. Cela nécessite une réflexion supplémentaire à l'avance, mais rend votre code beaucoup plus robuste et est en fait plus facile à écrire et à maintenir à long terme.

Karl Bielefeldt
la source