Quelle redondance / robustesse un logiciel complexe devrait-il implémenter?

12

L'objectif de cette question: certains logiciels effectuent un "travail supplémentaire" afin d'augmenter les chances d'un résultat "finalement réussi / satisfaisant", malgré une ou plusieurs erreurs internes dans le logiciel, ce qui nécessite un temps d'exécution plus long lorsque ces erreurs se produisent. Tout cela se produit à l'insu de l'utilisateur si le résultat est positif.

Définition d'un logiciel complexe:

  • Contient du code écrit par (contribué de) plus de 10 développeurs au cours de sa durée de vie, et non écrit dans le même laps de temps
  • Dépend de plus de 10 bibliothèques externes, chacune avec des mises en garde
  • Une tâche logicielle type (pour générer un résultat souhaité par l'utilisateur) nécessite 10 paramètres d'entrée ou plus, la plupart d'entre eux ayant des valeurs par défaut mais configurables si l'utilisateur a besoin de contrôle.
  • Plus important encore, un logiciel qui a la complexité appropriée par rapport à la tâche exécutée, c'est-à-dire qui n'est pas inutilement compliqué .

Édité: Qu'est-ce qui est complexe? Veuillez consulter Il existe une grande différence entre Complexe et Compliqué . (lien direct)

Définition de la redondance / robustesse dans cette question :
(Robustesse ajoutée sur la base des commentaires)

  • Si une tâche logicielle a échoué lorsque l'ensemble de paramètres actuel a été utilisé, essayez différents paramètres.
    • De toute évidence, il doit exister une connaissance interne du fait que ces paramètres "différents" utilisent un chemin de code différent, ce qui peut entraîner un résultat différent (espérons-le meilleur).
    • Parfois, ces différents chemins de code sont choisis en fonction des observations des bibliothèques externes.
  • À la fin, si la tâche réelle exécutée est légèrement différente de la spécification de l'utilisateur, l'utilisateur recevra un rapport détaillant l'écart.
  • Enfin, comme les paramètres configurables à plus de 10, la redondance et la génération de rapports sont également configurables.

Exemple d'un tel logiciel:

  • Migration de la base de données
    • Base de données d'entreprise
    • Base de données de contrôle des sources, etc.
  • Conversion par lots entre un document Word et un document OpenOffice, PowerPoint et OpenOffice Draw, etc.
  • Traduction automatique d'un site Web entier
  • Analyse automatique de progiciels, tels que Doxygen, mais où l'analyse doit être plus fiable (c'est-à-dire pas seulement un outil de documentation)
  • Communication réseau, où les paquets peuvent être perdus et un certain nombre de tentatives sont attendues

Cette question était à l'origine inspirée de Comment gérez-vous le code intentionnellement mauvais?
mais se concentre désormais sur une seule des causes du ballonnement logiciel. Cette question ne traite pas d'autres causes de ballonnement logiciel, telles que l'ajout de nouvelles fonctionnalités.

Peut-être lié:

rwong
la source
5
Ce n'est pas de la redondance, c'est de la robustesse ...
5
La réponse n'est-elle pas simplement "autant que nécessaire?"
Dean Harding
@Dean - absolument, c'est une exigence comme les autres. L'astuce consiste à l'expliquer ainsi qu'aux conséquences et aux coûts pour les utilisateurs, mais cela peut être fait.
Jon Hopkins
Merci @ Thorbjørn pour les commentaires. J'ai ajouté de la robustesse au titre et à la définition.
rwong
Éloignez-vous d'une ancienne base de code, sauf si vous avez 5 enfants à nourrir.
Job

Réponses:

7

C'est une question commerciale, pas technique.

Parfois, je code avec des chercheurs ou sur un prototype, donc nous allons construire quelque chose avec une très faible robustesse. S'il se casse, nous le réparons. Il ne sert à rien d'investir dans de la magie supplémentaire si nous allons jeter le code bientôt.

Mais si les utilisateurs de votre système en ont besoin pour être robuste, vous devez le construire de cette façon. Et vous devez le rendre robuste spécifiquement de la manière dont vous et eux devez maximiser le succès à long terme, tout en ignorant les types de redondance / robustesse dont ils n'ont pas besoin.

