Écrire du code robuste contre une ingénierie excessive

33

Comment savez-vous que vous écrivez le code le plus robuste possible sans trop d'ingénierie?

Je pense trop à tous les chemins possibles que mon code peut emprunter, ce qui est parfois une perte de temps. Cela dépend du type de programme que vous écrivez, mais je ne veux pas trop utiliser mon temps pour prendre en compte des situations qui ne se produiront jamais.

Fran Sevillano
la source
2
Codez-le dans Agda2
SK-logic le
un exemple concret aiderait grandement à faire valoir votre point de vue. :)
João Portela
Puis-je simplement vérifier que vous vous interrogez vraiment sur la robustesse, c'est-à-dire "la capacité d'un système à continuer à fonctionner en présence d'entrées non valides ou de conditions environnementales stressantes", car certaines réponses semblent penser que vous parlez d'une extensibilité.
DJClayworth
Je travaille dans des délais extrêmement longs, en plus de la démo, ce qui me permet de pouvoir me débrouiller avec plaisir rapidement sans paralysie parfaite.
Job
1
Voici un article sur le sujet: code-tag.com/2017/04/02/…
San

Réponses:

39

Comment savez-vous que vous écrivez le code le plus robuste possible sans trop d'ingénierie?

Que considérez-vous comme un code robuste? Un code déjà évolutif et si puissant qu’il peut faire face à toutes les situations? Faux, personne ne peut prédire l'avenir! Et encore une fois, parce que ça va être un gâchis compliqué et impossible à maintenir.

Je respecte divers principes: YAGNI (encore) et KISS , pour ne pas écrire de code inutile. Cela empêche également efficacement l'ingénierie. Je refactorise l'application lorsque des extensions sont nécessaires. Les outils de refactoring modernes vous permettent de créer assez facilement des interfaces et d’échanger des implémentations lorsque vous en avez besoin.

Ensuite, j'essaie de rendre le code que j'écris aussi robuste que possible, ce qui implique d'éliminer autant de chemins que le programme peut emprunter (et aussi d'états) que possible et un peu de programmation spartiate . Les fonctions / méthodes "atomiques" qui ne reposent pas sur des états externes ou du moins ne laissent pas le programme dans un état inconsistant s’avèrent bien utiles. Si vous le faites bien, il est également très peu probable que vous finissiez avec du code spaghetti, ce qui est également une bénédiction pour la facilité de maintenance. De plus, dans la conception orientée objet, les principes SOLID constituent un excellent guide pour un code robuste.

J'ai vraiment découvert qu'il est souvent possible de réduire la complexité, par exemple des explosions combinatoires de chemins ou d'états de programme, en réfléchissant profondément à la manière dont vous pourriez le concevoir comme le chemin le plus direct possible. Essayez de garder les combinaisons possibles au minimum en choisissant le meilleur ordre de vos sous-routines et en les concevant à cet effet.

Le code robuste est la plupart du temps un code simple et propre, mais la simplicité est un trait difficile à atteindre. Pourtant, vous devriez vous efforcer de l'obtenir. Écrivez toujours le code le plus simple possible et n’ajoutez de la complexité que lorsque vous n’avez pas le choix.

La simplicité est robuste, la complexité est fragile.

La complexité tue.

Faucon
la source
2
Parce qu'il n'y a pas beaucoup de classes, d'usines et d'abstractions. C'est un paradoxe, mais certaines personnes aiment ça. Aucune idée pourquoi.
Codeur
5
C'est Sparta!!!
Tom Squires
4
Les gens qui ne le font pas depuis vingt ans ne comprennent tout simplement pas à quel point la complexité peut vous tuer. Ils pensent qu'ils sont si intelligents. Ils sont stupides, pas intelligents. Cette complexité va vous tuer.
PeterAllenWebb
1
La robustesse n’est pas synonyme d’avenir, mais de continuer à fonctionner avec des entrées non valides ou des environnements stressants - Code Complete p464.
DJClayworth
5
À moins que le questionneur utilise «robuste» dans un sens différent de celui que je comprends, vous répondez à une question différente. Il ne demande pas "dois-je coder pour prendre en compte les besoins futurs", il demande "quels cas d'entrée inhabituels devrais-je traiter". YAGNI, KISS et SOLID ne sont pas pertinents. Avez-vous besoin d'autoriser un million d'utilisateurs essayant de se connecter simultanément? Que se passera-t-il si un nom de connexion commence par une barre oblique inverse? YAGNI n'a répondu à aucune de ces questions.
DJClayworth
8

