Existe-t-il un principe d'ingénierie logicielle qui relie le coût des tests de réutilisation et de régression sur un système de production?

12

J'ai travaillé sur un grand système de transactions financières pour une banque qui s'occupait des pensions et des investissements. Après 15 ans de changements de fonctionnalités, le coût du test de régression manuelle était passé à 200 000 $ par version. (10M LOC, 10M $ traités par jour). Ce système s'interface également avec 19 autres systèmes de l'entreprise qui déplacent de nombreuses données. Ce système a été implémenté en Java.

Ce que nous observons cependant, c'est que plus nous réutilisons, plus les coûts des tests de régression augmentent. (La raison étant que vous devez "tester le code que vous touchez" - et que le code réutilisé / partagé a un impact sur une multiplicité d'endroits quand il est touché. - nous observons une incitation financière à copier et coller du code. Cela permet de réduire les coûts des tests de régression, car nous ne voulons pas modifier le code qui pourrait être partagé, car cela entraînerait un impact important du test de régression.)

Ma question est la suivante : existe-t-il un principe de génie logiciel qui décrit la relation entre les coûts de réutilisation et de régression?

La raison pour laquelle je poserais cette question est qu'il est sans doute avantageux de décomposer le système en parties plus petites à tester.

Hypothèses:

  1. «Test de régression» signifie «test d'acceptation» - c'est-à-dire qu'un autre groupe passe du temps à écrire de nouveaux tests et à réutiliser d'anciens tests sur le système au nom de l'entreprise, y compris l'environnement et les configurations de données.

  2. Je sais que la réaction instinctive à un gros coût de test de régression est «des tests plus automatisés». C’est un bon principe. Dans cet environnement, il y a quelques défis.

    (a) Les tests automatisés sont moins utiles au-delà des frontières du système, à moins que ce système n'ait également une couverture de test automatisée élevée. (Défi sphère d'influence).

    (b) Il est culturellement difficile d'obtenir un élan sur le temps du programmeur ou un investissement en capital sur une couverture de test automatisée élevée lorsque votre système est déjà volumineux et complexe.

    (c) Le coût de la maintenance des tests automatisés est caché dans un projet et peut donc être facilement éliminé au niveau du projet.

    (d) C'est juste la réalité culturelle du travail dans une banque.

    (e) Je travaille pour résoudre ce problème d'une manière différente (décomposition).

