In Test Driven Development (TDD) beginnen Sie mit einer suboptimalen Lösung und erzeugen dann iterativ bessere, indem Sie Testfälle hinzufügen und umgestalten. Die Schritte sollten klein sein, was bedeutet, dass sich jede neue Lösung in der Nähe der vorherigen befindet.
Dies ähnelt mathematischen lokalen Optimierungsmethoden wie Gradientenabstieg oder lokale Suche. Eine bekannte Einschränkung solcher Verfahren besteht darin, dass sie nicht garantieren, das globale Optimum oder sogar ein akzeptables lokales Optimum zu finden. Wenn Ihr Ausgangspunkt von allen akzeptablen Lösungen durch eine große Anzahl fehlerhafter Lösungen getrennt ist, ist es unmöglich, dorthin zu gelangen, und die Methode schlägt fehl.
Genauer gesagt: Ich denke an ein Szenario, in dem Sie eine Reihe von Testfällen implementiert haben und dann feststellen, dass der nächste Testfall einen kompetent anderen Ansatz erfordert. Sie müssen Ihre bisherige Arbeit wegwerfen und von vorne beginnen.
Dieser Gedanke kann tatsächlich auf alle agilen Methoden angewendet werden, die in kleinen Schritten ablaufen, nicht nur auf TDD. Hat diese vorgeschlagene Analogie zwischen TDD und lokaler Optimierung irgendwelche gravierenden Mängel?
quelle
Antworten:
Um Ihren Vergleich angemessener zu gestalten: Bei bestimmten Problemen führen iterative Optimierungsalgorithmen mit hoher Wahrscheinlichkeit zu guten lokalen Optima. In einigen anderen Situationen können sie fehlschlagen.
Ich kann mir eine Situation vorstellen, in der dies in der Realität passieren kann: Wenn Sie die falsche Architektur auswählen , müssen Sie alle vorhandenen Tests erneut von Grund auf neu erstellen. Nehmen wir an, Sie beginnen mit der Implementierung Ihrer ersten 20 Testfälle in der Programmiersprache X auf Betriebssystem A. Leider enthält Anforderung 21, dass das gesamte Programm auf Betriebssystem B ausgeführt werden muss, auf dem X nicht verfügbar ist. Daher müssen Sie den größten Teil Ihrer Arbeit und Neuimplementierung in Sprache Y wegwerfen. (Natürlich würden Sie den Code nicht vollständig wegwerfen, sondern ihn auf die neue Sprache und das neue System portieren.)
Dies lehrt uns, dass es auch bei der Verwendung von TDD eine gute Idee ist, vorher eine Gesamtanalyse und ein Gesamtdesign durchzuführen. Dies gilt jedoch auch für jede andere Herangehensweise, sodass ich dies nicht als inhärentes TDD-Problem sehe. Und für die meisten realen Programmieraufgaben können Sie einfach eine Standardarchitektur auswählen (wie Programmiersprache X, Betriebssystem Y, Datenbanksystem Z auf Hardware XYZ), und Sie können relativ sicher sein, dass eine iterative oder agile Methodik wie TDD bringt dich nicht in eine Sackgasse.
Unter Berufung auf Robert Harvey: "Aus Unit-Tests lässt sich keine Architektur entwickeln." Oder pdr: "TDD hilft mir nicht nur, zum besten endgültigen Design zu gelangen, sondern hilft mir auch, in weniger Versuchen dorthin zu gelangen."
Also eigentlich was du geschrieben hast
Wenn Sie sich für eine falsche Architektur entscheiden, erreichen Sie wahrscheinlich nicht die erforderliche Lösung.
Wenn Sie jedoch eine Gesamtplanung im Voraus durchführen und die richtige Architektur auswählen, sollte TDD so aussehen, als würde ein iterativer Suchalgorithmus in einem Bereich gestartet, in dem Sie ein "globales Maximum" (oder zumindest ein ausreichend hohes Maximum) erwarten können ) in wenigen Zyklen.
quelle
Ich glaube nicht, dass TDD ein Problem mit lokalen Maxima hat. Der von Ihnen geschriebene Code könnte, wie Sie richtig bemerkt haben, umgestaltet werden (Code umschreiben, ohne die Funktionalität zu ändern). Grundsätzlich können Sie mit zunehmender Anzahl Ihrer Tests wichtige Teile Ihres Objektmodells neu schreiben, wenn dies erforderlich ist, während das Verhalten dank der Tests unverändert bleibt. Tests geben unveränderliche Wahrheiten über Ihr System an, die daher sowohl in lokalen als auch in absoluten Maxima gültig sein müssen.
Wenn Sie an Problemen im Zusammenhang mit TDD interessiert sind, kann ich drei verschiedene erwähnen, an die ich oft denke:
Das Vollständigkeitsproblem : Wie viele Tests sind erforderlich, um ein System vollständig zu beschreiben? Ist "Codierung anhand von Beispielfällen" eine vollständige Beschreibung eines Systems?
Das Problem der Aushärtung : Was auch immer die Schnittstelle testet, muss eine unveränderbare Schnittstelle haben. Tests repräsentieren unveränderliche Wahrheiten . Leider sind diese Wahrheiten für den größten Teil des Codes, den wir schreiben, überhaupt nicht bekannt, allenfalls für Objekte, die nach außen gerichtet sind.
Der Test Schaden Problem: Um Behauptungen testbar zu machen, könnten wir brauchen Schreib suboptimalen Code (weniger performant, zum Beispiel). Wie schreiben wir Tests, damit der Code so gut wie möglich ist?
Bearbeitet, um einen Kommentar zu adressieren: Hier ist ein Beispiel für das Erhöhen eines lokalen Maximums für eine "doppelte" Funktion durch Refactoring
Test 1: Wenn der Eingang 0 ist, wird Null zurückgegeben
Implementierung:
Refactoring: nicht erforderlich
Test 2: Wenn die Eingabe 1 ist, wird 2 zurückgegeben
Implementierung:
Refactoring: nicht erforderlich
Test 3: Wenn die Eingabe 2 ist, wird 4 zurückgegeben
Implementierung:
Refactoring:
quelle
Was Sie in mathematischen Begriffen beschreiben, nennen wir sich selbst in einer Ecke malen. Dieses Vorkommen ist für TDD kaum exklusiv. Im Wasserfall können Sie monatelang Anforderungen sammeln und überfluten, in der Hoffnung, dass Sie das globale Maximum sehen können, um dorthin zu gelangen und zu erkennen, dass es eine bessere Idee gibt, wenn Sie den nächsten Hügel überqueren.
Der Unterschied liegt in einer agilen Umgebung, von der Sie nie erwartet haben, dass sie zu diesem Zeitpunkt perfekt ist. Sie sind also mehr als bereit, die alte Idee zu verwerfen und zur neuen Idee überzugehen.
Spezifischer für TDD gibt es eine Technik, die verhindert, dass Ihnen dies passiert, wenn Sie Funktionen unter TDD hinzufügen. Es ist die Prämisse der Transformationspriorität . Wenn TDD eine formale Möglichkeit zum Umgestalten bietet, können Sie auf diese Weise Funktionen formell hinzufügen.
quelle
In seiner Antwort hat @Sklivvz überzeugend argumentiert, dass das Problem nicht besteht.
Ich möchte argumentieren, dass es keine Rolle spielt: Die Grundvoraussetzung (und das Grundprinzip) von iterativen Methoden im Allgemeinen und von Agile und insbesondere von TDD im Besonderen ist, dass nicht nur das globale Optimum, sondern auch die lokalen Optimalwerte vorhanden sind. ' nicht bekannt. Mit anderen Worten: Auch wenn das ein Problem war, gibt es sowieso keinen Weg, es iterativ zu machen. Angenommen, Sie akzeptieren die Grundvoraussetzung.
quelle
Können TDD- und Agile-Verfahren eine optimale Lösung versprechen? (Oder sogar eine "gute" Lösung?)
Nicht genau. Aber das ist nicht ihr Zweck.
Diese Methoden bieten lediglich einen "sicheren Übergang" von einem Zustand in einen anderen, wobei anerkannt wird, dass Änderungen zeitaufwändig, schwierig und riskant sind. Beide Vorgehensweisen zielen darauf ab, sicherzustellen, dass die Anwendung und der Code funktionsfähig sind und nachweislich die Anforderungen schneller und regelmäßiger erfüllen.
TDD konzentriert sich darauf, sicherzustellen, dass jeder Codeabschnitt die Anforderungen erfüllt. Insbesondere wird sichergestellt, dass der Code den bereits bestehenden Anforderungen entspricht, anstatt dass die Anforderungen durch eine schlechte Codierung bestimmt werden. Es gibt jedoch kein Versprechen, dass die Implementierung in irgendeiner Weise "optimal" ist.
Wie bei agilen Prozessen:
Beweglichkeit sucht nicht nach einer optimalen Lösung ; Nur eine funktionierende Lösung - mit der Absicht, den ROI zu optimieren . Es verspricht eher früher als später eine funktionierende Lösung ; kein "optimaler".
Aber das ist in Ordnung, denn die Frage ist falsch.
Optimale Werte in der Softwareentwicklung sind unscharfe, sich bewegende Ziele. Die Anforderungen sind in der Regel im Fluss und voller Geheimnisse, die nur zu Ihrer großen Verlegenheit in einem Konferenzraum mit vielen Vorgesetzten Ihres Chefs auftauchen. Und die "eigentliche Güte" der Architektur und Codierung einer Lösung wird durch die geteilten und subjektiven Meinungen Ihrer Kollegen und derjenigen Ihres Manager-Overlords bewertet - von denen möglicherweise keiner etwas über gute Software weiß.
TDD- und Agile-Praktiken erkennen zumindest die Schwierigkeiten an und versuchen, zwei Dinge zu optimieren, die objektiv und messbar sind: Arbeiten gegen Nichtarbeiten und Früher gegen Später.
Und selbst wenn wir "arbeiten" und "früher" als objektive Metriken haben, hängt Ihre Fähigkeit zur Optimierung in erster Linie von den Fähigkeiten und der Erfahrung eines Teams ab.
Zu den Dingen, die Sie als Bemühungen zur Erzielung optimaler Lösungen auffassen könnten , gehören:
etc..
Ob aus all diesen Dingen tatsächlich optimale Lösungen hervorgehen, wäre eine weitere wichtige Frage!
quelle
Was bisher noch niemand hinzugefügt hat, ist, dass die von Ihnen beschriebene "TDD-Entwicklung" sehr abstrakt und unrealistisch ist. In einer mathematischen Anwendung, in der Sie einen Algorithmus optimieren, mag dies ähnlich sein, in den Geschäftsanwendungen, an denen die meisten Programmierer arbeiten, passiert dies jedoch nicht häufig.
In der realen Welt werden bei Ihren Tests im Wesentlichen Geschäftsregeln ausgeführt und validiert:
Zum Beispiel - Wenn ein Kunde ein 30-jähriger Nichtraucher mit einer Frau und zwei Kindern ist, lautet die Premium-Kategorie "x" usw.
Sie werden die Premium-Berechnungs-Engine erst dann iterativ ändern, wenn sie sehr lange korrekt ist - und mit ziemlicher Sicherheit nicht, wenn die Anwendung aktiv ist;).
Was Sie tatsächlich erstellt haben, ist ein Sicherheitsnetz, damit beim Hinzufügen einer neuen Berechnungsmethode für eine bestimmte Kundenkategorie nicht plötzlich alle alten Regeln brechen und die falsche Antwort geben. Das Sicherheitsnetz ist noch nützlicher, wenn der erste Schritt des Debuggens darin besteht, einen Test (oder eine Reihe von Tests) zu erstellen, der den Fehler reproduziert, bevor der Code zur Behebung des Fehlers geschrieben wird. Dann, ein Jahr später, bricht der Unit-Test ab, wenn jemand versehentlich den ursprünglichen Fehler wieder herstellt, bevor der Code überhaupt eingecheckt wurde Aber es sollte kein massiver Teil Ihres Jobs sein.
quelle
Ich glaube nicht, dass es im Weg steht. Die meisten Teams haben niemanden, der in der Lage ist, eine optimale Lösung zu finden, selbst wenn Sie diese auf ihr Whiteboard geschrieben haben. TDD / Agile werden ihnen nicht in die Quere kommen.
Viele Projekte erfordern keine optimalen Lösungen, und diejenigen, die dies tun, werden die notwendige Zeit, Energie und Konzentration in diesem Bereich aufbringen. Wie alles andere neigen wir dazu, es zuerst zum Laufen zu bringen. Dann mach es schnell. Sie könnten dies mit einer Art Prototyp tun, wenn die Leistung so wichtig ist, und dann das Ganze mit der Weisheit, die durch viele Iterationen gewonnen wurde, neu aufbauen.
Dies könnte passieren, aber was wahrscheinlicher ist, ist die Angst, komplexe Teile der Anwendung zu ändern. Keine Tests zu haben, kann ein größeres Gefühl der Angst in diesem Bereich hervorrufen. Ein Vorteil von TDD und einer Reihe von Tests besteht darin, dass Sie dieses System mit der Vorstellung erstellt haben, dass es geändert werden muss. Wenn Sie von Anfang an auf diese monolithisch optimierte Lösung kommen, kann es sehr schwierig sein, sie zu ändern.
Stellen Sie dies auch in den Zusammenhang mit Ihrer Sorge um Unteroptimierung, und Sie müssen Zeit darauf verwenden, Dinge zu optimieren, die Sie nicht haben sollten, und unflexible Lösungen zu entwickeln, weil Sie sich so sehr auf deren Leistung konzentriert haben.
quelle
Es kann trügerisch sein, mathematische Konzepte wie "lokales Optimum" auf das Software-Design anzuwenden. Mit solchen Begriffen klingt Softwareentwicklung viel quantifizierbarer und wissenschaftlicher als es wirklich ist. Selbst wenn es für Code ein "Optimum" gab, haben wir keine Möglichkeit, es zu messen und können daher nicht wissen, ob wir es erreicht haben.
Die agile Bewegung war wirklich eine Reaktion gegen den Glauben, dass Softwareentwicklung mit mathematischen Methoden geplant und vorhergesagt werden könnte. Softwareentwicklung ist eher ein Handwerk als eine Wissenschaft.
quelle
fibonacci
, dass das , was ich als TDD-Beispiel / Tutorial gesehen habe, mehr oder weniger eine Lüge ist. Ich bin bereit zu wetten, dass niemand jemals Fibonacci oder eine ähnliche Serie durch TDD entdeckt hat. Jeder beginnt damit, Fibonacci bereits zu kennen, was betrügt. Wenn Sie versuchen, dies durch TDD zu entdecken, erreichen Sie wahrscheinlich die Sackgasse, nach der das OP gefragt hat: Sie können die Reihe niemals verallgemeinern, indem Sie einfach mehr Tests und Refactoring schreiben - Sie müssen mathematisch vorgehen Argumentation!