Wie ich Software Builde - : - es Arbeit machen -, - es schön machen - , - es verriegeln
Warum ich letzte Tests schreibe, und warum das nicht das ist, was du denkst, es bedeutet
Kontroverse Einnahme Test--Driven Development ist eine brillante Praxis, die das falsche Problem für die meisten kreativen Arbeit löst.
Die drei Phasen des Bauens von Software, die tatsächlich funktioniert
Dies ist, wie ich Software Build-.-It-' simple -, - es macht effektiv , - und es macht TDD Puristen höflich furrow ihre Brauen
Machen Sie es work?.-Machen Sie es hübsch... Sperren Sie ihn mit Tests ab.
In dieser Reihenfolge... immer...
Nicht, weil I'-m faul, .-nicht, weil ich nicht '-t Wert testing -.-Aber weil das ist, wie kreative Arbeit tatsächlich geschieht wenn du einen Problemraum erforschst, den du nicht vollständig verstehst
graph TB
Start[New Feature Request] --> P1[Phase 1: Make It Work]
P1 --> P1_Private[Private Exploration]
P1_Private --> P1_Sketch["Sketch the solution<br/>Messy code OK<br/>No tests yet<br/>Focus on learning"]
P1_Sketch --> P1_Run["Run it manually<br/>Try different inputs<br/>See what breaks<br/>Understand the problem"]
P1_Run --> P1_Decision{Does it work<br/>basically?}
P1_Decision -->|No| P1_Iterate[Try different approach]
P1_Iterate --> P1_Sketch
P1_Decision -->|Yes| P2
P2[Phase 2: Make It Pretty] --> P2_Refine[Private Refinement]
P2_Refine --> P2_Clean["Clean up the code<br/>Better names<br/>Type hints/annotations<br/>Clear structure"]
P2_Clean --> P2_Align["Align with spec<br/>Cover all requirements<br/>Handle edge cases<br/>Polish API surface"]
P2_Align --> P2_Optimise["Optimise<br/>Performance<br/>Readability<br/>Maintainability"]
P2_Optimise --> P3
P3[Phase 3: Lock It Down] --> P3_Share[Public Sharing]
P3_Share --> P3_Tests["Write comprehensive tests<br/>Full coverage<br/>Edge cases<br/>Error conditions"]
P3_Tests --> P3_CI["CI/CD Integration<br/>Automated testing<br/>Code review<br/>Documentation"]
P3_CI --> P3_Deploy["Ready to Share<br/>Commit to main<br/>Deploy to prod<br/>Other devs can use it"]
P3_Deploy --> Done[Well-Tested,<br/>Well-Designed Code]
style P1 stroke:#ff6b6b,stroke-width:3px
style P2 stroke:#4ecdc4,stroke-width:3px
style P3 stroke:#95e1d3,stroke-width:3px
style Done stroke:#38ada9,stroke-width:4px
Nicht, weil I'-m faul, .-nicht, weil ich nicht '-t Wert testing -.-Aber weil das ist, wie kreative Arbeit tatsächlich geschieht wenn du einen Problemraum erforschst, den du nicht vollständig verstehst
Lassen Sie mich erklären...
Phase Machen Sie es ?(?The Private Sketch
Dies ist der chaotische Teil, wo ich keine Ahnung habe, was I.
I''m versucht zu verstehen
- Funktioniert diese API tatsächlich die Art und Weise, wie die docs behaupten
- Kann ich dieses Problem sogar mit den Werkzeugen lösen, die ich habe?
- Was bedeutet " auch in diesem Zusammenhang?
- Ist dies ein zwei---Stunden-Problem oder ein zwei ---Wochen-Problem?
Dies ist Erfindung, das ist Exploration, das bedeutet Jazz, das heißt
Und du weißt, was Jazz tötet...... jemand, der über deiner Schulter steht und sagt: "write the test first."
Warum es noch keine Tests gibt
Weil die Form ist immer noch flüssig
Stellen Sie sich vor, du bist ein Bildhauer... du hast einen Marmorblock und eine vage Idee... ich will einen Vogel schnitzen.
TDD sagt: : Bevor Sie den Marmor berühren, schreiben Sie genau auf, wie ein Vogel aussieht.. Definieren Sie seine Flügelspannweite.
Nicht, wie Vögel geschnitzt erhalten .... oder Software wird geschrieben ,, wie es sich herausstellt
Echte Bildhauer rauh aus der Form zuerst-. Sie skizzieren.-Sie machen maquettes-.-Sie schneiden große Stücke von Stein weg, um die allgemeine Form zu finden..-Nur später verfeinern sie die Details
Code ist der gleiche.
In der Phase des Schreibens
# Ugly, exploratory code
def process_thing(data):
# TODO: This is terrible, fix later
result = []
for item in data:
# Not sure if this is the right approach...
x = item.get('value')
if x: # Is this even the right condition?
result.append(x * 2) # Why multiply by 2? Testing something...
return result
Dieser Code ist privat Es ist ein Gespräch mit mir selbst.
Schreiben Tests für dieses ist schlimmer als nutzlos aktiv schädlich Denn jedes Mal, wenn ich realisiere, dass "-oh wait-,- dieser ganze Ansatz falsch ist, müssen auch die Tests neu geschrieben werden.
Dass ,''s nicht rigor ,.' Das ,''s Bürokratie ,mesK3's
Die Freiheit, schnell zu scheitern
Phase ist ungefähr Lerngeschwindigkeit
Ich muss fünf verschiedene Ansätze in einer Stunde versuchen ,. , Ich muss entdecken, dass die dritte .- party Bibliothek Ich dachte, IM' use ?... ist ziemlich wie angepriesen . , ich muss das Problem erkennen IMMSC6 , das Problem zu lösen ist . sollte Lösung sein
Tests verlangsamen diese down-.-nicht, weil das Testen langsam ist, sondern weil verfrühte Spezifikation ist Tod.
Wenn ich Tests schreibe, bevor ich das Problem verstehe Falsche Sache... Dann investierte I'-m emotional in die falsche Sache, dann verteidigte IMCK2-m das falsche Ding im Codereview.
Besser, um schnell zu scheitern.,-in privatem... ohne Tests zu warten und kein Ego zum Schutz.
Was bedeutet Arbeit in der Phase?
Es bedeutet: :" , " ich lief es , , ." Ich gab ihm einige Eingänge , . Es produzierte Ausgänge, die vernünftig scheinen , ." I'"m , um sicher zu sein, dass ich das Problem jetzt verstehe.
Dass es ''s ist
Keine Kantenfälle... keine Fehlerbehandlung... keine Eleganz...
Nur ein Spike, ein Beweis des Konzepts, ein Sketch.
Phase Machen Sie es ziemlich ( Die Raffination
Okay, also jetzt verstehe ich das Problem. Ich habe etwas, das zumindest für den glücklichen Pfad funktioniert.
Jetzt kann ich anfangen, mich um Qualität zu kümmern
Die Phase ist, wo I:
Bereinigen Sie die Message
Python-Beispiel
# Before (Phase 1) - Exploratory code
def process_thing(data):
result = []
for item in data:
x = item.get('value')
if x:
result.append(x * 2)
return result
# After (Phase 2) - Refined code
def extract_doubled_values(items: list[dict]) -> list[int]:
"""Extract 'value' field from items and double each one.
Only includes items where 'value' is present and truthy.
"""
return [
item['value'] * 2
for item in items
if item.get('value')
]
ZUSAMMENFASSUNG
// Before (Phase 1) - Exploratory code
public List<int> ProcessThing(List<Dictionary<string, object>> data)
{
var result = new List<int>();
foreach (var item in data)
{
if (item.ContainsKey("value"))
{
var x = item["value"];
if (x != null)
{
result.Add(Convert.ToInt32(x) * 2);
}
}
}
return result;
}
// After (Phase 2) - Refined code
/// <summary>
/// Extracts 'value' field from items and doubles each one.
/// Only includes items where 'value' is present and non-null.
/// </summary>
public IEnumerable<int> ExtractDoubledValues(IEnumerable<IDictionary<string, object>> items)
{
return items
.Where(item => item.ContainsKey("value") && item["value"] != null)
.Select(item => Convert.ToInt32(item["value"]) * 2);
}
Besserer Name.-Typ Annotationen.-XML-Dokumentation.-Reiniger LINQ-Implementierung.-Typ
Mit dem Spezifik ausrichten
Jetzt, da ich weiß, was I'-m tatsächlich Gebäude Ich gehe zurück zu den ursprünglichen Anforderungen und stellen Sie sicher, dass IMSC2-m Lösung der Recht Problem: nicht nur a Problemstellung
Oft zeigt dies Lücken.
- Sie wollten, dass diese negative Zahlen anders behandelt werden.
- , ist ein Kantenfall, bei dem kein Wert gegen . sein könnte.
- Das Spezifik sagt: 'doppelt, aber ich denke, sie meinten '-Skala mit einem konfigurierbaren Faktor "MSC4"
Manchmal schiebe ich zurück auf die Spec-:.
Die API-Oberfläche polieren
Dies ist, wo ich denke über die Anrufer Erfahrungen mit .
Python-:
# Phase 1: Works, but awkward to use
process_thing(data)
# Phase 2: Clear, flexible, composable
extract_doubled_values(
items,
scale_factor=2.0,
include_zeros=False
)
C-#:-
// Phase 1: Works, but awkward to use
ProcessThing(data);
// Phase 2: Clear, flexible, composable
ExtractDoubledValues(
items,
scaleFactor: 2.0,
includeZeros: false
);
// Or even better with a fluent API
items.ExtractValues()
.Scale(by: 2.0)
.ExcludingZeros()
.ToList();
Bessere Namensgebung: Sensible defaults – Konfiguration, wenn es darauf ankommt
Das ist Handwerksbetrieb Dies ist, wo gute Software kommt von .
graph LR
subgraph "Phase 2 Refinement Process"
A[Rough Code] --> B[Clean Structure]
B --> C[Add Types]
C --> D[Polish API]
D --> E[Static Analysis]
E --> F{Quality Gates Pass?}
F -->|No| G[Fix Issues]
G --> B
F -->|Yes| H[Ready for Phase 3]
end
style A stroke:#ff6b6b,stroke-width:3px
style H stroke:#95e1d3,stroke-width:3px
Warum immer noch keine Tests
Oh ,, , IM , ' , das ständig läuft , . , Ich habe eine REPL offen ,. , I' , um verschiedene Eingänge zu versuchen , . , I.' , die alle Pfade , . ,
Aber I'-m nicht diese Experimente noch als formale Tests niederzuschreiben
Warum? I'-m immer noch entdecken, was sich lohnt zu testen
In der Phase 1,, wusste ich nicht, ob diese Funktion überhaupt existieren würde.
- Der Skalierungsfaktor kann nicht negativ sein
- Wenn die Liste leer ist, sollten wir leer zurückgeben.
- Wir müssen nicht-- numerische Werte anmutig behandeln
Diese Erkenntnisse aus dem Refactoring hervortreten Sie sind nicht offensichtlich im Voraus.
Wenn I'-d geschriebene Tests in Phase 1,-d werden neu schreiben sie jetzt, wenn ich sie schreibe nach I'-verfeinerte die Implementierung, ,-sie, wird das erste Mal korrekt sein.
Phase Verriegeln Sie es mit Tests Die Sharing-Phase
Okay ,, , jetzt es , ' , real , . ,
Der Code ist sauber Die API macht Sinn ,. , I' ,ve abgedeckt die Kantenfälle ,. ,I' ,m zuversichtlich in dem, was IMSC5 ,Ve gebaut , . ,
Jetzt schreibe ich Tests......viele von Tests,... ....... als -. schließen... -.... zu -.
Warum jetzt?
Weil Tests sind ein Sharing-Mechanismus.
Sie sind nicht mehr für mich, ich verstehe diesen Code schon innig.
Die Prüfungen sind für ,:'
- Zukünftige Die alles in drei Monaten vergessen wird
- Meine Teamkollegen Wer diesem Code vertrauen muss, funktioniert.
- Die CI-Pipeline Die Regression muss verhindert werden.
- Zukünftige Betreuer (, die diese sicher refaktorieren müssen
Prüfungen sind die Präsentationsschicht für meine Implementation
Sie dokumentieren
- Was dieser Code macht
- Welche Eingaben gültig sind
- Welche Ergebnisse erwartet man?
- Welche Kantenfälle I'?ve berücksichtigt
- Welche Annahmen I'-m machen
Keine Kompromisse
In der Phase gehen Sie hart auf das Testen
def test_extract_doubled_values_basic():
items = [{'value': 5}, {'value': 10}]
assert extract_doubled_values(items) == [10, 20]
def test_extract_doubled_values_skips_missing_values():
items = [{'value': 5}, {'name': 'foo'}, {'value': 3}]
assert extract_doubled_values(items) == [10, 6]
def test_extract_doubled_values_handles_zeros():
items = [{'value': 0}, {'value': 5}]
# By default, zeros are excluded (falsy)
assert extract_doubled_values(items) == [10]
def test_extract_doubled_values_includes_zeros_when_configured():
items = [{'value': 0}, {'value': 5}]
assert extract_doubled_values(items, include_zeros=True) == [0, 10]
def test_extract_doubled_values_custom_scale_factor():
items = [{'value': 5}]
assert extract_doubled_values(items, scale_factor=3.0) == [15.0]
def test_extract_doubled_values_rejects_negative_scale_factor():
with pytest.raises(ValueError):
extract_doubled_values([], scale_factor=-1.0)
def test_extract_doubled_values_handles_empty_list():
assert extract_doubled_values([]) == []
def test_extract_doubled_values_handles_non_numeric_values():
items = [{'value': 'not a number'}]
with pytest.raises(TypeError):
extract_doubled_values(items)
Dies ist das Sicherheitsnetz.
Jetzt können meine Teamkollegen
- Diese Funktion selbstbewusst ändern
- Verstehen Sie die Kantenfälle
- Fangregressionen sofort
- Refaktor ohne Angst
Prüfungen als Dokumentation
Gute Tests sind bessere Dokumentation als Kommentare
# This comment will drift from reality:
# Doubles all numeric values, skipping None
# This test will fail if reality changes:
def test_extract_doubled_values_skips_none_values():
items = [{'value': None}, {'value': 5}]
assert extract_doubled_values(items) == [10]
Tests don-'-t lie-.-t Sie können '-t driften.-t Wenn sich das Verhalten ändert, bricht der Test s.-t
Dass ausführbare Dokumentation Das ist, was Tests wertvoll macht.
Die Grenzen des Teilens
Hier ist der entscheidende Punkt. Phase 3, bevor sonst jemand den Code berührt
Ich teile nicht Code ohne Tests.
Aber ich schreibe auch Tests, bevor der Code bereit ist zu teilen.
Die Phasen sind:
- Private Exploration (, nur me, ohne Prüfungen
- Private Veredelung (-nur me-,-noch keine Tests
- Öffentliches Teilen Die gesamten neun Yards )
Das schützt meine Kreativität und meine Teamkollegen...
Warum das die Kreativität schützt
TDD-Verfechter sagen, dass Tests Ihnen Vertrauen zu Refactor geben.
Aber nur, wenn du bereits weißt, was du erbaust
Wenn I'-M in der Phase 1,-Ich brauche nicht Vertrauen zu refactor-.- Ich brauche die Erlaubnis, alles wegzuwerfen.
Tests erzeugen psychologische Schulden ,. , sobald I' ,ve sie geschrieben ,, , IMSC3 ,m widerwillig zu löschen sie ,mesK4 , auch wenn sie .' ,re die falsche Sache testen
Durch Aufschieben von Tests bis zur Phase 3,- Ich halte Phase 1- und 2- psychologisch billig...
Ich kann
- Versuchen Sie wilde Ideen
- Wirf ganze Ansätze weg
- Radisch das Design ändern
- Entdecken Sie das Problem I' tatsächlich Lösung
Alle ohne die Schuld von -", aber ich schrieb alle diese Tests-MSC1
So funktioniert Kreativität
Du skizzieren lose zuerst - ., du don-', beginnst mit den Details
Wie das mit Agile und XP passt und wo es sich verändert
Lassen Sie uns über etwas klar sein. dies ist nicht spätere Entwicklung testen
Das ist Tests während der Entwicklung von ,, aber zur richtigen Zeit
Das Timing von wenn Einheitstests hinzuzufügen ist kritisch..- Zu früh und you-'-re Spezifizierung der unbekannten .-Zu spät und you -'-re Versand ohne Sicherheitsnetze.
Die XP-Praxis, die ich behalte
Extreme Programmierung hat brillante Praktiken, die folgenswert sind
Kontinuierliche Integration
- Jedes Merkmal geht durch CI vor dem Zusammenführen
- Automatisierte Testläufe auf jedem Commit to Main
- Keine gebrochenen Builds toleriert
Koppelprogrammierung Code-Überprüfung
- Phase 3-Code wird vor dem Teilen überprüft
- Tests sind Teil des rezensierbaren Artefakts
- Kollaborative Veredelung verbessert Design
Einfaches Design
- Phase - 2- ist alles über Vereinfachung
- Entfernen, was Sie brauchen
- Machen Sie es so einfach wie möglich.
Refaktorisierung
- Phase 2- ist dedizierte Refactoring Zeit
- Code reinigen Vorher Verriegelung
- Tests schützen die Refaktorisierung -(-in Phase - 3+)-
Wo ich mich von strengem TDD divergiere
graph TB
subgraph "Traditional TDD (Red-Green-Refactor)"
TDD1[Write Failing Test] --> TDD2[Write Minimal Code to Pass]
TDD2 --> TDD3[Refactor]
TDD3 --> TDD1
end
subgraph "Three-Phase Approach (Explore-Refine-Lock)"
P1[Explore Solution Space] --> P2[Refine Implementation]
P2 --> P3[Write Comprehensive Tests]
P3 --> P4[Share With Team]
end
style TDD1 stroke:#ffaa00,stroke-width:2px
style TDD2 stroke:#ffaa00,stroke-width:2px
style TDD3 stroke:#ffaa00,stroke-width:2px
style P1 stroke:#ff6b6b,stroke-width:3px
style P2 stroke:#4ecdc4,stroke-width:3px
style P3 stroke:#95e1d3,stroke-width:3px
TDD sagt: : "-Tests zuerst . - Immer . - Rot - - Grün - - Refaktor ist der einzige Weg
Ich sage : Tests, wenn Sie wissen, was du testest ' testet . Explore messk3 refine - lock ist natürlicher
Der entscheidende Unterschied
TDD - Drei-MSK-Phasen |-----|-------------| Test legt die Schnittstelle fest. Exploration definiert das Interface. Weitere Informationen: http:europa.eu.int/comm/index_de.htm Vor der Implementierung geschriebene Tests vor der Umsetzung geschriebene Prüfungen Teilen | Testen Sie zuerst für alles.Testen Sie zur richtigen Zeit für jede Sache. |, Mikro--, Zyklen, Minuten, ), Makro--, Phasen, Stunden und Tage
Das Timing ist kritisch
Hier ist die entscheidende Erkenntnis. Es ist nicht ", die letzten Tests, sondern ', die vor dem Teilen von ."
Falsches Timing zu früh
Hour 1: Write test for API I haven't designed yet
Hour 2: Realize the API is wrong, rewrite test
Hour 3: Discover a better approach, rewrite both
Hour 4: Finally get it working
Hour 5: Rewrite tests to match what actually works
Rechtes Timing
Hour 1: Explore 3 different APIs, settle on the best one
Hour 2: Refine the implementation, handle edge cases
Hour 3: Write comprehensive tests for what I built
Hour 4: All tests pass, commit with confidence
Gleiches Ergebnis der Qualität... die Hälfte des Churn...
Das ist agil
Agile sagt: : Beantworten Sie, um nach einem Plan zu ändern.
TDD kann seine eigene Form des Plans werden ,- , folgend ,. , sobald Sie geschriebene Tests , , , you , ' , psychologisch verpflichtet zu diesem Design ,. ,
Mein Ansatz umarmt Veränderung.
- Phase Umsteigen schnell Umschalten Entdecken Sie die richtige Lösung
- Phase Umdenken bewusst zu verfeinern zur Eleganz
- Phase Umrüsten sorgfältig zu schützen, was funktioniert
Das ist mehr agil als streng TDD-,, weil es die Verpflichtung aufschiebt, bis Sie Informationen haben
Gegenläufige Annäherungen - Beispiel für ein C-Beispiel
Strengerer TDD-Ansatz
// Test 1: Write test first (but I don't know the right API yet)
[Test]
public void TestProcessData()
{
var processor = new DataProcessor();
var result = processor.Process(new[] { 1, 2, 3 });
Assert.That(result, Is.EqualTo(new[] { 2, 4, 6 }));
}
// Implementation 1: Make it pass
public class DataProcessor
{
public int[] Process(int[] data) => data.Select(x => x * 2).ToArray();
}
// Later: Realize I need configurability, rewrite test
[Test]
public void TestProcessDataWithFactor()
{
var processor = new DataProcessor();
var result = processor.Process(new[] { 1, 2, 3 }, factor: 3);
Assert.That(result, Is.EqualTo(new[] { 3, 6, 9 }));
}
// Later: Realize I should use IEnumerable, rewrite test again
[Test]
public void TestProcessDataEnumerable()
{
var processor = new DataProcessor();
var data = GetLargeDataset(); // This should be lazy
var result = processor.Process(data, factor: 3);
Assert.That(result.Take(3), Is.EqualTo(new[] { 3, 6, 9 }));
}
Beachten Sie die churn-?-Drei Test neu schreibt, wie das Design entwickelt .
Drei---Phasen-Ansatz
// Phase 1: Explore (no tests yet)
public int[] ProcessThing(int[] data)
{
return data.Select(x => x * 2).ToArray();
}
// Try it: var result = ProcessThing(new[] { 1, 2, 3 });
// Hmm, what about configurability? What about lazy evaluation?
// Phase 2: Refine (still no tests)
public static class DataProcessorExtensions
{
public static IEnumerable<int> Scale(
this IEnumerable<int> source,
int factor = 2)
{
return source.Select(x => x * factor);
}
}
// Try it: var result = data.Scale(factor: 3).ToList();
// Much better API!
// Phase 3: Lock it down (NOW write tests, correctly)
[TestFixture]
public class DataProcessorTests
{
[Test]
public void Scale_DefaultFactor_DoublesValues()
{
var result = new[] { 1, 2, 3 }.Scale();
Assert.That(result, Is.EqualTo(new[] { 2, 4, 6 }));
}
[Test]
public void Scale_CustomFactor_ScalesCorrectly()
{
var result = new[] { 1, 2, 3 }.Scale(factor: 3);
Assert.That(result, Is.EqualTo(new[] { 3, 6, 9 }));
}
[Test]
public void Scale_LazyEvaluation_DoesNotEnumerateImmediately()
{
var enumerated = false;
var data = GetTestData(() => enumerated = true);
var scaled = data.Scale(factor: 2);
Assert.That(enumerated, Is.False, "Should not enumerate yet");
scaled.ToList();
Assert.That(enumerated, Is.True, "Should enumerate on materialization");
}
[Test]
public void Scale_EmptySequence_ReturnsEmpty()
{
var result = Enumerable.Empty<int>().Scale();
Assert.That(result, Is.Empty);
}
}
Eine Reihe von Tests, die einmal geschrieben wurden, korrigieren Sie das erste Mal, weil ich wusste, was ich baute.
Die agile Verbindung zum Manifest
Erinnern Sie sich an die Werte
Arbeitssoftware über umfassende Dokumentation
- Phase 1Ã erhält zur Arbeitssoftware schnell
- Phase -3Ã macht Tests die umfassende Dokumentation
Beantwortet die Änderung nach einem Plan.
- Phasen Umarmung der Veränderung
- Phase-Schlösser im finalen Design
Einzelfälle und Wechselwirkungen über Prozesse und Werkzeuge
- Don-' let TDD Dogma überschreiben gutes Urteilsvermögen
- Kollaborieren, wenn es hilft, zu erforschen, wenn sie allein, wenn er nicht mehr '
Kundenkooperation über Vertragsverhandlungen
- Phase 2 ordnet sich mit der Spec nach Verständnis des Problems
- Besser zu liefern, was sie brauchen, als was sie angegeben haben
Warum dies nutzlose Churn reduziert
Das schmutzige Geheimnis von TDD-' Die meisten Tests, die vor der Implementierung geschrieben wurden, werden neu geschrieben oder gelöscht.
Warum?
- Sie haben die Anforderungen missverstanden.
- Du kennst die Kantenfälle noch nicht.
- Sie haben ein besseres API-Design entdeckt
- Sie erkannten, dass das Feature überhaupt nicht benötigt wurde.
Jeder Test, den Sie in Phase schreiben, ist wahrscheinlich verschwendet Aufwand
Besser zu schreiben eins Satz von Tests in der Phase 3,, wenn Sie wirklich wissen, was du gebäudest, als zu schreiben, - crewrite-- creewrite während der Exploration
Das TDD-Versprechen gegen die Realität
TDD verspricht Tests fahren Ihr Design. Sie helfen Ihnen, gute APIs zu entdecken.
TDD-Realität Ich schrieb Tests für eine API Ich dachte, dass es Sinn machte ,. , Dann implementierte ich es und realisierte die API war peinlich ,. , Jetzt I' ,m neu zu schreiben sowohl die Tests und den Code .." ,
Dass ,''s nicht konstruieren ,.'s thrashing-.
Mein Ansatz Design durch Implementierung – dann verriegeln Sie es mit Tests – .
Das Endresultat ist das gleiche, —.well.-.tested.MS K2. well......
Warum dies immer noch disziplinierte Software liefert
Hier ist, was dieser Ansatz ist nicht-:-
- Cowboy-Kodierung
- Keine Prüfungen
- Versanden und beten
Bis zum Zeitpunkt, wenn I'-M mit Phase .3, fertig ist, hat mein Code ?:
- Umfassende Testabdeckung
- Kantenfälle bearbeitet
- Saubere, lesbare Implementierung
- API-Design löschen
- Dokumentation über Tests
Genau das gleiche wie TDD-.
Der Unterschied ist: wenn Diese Tests wurden geschrieben.
Die Disziplin kommt von der Grenze
Die Regel ist einfach. Kein Code verlässt Phase ohne Phase
Ich weiß nicht.
- Unerprobten Code einspielen
- Offene PRs ohne Tests
- Zusammenführen zum Main ohne CI-Übergang
- Bereitstellung ohne Deckungsberichte
Die Disziplin ist im Schreiben Tests früh ,. , Es , ' , in nicht Code ohne Tests teilen
Die handwerklichen Metaphoren
Dies ist die neue Idee von ., wie kreative Arbeit immer gearbeitet hat.
Sketch Verfeinern Sie →
Die Maler don-'.t beginnen mit den letzten Pinselstrichen.
Sie n s s
- Skizze die Zusammensetzung
- Farbe die Schichten
- Lackierung zum Schutz und zur Gegenwart
Der Lack kommt nicht zuerst, aber er ist nicht optional.
Entwurf Bearbeiten Veröffentlichen
Schreiber don-'.t bearbeiten, während sie entwerfen
Sie n s s
- Entwurf unordentlich ,( ,Phase , 1) ,
- Bearbeiten rücksichtslos
- Veröffentlichen mit Vertrauen
Betrunken schreiben Besoffen editieren Besänftigen Editieren ist schrecklich Lebensrat, aber große Schreibrat
Clay-Form →-Glas
Töpfer don-'-t Glasur vor der Gestaltung
Sie n s s
- Werfen der Ton
- Zerkleinern und verfeinern das Formblatt „(-Phase"
- Glasur und Feuer für die Haltbarkeit
Die Glasur ist entscheidend, aber sie kommt zuletzt.
Wie diese Karten zur DiSE Codegen Pipeline
Hier, wo das interessant wird. umgesetzt diese Philosophie im DiSE-System der Synthetischen Evolution
Die Codegen Pipeline verkörpert diese drei Phasen explizit
Phase der Explorationsphase in DiSE-:
Wenn du DiSE fragst, um ein Problem zu lösen, dann springt es direkt zum Schreiben perfekter , Ausgetesteter Code.
Stattdessen wurde die Generator (-Codellama oder ähnliches ) erhält ein klares Mandat
"Write code that solves the specification.
Don't worry about perfection yet.
Just get it working."
Das System erzeugt explorativen Code.
- Konzentriert auf den glücklichen Weg
- Minimale Fehlerbehandlung
- Keine vorzeitige Optimierung
- Gerade genug, um den Ansatz zu testen
Dann die Vollstrecker führt es aus..-Nicht mit formalen Unit Tests... nur mit den Eingängen aus der Spezifikation...
Wenn es fehlschlägt , ? Kein Problem , das ,' , Learning-.
Das System hat eine adaptive Eskalation in der Phase
- Versuchen Sie es mit einem schnellen Modell
- Versuchen Sie es erneut mit höherer Kreativität
- Eskalieren zu einem leistungsfähigeren Modell
- Debug-Protokollierung hinzufügen, um Fehler zu verstehen
- Geben Sie dem leistungsstarken Modell den vollen Kontext
- Verwenden Sie als letztes Mittel das Modell -
Das ist Genau Phase Das System erforscht den Lösungsraum und lernt, was funktioniert., versagen schnell und passen sich an.
Während dieser Phase werden keine Tests durchgeführt Der Code ist privat für den Erzeugungsprozess.
Phase des Optimierungsstadiums
Wenn der Code die grundlegende Ausführung passiert, bewegt sich DiSE zu Optimierung-.-
Dies ist die Verfeinerungsphase. Das System.
Säubert die Umsetzung
- Entfernt die Debug-Protokollierung, die bei Fehlern hinzugefügt wurde
- Vereinfacht zu komplexen Code
- Verbessert Namensgebung und Struktur
Funktioniert statische Analyse
- flake8 (style checking) - pylint (code quality) - mypy (type checking) - black (formatting) - radon (complexity analysis)iterativ optimiert Standardmäßige Iterationen von )
for iteration in range(3): # Measure current performance metrics = measure_performance(code) # Generate improved version better_code = optimise(code, metrics) # Keep if better, discard if worse if better_code.score > code.score: code = better_code
Das ist Genau Phase: Der Code ist immer noch privat, aber wir polieren ihn.
- Bessere Namen
- Hinweise zum Typ
- Saubere Struktur
- Geringere Komplexität
- Bessere Leistung
Die Form ist nicht mehr fluid, wir wissen was wir machen gut ,.-
Phase des Prüf- und Lagerungsstadiums
Nur nach Optimierung erzeugt und läuft DiSE Förmliche Einheitenprüfungen
Hier werden die Tests aus dem Verfeinerte Spezifikation und UmsetzungNicht die erste Exploration
# Test generation happens AFTER optimisation
def generate_unit_tests(specification, optimized_code):
"""Generate comprehensive tests for refined code.
This happens in Phase 3, after we know:
- What the code actually does
- What edge cases exist
- What the API surface looks like
"""
return llm.generate(
f"""Generate comprehensive unit tests for this code.
Specification: {specification}
Implementation: {optimized_code}
Include tests for:
- Happy path (from spec examples)
- Edge cases (discovered during optimisation)
- Error conditions (based on actual error handling)
- Performance bounds (based on measured metrics)
"""
)
Die Prüfungen umfassen:
- Spezifizierungsbeispiele
- Kantenfälle, die während der Phase entdeckt wurden
- Fehlerbehandlung, die während der Verfeinerung hinzugefügt wurde
- Leistungskennzahlen, die gemessen wurden
Dann ist es so weit. nur bei bestandenen PrüfungenDer Code ist:
- Gespeichert im RAG-Speicher Mit Einbettungen
- Zugefügt zu Knotenregister Als ausführbares Artefakt
- verfügbar gemacht für Wiederverwendung in zukünftigen Workflows
- Verfolgt mit Qualitätsbewertungen und Leistungskennzahlen
Dies ist die Phase des Teilens. Der Code ist jetzt öffentlich, um das System zu erreichen. Es hat umfassende Tests.
Warum das in einem Code-Generierungssystem funktioniert
Der dreiphasige Ansatz ist wichtig für die KI---getriebene Codegenerierung, weil
Phase 1:-Modelle verstehen das Problem im Vorfeld nicht
- Sie müssen den Lösungsraum erkunden
- Erste Versuche scheitern oft auf interessante Weise
- Fehler liefern Informationen für bessere Versuche
- Vorzeitige Prüfung würde die vorzeitige Spezifikation erzwingen
Phase -MSA-Modelle optimieren mit Feedback besser
- Statische Analyse zeigt reale Probleme auf
- Leistungskennzahlen führen zu Verbesserungen
- Komplexitäts-Scores unterstreichen Vereinfachungsmöglichkeiten
- Hier entsteht Qualität
Phase-Tests dokumentieren, was gelernt wurde
- Tests erfassen die endgültig Verständnis
- Sie spiegeln die tatsächlich Kantenfälle
- Sie kodieren die raffiniert API-Design
- Sie korrigieren das erste Mal, dass sie nicht neu geschrieben haben.
Die Beweise, die es tatsächlich funktioniert
Hier ist ein echtes Beispiel aus dem DiSE-System.
User: "Calculate fibonacci numbers"
[Phase 1: Exploration - 3 attempts]
Attempt 1: Works for small inputs, explodes on large ones (no safety limit)
Attempt 2: Adds safety limit, but inefficient recursive approach
Attempt 3: Switches to iterative DP (PASS)
[Phase 2: Optimization - 3 iterations]
Iteration 1: Add type hints, improve naming (Score: 1.05)
Iteration 2: Optimize memory usage with generator (Score: 1.10)
Iteration 3: Add input validation (Score: 1.15)
[Phase 3: Testing and Storage]
Generated 8 unit tests covering:
- Basic cases (n=0, n=1, n=5, n=10)
- Edge cases (n=negative, n=100, n=None)
- Type validation
All tests pass ✓
Stored in RAG with quality score: 1.15
Available for reuse: YES
Der endgültige Code ist:
- Gut getestete Tests von (8, alle bestandenen Tests von ,,
- Gut ,- , optimiert , (15% , besser als die erste Version .) ,
- Gut dokumentierte Tests und Typ-Hinweise
- Bereit, in der Registrierung zu teilen
Genau wie TDD verspricht... Nur über einen natürlicheren Pfad
Das System optimiert sich selbst
Hier wird ', wo es wirklich interessant wird, :, DiSE wendet diesen Drei-- Phase Ansatz an zu sich selbst
Das Rekursive System Optimizer hat -4-Levels-:-
- Ebene Optimieren von Code-Artefakten (-get sie arbeitend ,- machen sie hübsch ,- sperren sie unten
- Ebene Optimieren Sie Workflows, die diese Artefakte verwenden
- Ebene Optimieren Sie die Werkzeuge und Auswahllogik
- Ebene Optimieren Sie den Optimierer selbst
Auf jedem Level: :-Erforschen Sie die Verfeinerung von →-Tests.
Das ist das Mantra. den ganzen Weg nach unten
Der wahre Grund, warum das funktioniert
Hier ist die unbequeme Wahrheit. Die meisten Code wird von Menschen geschrieben, die Don-'-t vollständig verstehen das Problem yet-.-
Dass ,' , nicht ein Ausfall ,. , das ,mesK2 , Realität
Anforderungen sind mehrdeutig – .-Spezifikationen sind unvollständig –. – Third – - – Party-APIs sind schlecht dokumentiert – MS K3 – Edge-Fälle entstehen aus dem Nichts.
Sie können die perfekte API im Voraus entwerfen, weil du hast genug Informationen...
Die drei Phasen erkennen dies an
- Phase Erfahren Sie, was Sie wissen
- Phase Wenden Sie an, was Sie gelernt haben
- Phase Beschütze das, was du gebaut hast
Indem ich die Exploration von der Spezifikation trenne
- Freies Erforschen ohne Testschulden
- Verfeinern Sie vertrauensvoll, wenn Sie wissen, was zählt
- Mit umfassenden Tests sicher teilen
Dies gilt für Menschen, die Code schreiben auch true for LLMs Generierung von Code. Und für selbst optimierte Systeme.
Wenn TDD wirklich Sinn macht
I'-M nicht anti---TDDD.-I'-m anti---verfrüht TDD-.-
TDD funktioniert sehr gut, wenn
Das Problem ist gut verstanden
Wenn I'-Implementierung eines bekannten Algorithmus (-Sortierung ,-Parsing , usw. .),-TDD perfekt ist, weiß ich genau, was "-korrekt " im Vorfeld bedeutet
# I know what sort() should do. Tests first? Sure!
def test_sort_empty_list():
assert sort([]) == []
def test_sort_single_element():
assert sort([5]) == [5]
def test_sort_multiple_elements():
assert sort([3, 1, 2]) == [1, 2, 3]
Sie beheben einen Fehler
Wenn ich eine Regression habe, ist Genau richtig.:
- Schreiben Sie einen Test, der den Fehler reproduziert
- Befestigen Sie den Code (
- Refactor, wenn nötig
Perfekt
Code für die Refaktorierung vorhandener Codes
Wenn ich bereits eine Implementierung habe und seine Struktur ändern möchte, sind die Tests unerlässlich. Vorher Ich fange an.
Aber dass nicht Phase 1., die ' Phase MSC3, Wartung .
Die ehrliche Wahrheit
Dies ist, wie ich tatsächlich arbeite... und ich vermute es... die meisten Entwickler arbeiten tatsächlich ,, , auch diejenigen, die Anspruch auf strenge TDD , . ,
Weil Kreativität unordentlich ist, ist das Lernen iterativ.
Du kannst dich aus Unwissenheit spezifizieren
Du musst die Sache bauen, um das Ding zu verstehen
Also baue ich es aus (-Phasen, um es zu verfeinern. (-Phase, dann verriegele ich ihn.(-Phase.
In dieser Reihenfolge... jedes Mal...
Und du weißt, was ,?' Der Code ich Schiff ist gut , - , getestet , , , gut .- , entworfen , und auch ,- , verstanden , .
Genau wie TDD verspricht...
Ich habe gerade über einen anderen Pfad dorthin gekommen, der respektiert, wie kreative Arbeit tatsächlich geschieht.
Was ist 's Ihre Software Gebäude mantra , MS K1 , TDD Sie alles , oder haben Sie einen anderen Ansatz ,? , I' , wirklich Liebe zu hören Ihre Gedanken in den Kommentaren ,. ,
Wenn du einen TDD-Puristen bist und dieser Beitrag dich eine Augenbraue erhöht hat, dann ist das ein ausgezeichnetes !. Das bedeutet, dass wir '.re eine richtige technische Diskussion haben...I.'.ve war falsch über viele Dinge, bevor ich meine Git Geschichte frage, also I.'.m immer neugierig auf verschiedene Perspektiven zu hören.