oeil de faucon
la source
2
Vous faites la déclaration vague que « nous observons […] que plus nous réutilisons, plus les coûts des tests [d'acceptation] augmentent » - pourriez-vous développer cela? Un test d'acceptation ne devrait-il pas être indépendant des détails d'implémentation tels que les hiérarchies d'héritage et jouer à la place à travers des scénarios d'utilisation, par exemple?
amon
2
Votre question est intéressante, mais elle contient des hypothèses très biaisées comme "la réutilisation est une conséquence de l'OO" ou la partie @amon mentionnée. Je vous suggère de supprimer complètement la partie "OO", car votre question principale est indépendante du langage de programmation ou de toute programmation OO. Et il est assez difficile de savoir quel type de «réutilisation» vous avez en tête - «réutilisation» est un terme large, la réutilisation copier-coller est différente de la réutilisation de composants ou de bibliothèques.
Doc Brown
Merci, c'est utile. J'ai supprimé les références à OO - et développé l'idée de toucher le code partagé (ou le code qui deviendra du code partagé).
hawkeye
1
Dans la forme actuelle, je suppose que je répondrais simplement "non, je ne pense pas" à votre question - qui, je suppose, ne serait pas très satisfaisante pour vous. Ou êtes-vous intéressé par des principes et des pratiques pour réduire réellement les coûts de test lors de la construction d'un grand système comme le vôtre avec des composants réutilisables fabriqués par vous-même?
Doc Brown

Réponses:

11

nous ne voulons pas modifier le code qui pourrait être partagé, car cela entraînerait un impact important sur le test de régression

Les sons ci-dessus me conviennent. Plus le code est important, plus il est partagé, plus les exigences de qualité sont élevées, plus l'assurance qualité doit être impliquée lorsqu'il change.

Puisque votre système est implémenté en Java, vous pouvez voir un exemple ci-dessus, dans les bibliothèques standard Java (JDK). Ses versions majeures sont peu fréquentes et s'accompagnent de tests très gourmands. Et même des versions mineures passent par une suite de tests JCK très complète pour vérifier l'absence de régressions.

Vous pensez peut-être que cela étouffe en quelque sorte l'évolution du code partagé, et ... oui, c'est à peu près juste. Plus l'impact et le risque sont associés au changement de code, plus vous devez faire attention à le faire, plus d'efforts doivent être impliqués dans le test de ses versions.

Idéalement, la qualité des versions de code largement partagé devrait être telle qu'elle ne nécessite pas de grands changements (sauf pour des améliorations peu fréquentes). Cette ligne de pensée se reflète dans une célèbre citation de Joshua Bloch :

Les API publiques, comme les diamants, sont éternelles. Vous avez une chance de bien faire les choses, alors faites de votre mieux.


Cela dit, il semble que certains des problèmes que vous décrivez soient causés par une stratégie inefficace de développement de code partagé. En particulier, il semble particulièrement gênant que pour le code réutilisé, seules deux options soient envisagées: soit dupliquer ce code, soit l'inclure immédiatement dans les bibliothèques partagées "de base".

Il n'est pas nécessaire de se limiter à ces deux options et, encore une fois, vous pouvez trouver des exemples de la meilleure façon de le faire dans le JDK que vous utilisez. Jetez un coup d'œil aux java.util.concurrentpackages ( JSR 166 ) - jusqu'à la version Java 5, il s'agissait d'une bibliothèque distincte, qui ne faisait pas partie des versions JDK principales.

Pensez-y, c'est une troisième option que vous avez négligée, et assez pragmatique, celle que vous devez considérer lors de la "création" du nouveau code partagé. Lorsque vous venez de comprendre du code qui peut être partagé entre 2-3 composants, rien ne vous oblige à l'inclure immédiatement dans l'API de base du système.

Vous pouvez empaqueter et publier ce code partagé "immature" en tant que bibliothèque distincte, comme cela a été fait pour les utilitaires Java simultanés. De cette façon, vous évitez d'avoir à effectuer des tests de régression complets, car vous ne pouvez utiliser qu'une quantité relativement faible de composants impliqués. En conséquence, vous avez plus de latitude pour modifier et améliorer ce code partagé et pour tester son fonctionnement en production.

Une fois que votre bibliothèque est arrivée à maturité et s'est suffisamment stabilisée pour vous donner l'assurance que de nouveaux changements sont improbables, vous pouvez envisager son inclusion dans les bibliothèques principales du système, tout comme les utilitaires concurrents ont finalement été inclus dans JDK.


Un exemple concret de combien d'efforts (y compris les tests) peuvent être impliqués dans la modification de code fortement réutilisé peut être trouvé, encore une fois, dans JDK. Dans la version 7u6, ils ont changé la Stringreprésentation interne, ce qui a entraîné un changement de substringperformances. Les commentaires d'un développeur de fonctionnalités chez Reddit décrivent combien d'efforts ont été impliqués dans ce changement:

L'analyse initiale est sortie du groupe GC en 2007 ...

En interne, l'équipe de performance Oracle gère un ensemble d'applications et de références représentatives et importantes qu'elles utilisent pour évaluer les changements de performances. Cet ensemble d'applications a été crucial pour évaluer le changement de sous-chaîne. Nous avons examiné de près les changements de performances et les changements d'empreinte. Inévitablement, comme c'est le cas pour tout changement significatif, il y a eu des régressions dans certaines applications ainsi que des gains dans d'autres. Nous avons étudié les régressions pour voir si les performances étaient toujours acceptables et si l'exactitude était maintenue ...

Ma réponse ne se veut pas exhaustive mais est un très bref résumé de ce qui a été près de six mois de travail dévoué ...

moucheron
la source
Il semble que nous ayons tous les deux travaillé sur une réponse en parallèle, avec beaucoup de pensées similaires ...
Doc Brown
@DocBrown ouais, il est intéressant que nous ayons également répondu presque simultanément, environ une heure après que la question a été mise en forme
gnat
9

Je ne pense pas qu'il existe de métriques pour calculer "le coût des tests de régression / LOC du code réutilisé construit". Et je ne pense pas que quiconque ait jamais investi autant de temps et d'argent pour construire le même "gros" système deux fois, une version avec beaucoup de composants réutilisables, et une sans, pour faire des recherches sérieuses à ce sujet.

Mais j'ai vu des problèmes causés par une réutilisation comme la vôtre auparavant, et peut-être êtes-vous intéressé par quelques réflexions sur la façon de mieux gérer cela.

Tout d'abord, ce n'est pas la réutilisation qui est votre problème - c'est plutôt la tentative de construire vos propres composants réutilisables et de les utiliser dans votre système. Je suis sûr que vous réutilisez beaucoup de gros progiciels où vos problèmes ne se posent pas: pensez à la pile Java entière que vous utilisez, ou peut-être à certains composants tiers (en supposant que vous soyez satisfait de ces composants). Mais qu'est-ce qui est différent avec ce logiciel, par exemple, les bibliothèques Java, alors que vos propres composants réutilisables vous causent tellement de coûts supplémentaires pour les tests de régression? Voici quelques points qui, je suppose, pourraient être différents:

  • ces composants sont très matures et stables

  • ils sont développés et entièrement testés isolément, par une organisation complètement différente

  • pour les (ré) utiliser, vous n'avez pas à les changer (en fait vous ne pourriez pas même si vous le souhaitez, car vous ne conservez pas le code source)

  • vous n'obtenez pas quotidiennement une nouvelle version, seulement des mises à jour mineures (au maximum par mois), ou des mises à jour majeures à intervalles par an

  • la plupart des mises à jour sont conçues pour être compatibles à 100% vers le bas, en particulier les mises à jour mineures

Donc, pour rendre vos propres composants réutilisables plus efficaces, vous devez adapter certaines de ces choses d'en haut pour votre propre développement:

  • pour tout composant réutilisable, ayez une responsabilité claire qui s'occupe de la maintenance et assurez-vous que toutes les personnes qui réutilisent un composant peuvent être sûres d'obtenir une correction de bogue immédiatement si des problèmes surviennent.

  • établir des politiques strictes de version et de version. Lorsque vous faites évoluer un composant réutilisable, ne le publiez pas "à tout le monde" tous les jours (du moins, pas si cela impliquerait d'exécuter un test de régression complet de 200 000 $ sur le système). Au lieu de cela, laissez les nouvelles versions être publiées de temps en temps et fournissez des mécanismes pour permettre à l'utilisateur de ce composant de reporter la modification à la nouvelle version.

  • plus un composant est souvent réutilisé, plus il est important qu'il fournisse une interface stable et un comportement compatible vers le bas.

  • les composants réutilisables nécessitent des suites de tests très complètes pour les tester isolément.

Beaucoup de ces choses signifieront que le coût de construction du composant lui-même augmentera, mais cela diminuera également les coûts des changements causés par les régressions échouées.

Doc Brown
la source
0

Bien qu'il puisse y avoir une augmentation "observable" des coûts en raison de la nécessité de plus de tests, ce type de refactorisation rend généralement le code plus maintenable à l'avenir, car vous réduisez la dette technique du système.

Nous espérons que cela réduira les bogues futurs et facilitera la mise en œuvre de nouvelles fonctionnalités ou modifications des fonctionnalités existantes.

Par plus simple, je veux dire qu'ils devraient prendre moins de temps et donc coûter moins cher.

Réduire, plus facile et moins sont tous des termes plutôt nébuleux ici et toute économie future (ou plutôt espérée) est impossible à calculer car elle ne s'est pas encore produite.

Une base de code plus simple devrait permettre aux nouveaux employés ou aux employés existants qui entrent dans le projet de se mettre à niveau plus rapidement, en particulier pour les grands systèmes.

Cela peut également réduire le roulement du personnel dans la mesure où le moral des membres du projet pourrait être amélioré.

Bien sûr, il n'est pas garanti que vous obtiendrez ces avantages, mais ce sont des choses qui devraient être prises en compte parallèlement aux coûts (comme l'augmentation des tests) qui peuvent être mesurés.

En fait, un meilleur code devrait éventuellement réduire les coûts de test au fil du temps, même s'il y a une augmentation initiale due à ce que vous décrivez.

ozz
la source