Généralement, je commence brutalement puis j'ajoute de la robustesse au fil du temps. Je pose fréquemment des questions comme cette partie du processus de planification normal. Je travaille généralement dans le style Extreme Programming, où nous faisons une longue liste de fonctionnalités souhaitées, et j'y mets également des fonctionnalités de robustesse. Par exemple, "Le système survit à l'échec de n'importe quelle boîte", se mélange avec des choses comme "L'utilisateur peut rejoindre en utilisant les informations d'identification Facebook". Quel que soit celui qui apparaît en premier, je construis en premier.

William Pietri
la source
5

Un logiciel complexe est généralement redondant, comme vous le savez probablement, mais évidemment pas parce que c'est la meilleure façon de le faire, mais parce que les développeurs ont tendance à "virer" sur le code existant plutôt que d'essayer de comprendre en détail comment fonctionne le logiciel.

Cependant, si vous me demandiez dans quelle mesure la redondance devrait être acceptable, je n'en répondrais absolument pas. La redondance est l'un des nombreux effets secondaires de la complexité, l'archnémèse de la simplicité. On peut soutenir que la simplicité devrait prendre le dessus si le temps est plus important, bien que je souligne que ceux qui soutiennent que le temps est essentiel sont rarement ceux qui prennent réellement soin de développer des logiciels. C'est généralement votre chef de projet qui vous demande de terminer le travail le plus tôt possible afin que vous puissiez revenir à des problèmes plus urgents, mais il est de votre devoir en tant que programmeur de savoir quand le travail est terminé. Je pense que le travail n'est pas fait tant que vous ne l'avez pas intégré avec succès au programme tel qu'il était censé être. Peut-être que le programme est compliqué,

Cependant, il faut dire que ce faisant, vous devrez peut-être produire du code redondant. Si le projet est déjà très redondant, il peut en fait être plus simple de continuer le modèle en supposant bien sûr que votre patron n'a pas quelques semaines à tuer, ce qui vous permet de restructurer l'ensemble du projet.

Edit: à la lumière de la reformulation de la question, je vais ajouter un peu sur la robustesse. À mon avis, la vérification des paramètres ne doit être effectuée que si A) vous acceptez un format très spécifique tel qu'une valeur de date sous forme de chaîne ou B) divers paramètres peuvent potentiellement entrer en conflit ou s'exclure mutuellement.

Avec A), les exigences que les paramètres correspondent à un format spécifique sont généralement essentielles à la nécessité de la méthode (comme une conversion à une date à partir d'une chaîne). Techniquement, cela pourrait encore arriver dans votre programme sans que cela soit une nécessité, mais je vous encourage fortement à éliminer ces possibilités et à n'accepter que les paramètres que vous connaissez représentent le type de données que vous recherchez (Acceptez une date plutôt qu'une chaîne si le le point n'est pas de convertir par exemple. Si la conversion doit également être effectuée, utilisez une méthode utilitaire pour convertir la chaîne avant de la passer à la méthode).

Comme pour B), les paramètres mutuellement exclusifs représentent une mauvaise structure. Il devrait y avoir deux classes, une qui gère un cas et une autre qui le gère d'une autre manière. Toutes les opérations courantes peuvent être effectuées dans une seule classe de base pour éviter la redondance.

Dans les situations où le nombre de paramètres d'une méthode devient 10+, je commence à considérer un fichier de propriétés contenant tous ces paramètres qui ne changeront probablement pas souvent. S'ils changent, vous pouvez enregistrer la valeur par défaut dans le fichier de propriétés et ajouter une méthode "setPropertyName ()" qui vous permet de remplacer la valeur par défaut lors de l'exécution.

Neil
la source
4
Je pense que vous avez mal compris la question. Il signifie "redondance" comme dans "ne pas mourir dans les flammes lorsqu'une erreur se produit" pas comme dans "écrire du code en double". La robustesse est un meilleur terme pour cela, comme d'autres l'ont souligné.
Adam Lear
la redondance est une chose positive dans les tâches cruciales. le corps humain en est l'exemple parfait.
Claudiu Creanga
3