J'essaie de garder un équilibre en me concentrant sur

  • gérer tous les chemins d'exécution possibles dans les cas d'utilisation existants (c'est la partie "robustesse"),
  • fonctionnalités / exigences, je suis à peu près sûr que cela arrivera dans un avenir prévisible, et
  • Ce que je sais de l’expérience me sera nécessaire pour assurer la maintenance à long terme de la base de code (c’est-à-dire garder le code propre et testable).

C'est une zone frontalière floue - parfois, je réussis à faire un travail inutile, parfois, je ne fais pas quelque chose qui s'avère nécessaire par la suite. Si les échecs ne sont pas importants, je vais bien. En tout cas, je m'efforce d'apprendre de mes erreurs.

Péter Török
la source
Un exemple serait génial ici, avec tous les chemins d’exécution possibles dans les cas d’utilisation existants
CodeYogi
5

La différence entre une ingénierie robuste et une ingénierie excessive est la différence entre le traitement en douceur de tous les cas d'utilisation possibles, même les cas d'utilisation bizarres et marginaux qui NE DEVRAIENT PAS se produire. Lorsque je dis gracieusement, l'utilisateur entre un cas d'exception bizarre ou se retrouve dans une situation qui appelle une fonctionnalité non prise en charge ou non spécifiée qui n'a pas été définie et le code se ferme normalement sans provoquer de panne ou informer l'utilisateur de fonctionnalités non prises en charge.

La suringénierie, par contre, peut tomber dans le domaine de la mise en œuvre complète de fonctionnalités inutiles ou non sollicitées (certaines fonctionnalités demandées par le client ne sont jamais demandées!) OU elles peuvent être définies en dérivant une conception trop complexe ou trop complexe. code pour traiter un problème relativement simple.

arbre_érable
la source
4

1) Obtenir les exigences.

2) Écrivez le code minimum pour répondre aux exigences. Si quelque chose est ambigu, faites une supposition éclairée. Si elle est super ambiguë, revenir à 1.

3) envoyer à tester.

4) Si les testeurs disent que c'est bon, documentez l'approbation. Si quelque chose ne va pas, retournez à 1.

Concentrez-vous sur les tests et non sur les tests. Si vous n'avez pas de testeurs, procurez-vous des testeurs! Ils sont essentiels non seulement pour vérifier l'exactitude du code, mais pour tout le processus de développement.

Morgan Herlocker
la source
1
+1 pour Focus sur les tests réussis et non prévisionnels. Cependant, de nombreux développeurs comme moi sont censés faire les deux, faute d’analystes commerciaux puissants.
maple_shaft
@maple_shaft - Très vrai. Le fait est que ces problèmes résultent de l'incompétence de quelqu'un d'autre. Souligner le travail de quelqu'un d'autre est la voie de l'épuisement professionnel. Si ma société était assez bête pour que je fasse les comptes débiteurs du mois, je ne serais pas trop déçu de moi-même si cela ne tournait pas bien. Pour définir les exigences, il suffit généralement de décrire ce que vous faites tous les jours, de manière à pouvoir l’automatiser. Si aucun des employés ne peut le faire, eh bien… la société risque d’être en difficulté.
Morgan Herlocker
3

En premier lieu, gardez les données normalisées (non redondantes) autant que vous le pouvez. Si les données sont entièrement normalisées, aucune mise à jour des données ne peut les rendre incohérentes.

Vous ne pouvez pas toujours garder les données normalisées, autrement dit, vous ne pourrez peut-être pas éliminer la redondance. Dans ce cas, les états peuvent être incohérents. La chose à faire est alors de tolérer l’incohérence et de la réparer périodiquement à l’aide d’un programme quelconque qui la corrige et la corrige.

Il existe une forte tendance à essayer de gérer la redondance de manière stricte au moyen de notifications. Celles-ci sont non seulement difficiles à vérifier, mais peuvent également entraîner de graves problèmes d' efficacité. (Une partie de la tentation d'écrire des notifications est due au fait qu'elles sont pratiquement encouragées en POO.)

