Dans Test Driven Development (TDD), vous commencez avec une solution sous-optimale, puis vous en produisez de manière itérative de meilleures solutions en ajoutant des cas de test et en refactorisant. Les étapes sont censées être petites, ce qui signifie que chaque nouvelle solution sera en quelque sorte dans le voisinage de la précédente.
Cela ressemble à des méthodes mathématiques d'optimisation locale comme la descente de gradient ou la recherche locale. Une limitation bien connue de ces méthodes est qu'elles ne garantissent pas de trouver l'optimum global, ni même un optimum local acceptable. Si votre point de départ est séparé de toutes les solutions acceptables par une grande région de mauvaises solutions, il est impossible d'y arriver et la méthode échouera.
Pour être plus précis: je pense à un scénario dans lequel vous avez implémenté un certain nombre de cas de test, puis constatez que le prochain cas de test nécessiterait une approche concurrentielle différente. Vous devrez jeter votre travail précédent et recommencer.
Cette pensée peut en fait être appliquée à toutes les méthodes agiles qui procèdent par petites étapes, pas seulement au TDD. Cette analogie proposée entre TDD et l'optimisation locale présente-t-elle de sérieux défauts?
la source
Réponses:
Pour rendre votre comparaison plus adéquate: pour certains types de problèmes, les algorithmes d'optimisation itérative sont très susceptibles de produire de bons optima locaux, pour d'autres situations, ils peuvent échouer.
Je peux imaginer une situation où cela peut se produire dans la réalité: lorsque vous choisissez la mauvaise architecture d'une manière dont vous devez recréer tous vos tests existants à partir de zéro. Disons que vous commencez à implémenter vos 20 premiers cas de test dans le langage de programmation X sur le système d'exploitation A. Malheureusement, l'exigence 21 comprend que le programme entier doit s'exécuter sur le système d'exploitation B, où X n'est pas disponible. Ainsi, vous devez jeter la plupart de votre travail et réimplémenter dans la langue Y. (Bien sûr, vous ne jeteriez pas le code complètement, mais le porteriez dans le nouveau langage et le nouveau système.)
Cela nous apprend, même lorsque vous utilisez TDD, c'est une bonne idée de faire une analyse et une conception globales au préalable. Ceci, cependant, est également vrai pour tout autre type d'approche, donc je ne vois pas cela comme un problème TDD inhérent. Et, pour la majorité des tâches de programmation du monde réel, vous pouvez simplement choisir une architecture standard (comme le langage de programmation X, le système d'exploitation Y, le système de base de données Z sur le matériel XYZ), et vous pouvez être relativement sûr qu'une méthodologie itérative ou agile comme TDD ne vous amènera pas dans une impasse.
Citant Robert Harvey: "Vous ne pouvez pas développer une architecture à partir de tests unitaires." Ou pdr: "TDD ne m'aide pas seulement à trouver le meilleur design final, il m'aide à y arriver en moins de tentatives."
Donc en fait ce que tu as écrit
pourrait devenir vrai - lorsque vous choisissez une mauvaise architecture, vous n'atteindrez probablement pas la solution requise à partir de là.
D'un autre côté, lorsque vous faites une planification globale au préalable et choisissez la bonne architecture, l'utilisation de TDD devrait être comme démarrer un algorithme de recherche itérative dans une zone où vous pouvez vous attendre à atteindre le "maximum global" (ou au moins un maximum suffisant) ) en quelques cycles.
la source
Je ne pense pas que TDD ait un problème de maxima locaux. Le code que vous écrivez pourrait, comme vous l'avez correctement remarqué, mais c'est pourquoi la refactorisation (réécriture de code sans changer de fonctionnalité) est en place. Fondamentalement, à mesure que vos tests augmentent, vous pouvez réécrire des parties importantes de votre modèle d'objet si vous en avez besoin tout en gardant le comportement inchangé grâce aux tests. Les tests indiquent des vérités invariantes sur votre système qui, par conséquent, doivent être valides à la fois dans les maxima locaux et absolus.
Si vous êtes intéressé par les problèmes liés au TDD, je peux en mentionner trois différents auxquels je pense souvent:
Le problème d' exhaustivité : combien de tests sont nécessaires pour décrire complètement un système? Le «codage par des exemples de cas» est-il une façon complète de décrire un système?
Le problème de durcissement : quel que soit l'interface de test, il doit avoir une interface immuable. Les tests représentent des vérités invariantes , rappelez-vous. Malheureusement, ces vérités ne sont pas connues du tout pour la plupart du code que nous écrivons, au mieux uniquement pour les objets extérieurs.
Le problème des dommages liés aux tests : afin de rendre les assertions testables, nous pourrions avoir besoin d'écrire du code sous-optimal (moins performant, par exemple). Comment écrire des tests pour que le code soit aussi bon qu'il peut l'être?
Modifié pour répondre à un commentaire: voici un exemple d'échanger un maximum local pour une fonction "double" via le refactoring
Test 1: lorsque l'entrée est 0, retourne zéro
La mise en oeuvre:
Refactoring: pas nécessaire
Test 2: lorsque l'entrée est 1, retournez 2
La mise en oeuvre:
Refactoring: pas nécessaire
Test 3: lorsque l'entrée est 2, retournez 4
La mise en oeuvre:
Refactoring:
la source
Ce que vous décrivez en termes mathématiques est ce que nous appelons vous peindre dans un coin. Cet événement n'est guère exclusif à TDD. Dans la cascade, vous pouvez rassembler et remplir les exigences pendant des mois en espérant voir le maximum global uniquement pour y arriver et réaliser qu'il y a une meilleure idée juste à la prochaine colline.
La différence réside dans un environnement agile que vous ne vous attendiez pas à être parfait à ce stade, vous êtes donc plus que prêt à jeter l'ancienne idée et à passer à la nouvelle idée.
Plus spécifique à TDD, il existe une technique pour éviter que cela ne vous arrive lorsque vous ajoutez des fonctionnalités sous TDD. C'est le lieu de priorité de transformation . Lorsque TDD a un moyen formel pour vous de refactoriser, c'est un moyen formel d'ajouter des fonctionnalités.
la source
Dans sa réponse , @Sklivvz a soutenu de manière convaincante que le problème n'existe pas.
Je veux faire valoir que cela n'a pas d'importance: la prémisse fondamentale (et la raison d'être) des méthodologies itératives en général et Agile et en particulier TDD en particulier, est que non seulement l'optimum global, mais les optimums locaux aussi ne sont pas '' t connu. Donc, en d'autres termes: même si c'était un problème, il n'y a aucun moyen de le faire de manière itérative de toute façon. En supposant que vous acceptez la prémisse de base.
la source
Les pratiques TDD et Agile peuvent-elles promettre de produire une solution optimale? (Ou même une "bonne" solution?)
Pas exactement. Mais ce n'est pas leur but.
Ces méthodes fournissent simplement un "passage sûr" d'un état à un autre, reconnaissant que les changements prennent du temps, sont difficiles et risqués. Et le but des deux pratiques est de garantir que l'application et le code sont à la fois viables et éprouvés pour répondre aux exigences plus rapidement et plus régulièrement.
TDD se concentre sur la garantie que chaque "morceau" de code satisfait aux exigences. En particulier, cela permet de garantir que le code répond aux exigences préexistantes, au lieu de laisser les exigences être motivées par un mauvais codage. Mais, il ne fait aucune promesse que la mise en œuvre est "optimale" en aucune façon.
Quant aux processus Agiles:
L'agilité n'est pas à la recherche d'une solution optimale ; juste une solution de travail - dans le but d'optimiser le retour sur investissement . Il promet une solution de travail plus tôt plutôt que plus tard ; pas «optimal».
Mais, ça va, parce que la question est fausse.
Les optimaux dans le développement de logiciels sont des cibles floues et mobiles. Les exigences sont généralement changeantes et truffées de secrets qui n'apparaissent, à votre grande gêne, que dans une salle de conférence pleine de patrons de votre patron. Et la «bonté intrinsèque» de l'architecture et du codage d'une solution est évaluée par les opinions divisées et subjectives de vos pairs et celle de votre suzerain managérial - dont aucun ne peut réellement rien savoir sur un bon logiciel.
À tout le moins, les pratiques TDD et Agile reconnaissent les difficultés et tentent d'optimiser pour deux choses qui sont objectives et mesurables: Travailler contre Ne pas Travailler et Plus Tôt contre Plus Tard.
Et, même si nous avons des mesures objectives «fonctionnelles» et «plus tôt», votre capacité à les optimiser dépend principalement des compétences et de l'expérience d'une équipe.
Les choses que vous pourriez interpréter comme des efforts produisant des solutions optimales comprennent des choses comme:
etc..
Que chacune de ces choses produise réellement des solutions optimales serait une autre excellente question à se poser!
la source
Personne n'a ajouté jusqu'à présent que le "développement TDD" que vous décrivez est très abstrait et irréaliste. Cela peut être comme ça dans une application mathématique où vous optimisez un algorithme mais cela n'arrive pas souvent dans les applications métier sur lesquelles la plupart des codeurs travaillent.
Dans le monde réel, vos tests exercent et valident essentiellement des règles commerciales:
Par exemple - Si un client est un non-fumeur de 30 ans avec une femme et deux enfants, la catégorie premium est "x" etc.
Vous n'allez pas changer de manière itérative le moteur de calcul premium tant qu'il n'est pas correct pendant très longtemps - et presque certainement pas tant que l'application est en direct;).
Ce que vous avez réellement créé est un filet de sécurité afin que, lorsqu'une nouvelle méthode de calcul est ajoutée pour une catégorie particulière de clients, toutes les anciennes règles ne se brisent pas soudainement et ne donnent pas la mauvaise réponse. Le filet de sécurité est encore plus utile si la première étape du débogage consiste à créer un test (ou une série de tests) qui reproduit l'erreur avant d'écrire le code pour corriger le bogue. Ensuite, un an plus tard, si quelqu'un recrée accidentellement le bogue d'origine, le test unitaire se casse avant même que le code ne soit archivé. mais cela ne devrait pas faire partie intégrante de votre travail.
la source
Je ne pense pas que cela gêne. La plupart des équipes n'ont personne capable de trouver une solution optimale même si vous l'avez écrite sur leur tableau blanc. TDD / Agile ne les gênera pas.
De nombreux projets ne nécessitent pas de solutions optimales et ceux qui le font, le temps, l'énergie et l'attention nécessaires seront faits dans ce domaine. Comme tout le reste, nous avons tendance à commencer par le faire fonctionner. Faites vite. Vous pouvez le faire avec une sorte de prototype si les performances sont si importantes, puis reconstruire le tout avec la sagesse acquise à travers de nombreuses itérations.
Cela pourrait se produire, mais ce qui est plus susceptible de se produire, c'est la peur de modifier des parties complexes de l'application. Ne pas avoir de test peut créer un plus grand sentiment de peur dans ce domaine. L'un des avantages de TDD et d'avoir une suite de tests est que vous avez construit ce système avec l'idée qu'il devra être changé. Lorsque vous proposez cette solution optimisée monolithique dès le début, il peut être très difficile de la changer.
Aussi, mettez cela dans le contexte de votre préoccupation de sous-optimisation, et vous ne pouvez pas vous empêcher de passer du temps à optimiser les choses que vous ne devriez pas avoir et à créer des solutions inflexibles parce que vous étiez tellement hyper concentré sur leurs performances.
la source
Il peut être trompeur d'appliquer un concept mathématique comme "l'optimum local" à la conception de logiciels. L'utilisation de tels termes rend le développement logiciel beaucoup plus quantifiable et scientifique qu'il ne l'est réellement. Même si "optimal" existait pour le code, nous n'avons aucun moyen de le mesurer et donc aucun moyen de savoir si nous l'avons atteint.
Le mouvement agile était vraiment une réaction contre la croyance que le développement de logiciels pouvait être planifié et prédit avec des méthodes mathématiques. Pour le meilleur ou pour le pire, le développement de logiciels ressemble plus à un métier qu'à une science.
la source
fibonacci
, que j'ai vu utilisé comme un exemple / tutoriel TDD, est plus ou moins un mensonge. Je suis prêt à parier que personne n'a jamais "découvert" fibonacci ou toute autre série similaire en TDD. Tout le monde commence par déjà connaître fibonacci, qui est de la triche. Si vous essayez de découvrir cela en TDD, vous atteindrez probablement l'impasse à laquelle le PO demandait: vous ne pourrez jamais généraliser la série en écrivant simplement plus de tests et de refactorisation - vous devez appliquer des mathématiques raisonnement!