Ces messages sont destinés aux autres développeurs
Ces messages devraient être lus par les développeurs pour les aider à déboguer l'application. Cela peut prendre deux formes:
Débogage actif. Vous exécutez en fait un débogueur tout en écrivant du code et en essayant de comprendre ce qui se passe. Dans ce contexte, une exception utile vous guidera en facilitant la compréhension de ce qui ne va pas ou en suggérant éventuellement une solution de contournement (bien que cela soit facultatif).
Débogage passif. Le code s'exécute en production et échoue. L'exception est enregistrée, mais vous obtenez uniquement le message et la trace de pile. Dans ce contexte, un message d'exception utile vous aidera à localiser rapidement le bogue.
Étant donné que ces messages sont souvent enregistrés, cela signifie également que vous ne devez pas y inclure d'informations sensibles (telles que des clés privées ou des mots de passe, même si cela peut être utile pour déboguer l'application).
Par exemple, le IOSecurityException
type d'une exception levée lors de l'écriture d'un fichier n'est pas très explicite sur le problème: est-ce parce que nous n'avons pas les autorisations d'accès à un fichier? Ou peut-être pouvons-nous le lire, mais pas l'écrire? Ou peut-être que le fichier n'existe pas et que nous n'avons pas les autorisations pour y créer des fichiers? Ou peut-être qu'il est verrouillé (espérons que le type de l'exception sera différent dans ce cas, mais dans la pratique, les types peuvent parfois être cryptiques). Ou peut-être que la sécurité d'accès au code nous empêche d'effectuer des opérations d'E / S?
Au lieu:
IOSecurityException: le fichier a été trouvé mais l'autorisation de lire son contenu a été refusée.
est beaucoup plus explicite. Ici, nous savons immédiatement que les autorisations sur le répertoire sont définies correctement (sinon, nous ne pourrons pas savoir que le fichier existe), mais les autorisations au niveau du fichier sont problématiques.
Cela signifie également que si vous ne pouvez pas fournir d'informations supplémentaires qui ne sont pas déjà dans le type de l'exception, vous pouvez laisser le message vide. DivisionByZeroException
est un bon exemple où le message est redondant. D'un autre côté, le fait que la plupart des langues vous permettent de lever une exception sans spécifier son message se fait pour une raison différente: soit parce que le message par défaut est déjà disponible, soit parce qu'il sera généré plus tard si nécessaire (et la génération de ce message est inclus dans le type d'exception, ce qui est parfaitement logique, "OOPly" parlant).
Notez que pour des raisons techniques (souvent de performances), certains messages finissent par être beaucoup plus cryptés qu'ils ne devraient l'être. .NET NullReferenceException
:
La référence d'objet n'est pas définie à une instance d'un objet.
est un excellent exemple d'un message qui n'est pas utile. Un message utile serait:
La référence d'objet product
n'est pas définie sur une instance d'un objet lorsqu'elle est appelée product.Price
.
Ces messages ne sont pas destinés aux utilisateurs finaux!
Les utilisateurs finaux ne devraient pas voir les messages d'exception. Jamais. Bien que certains développeurs finissent par montrer ces messages aux utilisateurs, cela entraîne une mauvaise expérience utilisateur et de la frustration. Messages tels que:
La référence d'objet n'est pas définie à une instance d'un objet.
ne signifie absolument rien pour un utilisateur final et doit être évité à tout prix.
Le pire des cas est d'avoir un try / catch global qui lève l'exception à l'utilisateur et quitte l'application. Une application soucieuse de ses utilisateurs:
Gère les exceptions en premier lieu. La plupart d'entre eux peuvent être manipulés sans déranger l'utilisateur. Le réseau est en panne? Pourquoi ne pas attendre quelques secondes et réessayer?
Empêche un utilisateur de diriger l'application vers un cas exceptionnel. Si vous demandez à l'utilisateur d'entrer deux nombres et de diviser le premier par le second, pourquoi laisseriez-vous l'utilisateur entrer zéro dans le deuxième cas afin de le blâmer quelques secondes plus tard? Qu'en est-il de surligner la zone de texte en rouge (avec une info-bulle utile indiquant que le nombre doit être différent de zéro) et de désactiver le bouton de validation jusqu'à ce que le champ reste rouge?
Invite un utilisateur à effectuer une action dans un formulaire qui n'est pas une erreur. Il n'y a pas suffisamment d'autorisations pour accéder à un fichier? Pourquoi ne pas demander à l'utilisateur d'accorder des autorisations administratives ou de choisir un autre fichier?
Si rien d'autre ne fonctionne, affiche une erreur utile et conviviale qui est spécifiquement écrite pour réduire la frustration de l'utilisateur, aider l'utilisateur à comprendre ce qui s'est mal passé et éventuellement résoudre le problème, et également l'aider à prévenir l'erreur à l'avenir (le cas échéant).
Dans votre question, vous avez suggéré d'avoir deux messages dans une exception: un message technique pour les développeurs et un pour les utilisateurs finaux. Bien qu'il s'agisse d'une suggestion valable dans certains cas mineurs, la plupart des exceptions sont produites à un niveau où il est impossible de produire un message significatif pour les utilisateurs. Prenez DivisionByZeroException
et imaginez que nous ne pouvions pas empêcher l'exception de se produire et que nous ne pouvons pas la gérer nous-mêmes. Lorsque la division se produit, le framework (puisque c'est le framework, et non le code métier, qui lève l'exception) sait-il quel sera un message utile pour un utilisateur? Absolument pas:
La division par zéro s'est produite. [D'ACCORD]
Au lieu de cela, on peut le laisser lever l'exception, puis l'attraper à un niveau supérieur où nous connaissions le contexte commercial et pourrions agir en conséquence afin d'aider réellement l'utilisateur final, montrant ainsi quelque chose comme:
Le champ D13 ne peut pas avoir la valeur identique à celle du champ E6, car la soustraction de ces valeurs est utilisée comme diviseur. [D'ACCORD]
ou peut-être:
Les valeurs signalées par le service ATP ne correspondent pas aux données locales. Cela peut être dû au fait que les données locales ne sont pas synchronisées. Souhaitez-vous synchroniser les informations d'expédition et réessayer? [Oui Non]
Ces messages ne sont pas destinés à l'analyse
Les messages d'exception ne devraient pas non plus être analysés ni utilisés par programme. Si vous pensez que l'appelant peut avoir besoin d'informations supplémentaires, incluez-les dans l'exception côte à côte avec le message. Ceci est important, car le message peut être modifié sans préavis. Le type fait partie de l'interface, mais le message ne l'est pas: ne vous y fiez jamais pour la gestion des exceptions.
Imaginez le message d'exception:
La connexion au serveur de mise en cache a expiré après avoir attendu 500 ms. Augmentez le délai d'expiration ou vérifiez la surveillance des performances pour identifier une baisse des performances du serveur. Le temps d'attente moyen pour la mise en cache du serveur était de 6 ms. pour le dernier mois, 4 ms. pour la dernière semaine et 377 ms. pour la dernière heure.
Vous souhaitez extraire les valeurs «500», «6», «4» et «377». Réfléchissez un peu à l'approche que vous utiliserez pour effectuer l'analyse, puis poursuivez la lecture.
Vous avez l'idée? Génial.
Maintenant, le développeur d'origine a découvert une faute de frappe:
Connecting to the caching sever timed out after waiting [...]
devrait être:
↓
Connecting to the caching server timed out after waiting [...]
De plus, le développeur considère que le mois / semaine / une heure ne sont pas particulièrement pertinents, il fait donc également un changement supplémentaire:
Le temps d'attente moyen pour la mise en cache du serveur était de 6 ms. pour le dernier mois, 5 ms. pour les dernières 24 heures et 377 ms. pour la dernière heure.
Que se passe-t-il avec votre analyse?
Au lieu d'analyser, vous pouvez utiliser des propriétés d'exception (qui peuvent contenir tout ce que vous voulez, dès que les données peuvent être sérialisées):
{
message: "Connecting to the caching [...]",
properties: {
"timeout": 500,
"statistics": [
{ "timespan": 1, "unit": "month", "average-timeout": 6 },
{ "timespan": 7, "unit": "day", "average-timeout": 4 },
{ "timespan": 1, "unit": "hour", "average-timeout": 377 },
]
}
}
Est-il facile d'utiliser ces données maintenant?
Parfois (comme dans .NET), le message peut même être traduit dans la langue de l'utilisateur (à mon humble avis, traduire ces messages est absolument faux, car tout développeur devrait être capable de lire en anglais). L'analyse de tels messages est presque impossible.