En général, tout ce qui dépend de la chronologie des événements, des messages, etc., devient vulnérable et nécessite des tonnes de codage défensif. Les événements et les messages sont caractéristiques des données redondantes, car ils communiquent les modifications d'une partie à une autre, en essayant d'éviter les incohérences.

Comme je l'ai dit, si vous devez avoir une redondance (et les chances sont assez bonnes, vous devez), il est préférable de pouvoir a) tolérer et b) le réparer. Si vous essayez d'éviter les incohérences uniquement au moyen de messages, de notifications, de déclencheurs, etc., il vous sera très difficile de les rendre robustes.

Mike Dunlavey
la source
3
  • écrire pour la réutilisation.
  • écrire des tests. triviales, non triviales, certaines absurdement complexes pour voir comment il gère dans de telles conditions. des tests vous aideront également à déterminer la forme de l'interface.
  • écrire le programme pour échouer durement (par exemple, assertion). mon code a une tonne de réutilisation et je teste pour une tonne de cas - il y a plus de vérification / traitement des erreurs que l'implémentation réelle (basée sur le nombre de lignes).
  • réutilisation.
  • réparer immédiatement les choses qui vont mal.
  • apprendre et construire de l'expérience.

des erreurs se produiront en cours de route, mais elles seront (heureusement) localisées et elles apparaîtront (dans la plupart des cas) très tôt dans les tests. L’autre avantage de la réutilisation est que le client / appelant peut enregistrer la plupart des erreurs de vérification / échafaudage en utilisant celles apportées par l’implémentation.

vos tests doivent ensuite définir les capacités de votre programme et leur robustesse - continuez à ajouter des tests jusqu'à ce que vous soyez satisfait des taux de réussite et des entrées; améliorer, étendre et fortifier au besoin.

Justin
la source
2

Je fais cette distinction en écrivant du code avec un comportement bien défini, mais pas nécessairement optimal, pour des passes d'exécution très improbables. Par exemple, lorsque je suis à peu près sûr (prouvé, mais non testé) qu'une matrice sera définie positive, j'insère une assertion ou une exception dans le programme pour tester l'état, mais je n'écris pas son propre chemin de code. De ce fait, le comportement est défini, mais sous-optimal.

thiton
la source
2

Robustesse: Mesure dans laquelle un système continue de fonctionner en présence d'entrées non valides ou de conditions environnementales stressantes. (Code complet 2, p464)

La question importante ici est de vous demander à quel point la robustesse est importante pour vous. Si vous êtes Facebook, il est primordial que votre site Web continue de fonctionner lorsqu'une personne entre des caractères spéciaux dans l'entrée et que votre serveur reste opérationnel lorsque 100 millions d'utilisateurs sont connectés simultanément. Si vous écrivez un script pour effectuer une opération courante que vous seul faites, vous ne vous en souciez pas beaucoup. Entre les deux il y a beaucoup de niveaux. L'une des compétences importantes qu'un développeur doit acquérir est de juger de la robustesse dont vous avez besoin.

Le principe de YAGNI s'applique à l'ajout de fonctionnalités dont un programme pourrait avoir besoin. Mais ce principe ne s'applique pas à la robustesse. Les programmeurs ont tendance à surestimer la probabilité qu'une extension donnée soit nécessaire (surtout s'il s'agit d'une solution cool), mais ils sous-estiment la probabilité que les choses tournent mal. De plus, s'il s'avère qu'une fonctionnalité omise est nécessaire après, le programmeur peut l'écrire ultérieurement. S'il s'avère qu'un contrôle d'erreur omis est nécessaire, le dommage peut être causé.

Par conséquent, il est préférable de ne pas vérifier les conditions d'erreur inhabituelles. Mais il y a un équilibre. Certaines des choses à considérer dans cet équilibre:

  • À quelle fréquence cette erreur peut-elle se produire?
  • Quel est le coût de cette erreur?
  • Est-ce pour un usage interne ou externe?

N'oubliez pas que les gens peuvent et voudront essayer d'utiliser votre programme de manière inattendue. C'est mieux si quelque chose de prévisible se produit quand ils le font.

Comme dernière ligne de défense, utilisez l'assertion ou l'arrêt. Si vous rencontrez un problème qui ne vous permet pas de résoudre le problème, fermez le programme. C'est généralement mieux que de laisser le programme continuer et faire quelque chose d'imprévisible.

DJClayworth
la source