Les logiciels doivent être indulgents envers les erreurs des utilisateurs et totalement intolérants aux erreurs des programmeurs.

Cela signifie que le logiciel doit être très robuste et permettre une récupération en douceur pour des choses comme les erreurs de saisie utilisateur et les erreurs de configuration du système. À tout le moins, un message d'erreur convivial indiquant où l'erreur s'est produite (zone de saisie, fichier de configuration, argument de ligne de commande, etc ...) et quelle contrainte a été violée ("doit être inférieur à X caractères", "les options valides sont [X , Y, Z] ", etc ...) Pour plus de robustesse, le logiciel peut suggérer une alternative, ou utiliser une valeur par défaut raisonnable (mais il doit toujours indiquer qu'il n'utilise pas exactement ce que l'utilisateur a spécifié).

Je ne peux pas penser à beaucoup de situations où une nouvelle tentative automatique avec différentes valeurs par défaut est justifiée, mais il y en a certaines (une nouvelle tentative automatique pour établir une liaison de communication semble raisonnable). Je suis d'accord avec @William que ce niveau de «redondance» est une décision commerciale.

D'un autre côté, il ne devrait pas y avoir de robustesse à l'exécution contre les erreurs de programmation. S'il existe des conditions préalables pour les paramètres d'une fonction, ils doivent être vérifiés avec des assertions, pas des vérifications au moment de l'exécution. C'est une énorme bête noire pour moi de voir la vérification des erreurs redondantes et les rapports sur le même paramètre à trois ou quatre niveaux dans la pile des appels:

 int A(int x)
 {
   if (x==0) return -1
   ...
 }
 int B(int x)
 {
   if (x==0) return -1
   err = A(x)
   if (err) return err;
   ...
 }
 // and so on and so on....

Il s'agit simplement d'une complexité supplémentaire inutile. Vous ne devez pas passer du temps à déterminer comment une fonction doit gérer une erreur introduite en en utilisant une autre de manière incorrecte. S'il s'agit du type de «robustesse» auquel vous faites référence - vous n'en avez pas besoin. Remplacez-le par des assertions et des tests d'intégration approfondis.

AShelly
la source
3

C'est une question d'exigences.

Y a-t-il une exigence de robustesse?

"Lorsque la liaison de communication échoue, les paquets erronés sont supprimés" "Lorsque la liaison reprend son fonctionnement, aucune transaction n'est traitée deux fois"

il devrait y avoir des cas d'utilisation pour la récupération des erreurs (sinon comment savez-vous comment cela se produira?)

Laisser le soin aux programmeurs d'inventer la robustesse au fur et à mesure (si nécessaire) se traduit par des systèmes "magiques".

Tous les systèmes magiques deviennent une magie merdique au fil du temps. La correction des erreurs en arrière-plan masque l'occurrence des défauts, de sorte que les défauts ne seront pas corrigés, de sorte que le système finira par se dégrader vers un état de plus grande entropie; et fonctionner comme de la merde, car il corrige les erreurs tout le temps. Vous devez avoir une limite pour empêcher le système d'entrer dans un état de dégradation permanente.

Tim Williscroft
la source
2

Certaines opérations justifient probablement une approche de «réessai», surtout si elles dépendent de ressources externes comme une base de données. Par exemple, si une base de données ne peut pas être connectée ou si une requête échoue, l'opération peut être réessayée un certain nombre de fois avant d'abandonner et de renvoyer une erreur à un niveau supérieur. Cependant, dans une certaine logique, essayer la même chose plusieurs fois est souvent un symptôme de mauvais code et de pensée magique qui cache de vrais problèmes.

Matt H
la source
Si une nouvelle tentative se produit sans être comptée et signalée / visible, vos systèmes se dégraderont et finiront par mal fonctionner en raison de leur nature d'autocorrection magique. Utilisez toujours des compteurs de godets qui fuient (PLOPD4) pour signaler les nouvelles tentatives et les erreurs. De cette façon, un niveau bas est ignoré, mais les problèmes deviennent visibles pour les utilisateurs.
Tim Williscroft