Back to "Hur jag bygger programvara: Få det att fungera, göra det snyggt, låsa ner det"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Best Practices Craftsmanship Software Development TDD Testing

Hur jag bygger programvara: Få det att fungera, göra det snyggt, låsa ner det

Wednesday, 19 November 2025

Varför jag skriver tester sist, och varför det inte är vad du tror att det betyder

**Kontroversiell användning:**Testdriven utveckling är en lysande praxis som löser fel problem för de flesta kreativa arbete.

Här är vad som fungerar bättre.

De tre faserna av att bygga programvara (som faktiskt fungerar)

Så här bygger jag mjukvara.

Det är enkelt, det är effektivt, och det gör att TDD purister artigt får sina ögonbryn:

Få det att fungera.*Gör det vackert.*Lås den med testerna.

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

I den ordningen.*Alltid.*Inte för att jag är lat.

Inte för att jag inte värdesätter tester.

Men för att

Det är så här kreativt arbete faktiskt händer

När du utforskar ett problemutrymme förstår du inte riktigt än.

  • Inte för att jag är lat.
  • Inte för att jag inte värdesätter tester.
  • Men för att
  • Det är så här kreativt arbete faktiskt händer

När du utforskar ett problemutrymme förstår du inte riktigt än.

Låt mig förklara.

Fas 1: Få det att fungera (The Private Sketch)

Det här är den röriga delen.Jag har ingen aning om vad jag gör.

Jag försöker förstå:

Fungerar detta API verkligen som dokumenten hävdar?

Kan jag ens lösa detta problem med de verktyg jag har?

Vad betyder "korrekt" ens i detta sammanhang?*Är det två timmars problem eller två veckors problem?*Det här är en uppfinning.

Det här är utforskning.

Det här är jazz.

# 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

Och vet du vad som dödar jazz?**Någon som står över axeln och säger "skriv provet först".**Varför inga prövningar ännu?

För att*Formen är fortfarande flytande.*Tänk dig att du är en skulptör.

Du har ett marmorblock och en vag idé: "Jag vill skära en fågel."

TDD säger: " Innan du rör vid den där marmorn, skriv ner exakt hur en fågel ser ut.

Definiera dess vingbredd.Ange vinkeln på varje fjäder.

Nu skära till dessa specifikationer."*Det är inte så fåglar blir snidade. (Eller programvara blir skriven, som det visar sig.)*Riktiga skulptörer

Skär ut blanketten först.De tecknar.

De gör maquettes.*De skär bort stora stenbitar för att hitta den allmänna formen.*Först senare förfinar de detaljerna.

Koden är densamma.

I fas 1 skriver jag:

Denna kod är

Det är privat.

Det är ett samtal med mig själv.

Det är så jag listar ut vad "arbete" betyder.

Att skriva tester för detta är värre än meningslöst – det är

aktivt skadligt.

För varje gång jag inser att "åh vänta, hela denna strategi är fel," skulle jag behöva skriva om testerna också.

Det är inte stelbent.

Det är byråkrati.

Friheten att misslyckas snabbt

# 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')
    ]

Fas 1 handlar om

// 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);
}

inlärningshastighet.

Jag måste prova fem olika inflygningar om en timme.

Jag måste upptäcka att det tredje partsbibliotek jag tänkte använda... inte är riktigt som det annonseras.*Jag måste inse att problemet jag löser inte är problemet.börVara lösa.*Testerna saktar ner det här.

Inte för att tester är långsamma, utan för att

  • Förtida specifikation är död.
  • Om jag skriver tester innan jag förstår problemet, Jag skriver tester för
  • Det är fel.

Då är jag känslomässigt engagerad i fel sak.

Då försvarar jag fel saker i kodgranskningen.

Bättre att misslyckas snabbt, privat, utan några tester att upprätthålla och inget ego att skydda.Vad "arbete" innebär i fas 1Det betyder: " Jag körde den.

Jag gav det några ingångar.

# 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
)

Det gav resultat som verkade rimliga.

// 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();

Jag är 60 % säker på att jag förstår problemet nu."

Så där ja.**Inga kantfall.**Ingen felhantering.

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

Ingen elegans.

*Bara en spik.*Ett bevis på koncept.

En skiss.Fas 2: Gör det vackert (Förfiningen)

Nu förstår jag problemet.Jag har något som fungerar, åtminstone för den lyckliga vägen.

Nu kan jag börja bry mig om kvalitet.

  • Fas 2 är där I:
    1. Vad är det som händer?
  • Städa upp röran

Exempel på Python:*C# Exempel:*Bättre namn.

Skriv anteckningar.*XML-dokumentation.*Genomförande av renare LINQ.

Två.

Anpassa dig till specifikationerna

Nu när jag vet vad jag faktiskt bygger, går jag tillbaka till de ursprungliga kraven och se till att jag löser

höger

problem, inte bara

aProblemet.

Ofta avslöjar detta luckor:

"Åh, de ville att detta skulle hantera negativa tal annorlunda."

  1. "Vänta, det finns en kant fall där värdet kan vara Ingen vs. 0.""Specen säger 'dubbel' men jag tror att de menade 'skala med en konfigurerbar faktor.'"
  2. **Jag fixar de här.**Ibland trycker jag tillbaka på spec: "Faktiskt bör vi göra X istället för att Y."
  3. **3. Vad är det som händer?**Polera API-ytan
  4. Det är här jag tänker påAnropsmottagarens

erfarenhet.**- Vad är det? - Python:**C#: Vad är det?

Bättre namngivning.

  • Förståeliga standardvärden.
  • Konfiguration där det är viktigt.
  • Flytande API:er där så är lämpligt.
  • Det här är
  • Fartyg.

Det är här bra programvara kommer ifrån.

Varför finns det fortfarande inga prövningar?

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)

Jag sköter det hela tiden.

Jag har en REPL öppen.

  • Jag försöker med olika ingångar.
  • Jag tränar alla vägar.
  • Men det är jag inte.
  • Skriva ner experimenten som formella tester än.

Varför?

För att

# 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]

Jag upptäcker fortfarande vad som är värt att testa.

I fas 1 visste jag inte ens om den här funktionen skulle existera.I fas 2 upptäcker jag:"Åh, skalfaktorn kan inte vara negativ.

Det är en bekräftelse."

"Om listan är tom, bör vi återvända tomma, inte Inga.""Vi måste hantera icke-numeriska värden graciöst."

Dessa insikter

framträder från refaktoring.

De är inte uppenbara i förskott.

  1. **Om jag hade skrivit prov i fas 1 hade jag skrivit om dem nu.**Om jag skriver dem
  2. efterJag har förfinat genomförandet, de kommer att ha rätt första gången.
  3. **Fas 3: Lås den med test (Delningsfasen)**Okej, nu är det på riktigt.

Koden är ren.*API:et är vettigt.*Jag har tagit hand om kanterna.

Jag är säker på vad jag har byggt.

Nu skriver jag prov.

Massor av tester.

Så nära-full täckning-som-möjliga tester.Varför nu?

För att

Tester är en delningsmekanism.De är inte till mig längre.

Jag förstår redan denna kod intimt.

  • Jag har levt i det i timmar eller dagar.
  • Testerna är avsedda för:
  • Framtida jag
  • (som kommer att glömma allt på tre månader)Mina lagkamrater(som måste lita på denna kod fungerar)

CI- pipelinen

(som måste förhindra regression)

Framtida utvecklare

(som behöver för att säkert refaktor detta)

Tester ärpresentationsskikt

för mitt genomförande.De innehåller följande dokument:

Vad denna kod görVilka indata som är giltigaVilka resultat att förvänta sig

Vilka kantfall jag har tänkt på

Vilka antaganden jag gör

Fullständig täckning, inga kompromisser

  • I fas 3 går jag hårt på att testa:
  • Det här är säkerhetsnätet.
  • Nu kan mina lagkamrater:

Ändra denna funktion med tillförsikt

  • Förstå kanterna fall
  • Fånga regressionerna omedelbart
  • Refaktor utan rädsla

Tester som dokumentation

  • Bra tester är bättre dokumentation än kommentarer:*Tester ljuger inte.*De kan inte glida.
  • Om beteendet ändras bryts testet.
  • Det är

körbar dokumentation.

  • Det är det som gör testerna värdefulla.
  • Delningsgränserna*Här är den avgörande punkten:*Fas 3 händer innan någon annan rör koden.
  • Jag delar inte kod utan tester.

Någonsin.

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

**Men jag skriver inte heller tester innan koden är klar att delas.**Faserna är följande:

Privat utforskning(bara jag, inga tester)

Privat förfining

(bara jag, fortfarande inga tester) |-----|-------------| Offentligt deltagande (test, CI, kodgranskning, hela nio varvet) Detta skyddar både min kreativitetoch | Mina lagkamraters mentala hälsa. Varför detta skyddar kreativiteten

TDD förespråkare säger "test ger dig självförtroende att refaktor."

Det är sant!Men bara om du redan vet vad du bygger.

När jag är i fas 1 behöver jag inte självförtroende för att refaktor.

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

Jag behöver tillstånd att kasta bort allt.

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

Tester skapar psykologiska skulder.

När jag skrivit dem vill jag inte radera dem.

Även om de testar fel sak.Genom att skjuta upp testerna till fas 3, behåller jag fas 1 och 2

Psykologiskt billigt.

Jag kan:

  • Prova vilda idéer
  • Kasta bort hela tillvägagångssätt
  • Radikalt ändra designen

Upptäck problemet jag ärfaktisktlösning

Utan skuldkänslorna av "men jag skrev alla dessa tester..."

Det är så kreativitet fungerar.

// 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 }));
}

Du ritar löst först.

Du börjar inte med detaljerna.

// 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);
    }
}

Hur detta passar med Agile och XP (Och där det avviker)

Låt oss vara tydliga med en sak:

Detta är inte "test senare utveckling".

Det här är

  • "prova medan du utvecklas, men i rätt tid."Tidpunkten för
  • när

Att lägga till enhetstester är kritiskt.

  • För tidigt och du anger det okända.
  • För sent och du fraktar utan skyddsnät.

De XP - metoder jag håller

  • Extrem programmering har briljanta metoder värda att följa:
  • Kontinuerlig integration

Varje funktion går igenom CI innan sammanslagning

  • Automatiserade test körs på varje åtagande till huvudInga trasiga byggnader tolererasParprogrammering / översyn av kod
  • Fas 3-kod granskas innan delning

Tester är en del av den recenserbara artefakten

Samverkansförfining förbättrar designenEnkel design

Fas 2 är

  • Allt om
  • Förenkling
  • Ta bort det du inte behöver
  • Gör det så enkelt som möjligt, inte enklare

Tillverkning

Fas 2 är dedikerad refaktortidRengör kodenföre

Låsa ner den

**Tester skyddar refaktorn (i fas 3+)**Där jag avviker från strikt TDD

TDD säger:"Test först.

Alltid.Red-Green-Refaktor är det enda sättet."

Jag säger:"Testar när man vet vad man testar.

Explore-Refine-Lock är mer naturligt."

Den viktigaste skillnaden:

TDD på trefastill Test definierar gränssnittet på Exploration definierar gränssnittet på

  • Red-Green-Refaktor cykel på Explore-Refine-Lock progression
  • till Tester skrivna före genomförande på test skrivna innan
  • delning

Pröva först för allt och testa vid rätt tidpunkt för varje sak

  • på mikrocyklar (minuter) på makrofaser (timmar/dagar)
  • Tidpunkten är kritisk
  • Här är den avgörande insikten:
  • Det är inte "test sist". Det är "test innan du delar med dig".
  • Fel timing (för tidigt):

Rätt tidpunkt (fas 3):

Samma kvalitetsresultat.*Hälften av churn.*Detta är agilt

Agile säger:

"Skjut om att ändra sig efter en plan."TDD kan bli sin egen form av planuppföljning.

När du har skrivit prov, är du psykologiskt engagerad i den designen.

  • Mitt tillvägagångssätt omfattar förändring:
  • Fas 1: Ändra snabbt, upptäck rätt lösning
  • Fas 2: Ändra medvetet, förfina mot elegans
  • Fas 3: Ändra noga, skydda det som fungerar

Det här ärfler

smidig än strikt TDD, eftersom det skjuter upp engagemang tills du har information.

Kontrasterande metoder: Ett exempel på C#

Strikt TDD-metod:

Lägger du märke till churnen?

Tre test omskriver som designen utvecklats.

  1. **Trefasmetoden:**En uppsättning tester.
  2. **Skrivet en gång.**Rätta första gången.
  3. **För att jag visste vad jag byggde.**Anslutningen till agilt manifest

Kom ihåg värdena:

"Arbeta programvara över omfattande dokumentation"

Fas 1 kommer till arbetsprogramvaran

snabb

  1. Fas 3 gör tester till den omfattande dokumentationen"Skjut på att ändra sig efter en plan"
  2. Faser 1-2 omfamna förändringFas 3 lås i den slutliga konstruktionen
  3. **"Individuella och interaktioner över processer och verktyg"**Låt inte TDD:s dogm överskugga gott omdöme

Samarbeta när det hjälper (fas 2-3), utforska ensam när det inte gör det (fas 1)

"Kundsamarbete över avtalsförhandlingar"

Fas 2 ligger i linje med specifikationerna

efter

  1. förstå problemetBättre att leverera vad de behöver än vad de angav
  2. Varför detta minskar den oanvändbara ChurnTDD: s smutsiga hemlighet:
  3. **De flesta tester som skrivs innan genomförandet skrivs om eller raderas.**Varför?

Därför att:

Du missförstod kraven.

Du kände inte till de bästa fallen än.Du upptäckte en bättre API-designDu insåg att funktionen inte behövdes alls

Varje test du skriver i fas 1 är förmodligen bortkastad ansträngning.

Bättre att skriva

en

uppsättning tester i fas 3, när du faktiskt vet vad du bygger, än att skriva-skriva-skriva-skriva om under utforskning.TDD-löftet mot verklighetenTDD lovar:

"Write code that solves the specification.
Don't worry about perfection yet.
Just get it working."

"Tests kör din design!

  • De hjälper dig att upptäcka bra API:er!"
  • TDD verklighet:
  • "Jag skrev tester för ett API jag tyckte var vettigt.
  • Sedan implementerade jag det och insåg att API:et var besvärligt.

Nu skriver jag om både testerna och koden."**Det är inte design.**Det är

Knuffas.Mitt tillvägagångssätt:

Designa genom implementation, sedan låsa ner den med tester.

  1. Slutresultatet är detsamma – väl beprövad, väl utformad kod.
  2. Men jag kom dit med hälften av churn.
  3. Varför detta fortfarande levererar disciplinerad programvara
  4. Så här går det till:
  5. inte:
  6. "Kowboykodning"

"Inga tester"*"Skicka och be"*När jag är klar med fas 3 har min kod:

Omfattande testtäckningHandläggning av kantfall

Ett rent och läsbart genomförande

Rensa API-designDokumentation (via tester)

Exakt samma som TDD.

  1. Skillnaden är

    • när
    • Dessa tester skrevs.
    • Disciplinen kommer från gränsen
  2. Regeln är enkel:

    - flake8 (style checking)
    - pylint (code quality)
    - mypy (type checking)
    - black (formatting)
    - radon (complexity analysis)
    
  3. **Ingen kod lämnar fas 2 utan fas 3.**Jag vet inte:

    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
    

Kommit oprövad kodÖppna PR utan testerSammanfoga till huvud utan att CI passerar

  • Utplacering utan täckningsrapporter
  • Disciplinen är inte i skrivande tester tidigt.
  • Den är inne.
  • inte dela kod utan tester.
  • Konsthantverksmetaforerna

Det här är ingen ny idé.Det är så kreativt arbete alltid har fungerat:

Sketch → Raffinera → Varnish

Målare börjar inte med de sista penseldragen.*De har följande egenskaper:*SkissSammansättningen (fas 1)

Färg*skikten (fas 2)*Varniska

# 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)
"""
    )

att skydda och presentera (fas 3)

  • Lacken kommer inte först.
  • Men det är inte frivilligt.
  • Utkast → Redigera → Publicera
  • Författare redigerar inte som de skriver.

De har följande egenskaper:Utkaststökigt (fas 1)

  1. Redigera**hänsynslöst (fas 2)**Publicera
  2. med tillförsikt (fas 3)**"Skriv full, redigera nykter" är hemska livsråd men bra skriftliga råd.**Lera → Form → Glaze
  3. Potters glaserar inte innan de formar sig.**De har följande egenskaper:**Kasta
  4. leran (fas 1)Trim och raffineringFormuläret (fas 2)Glasyr och eld

**för hållbarhet (fas 3)**Glasyren är avgörande.

Men det kommer sist.

Hur detta kartor till DiSE Codegen PipelineHär blir det intressant: Jag har faktisktGenomförande

denna filosofi i systemet DiSE (Dired Synthetic Evolution).

  • Codegen pipeline förkroppsligar uttryckligen dessa tre faser:
  • Fas 1 i diSE: Explorationssteget
  • När du ber DiSE att lösa ett problem, det inte hoppa direkt till skriva perfekt, testad kod.
  • I stället,

Generator

  • (codelama eller liknande) får ett tydligt mandat:
  • Systemet genererar explorativ kod:
  • Fokuserad på den lyckliga vägen
  • Minimal felhantering

Ingen för tidig optimering

  • Bara tillräckligt för att testa tillvägagångssättetOch senUtförare
  • Kör den.*Inte med formella enhetsprovningar – bara med indata från specifikationen.*Om det misslyckas?
  • Ingen orsak.Det ärAtt lära sig.
  • Systemet har en 6-stegs adaptiv upptrappning:

Prova med en snabb modell (låg temperatur)

Försök igen med högre kreativitet (högre temperatur)

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

Rulla vidare till en kraftfullare modell

  • Lägg till felsökningsloggning för att förstå fel
  • Ge den kraftfulla modellen ett fullständigt sammanhang
  • Använd "gud-nivå" modellen som en sista utväg
  • Det här är

exaktFas 1 .

Systemet utforskar lösningsutrymmet, lär sig vad som fungerar, misslyckas snabbt och anpassar sig.

Inga tester skrivs under denna fas.Koden är privat till generationsprocessen.

Fas 2 i diSE: OptimeringsstadietNär koden passerar grundläggande utförande, DiSE flyttar tillOptimering.

  1. **Det här är förfiningsfasen.**Systemet:
  2. Rensar upp genomförandetTar bort felsökningsloggning som lades till vid fel
  3. Förenklar alltför komplex kodFörbättrar namngivning och struktur
  4. Kör statisk analysIterativt optimerar

(3 iterationer som standard)

Det här ärexakt

Fas två.

Koden är fortfarande privat (inte i registret ännu), men vi polerar den:Bättre namn

Skriv tipsRenare struktur

Lägre komplexitet

Bättre resultatFormen är inte längre flytande.

Vi vet vad vi bygger.

  1. **Nu klarar vi det.**Bra.
  2. Fas 3 i diSE: Test- och lagringsstadietEndast
  3. efteroptimering genererar och kör DiSE

formella enhetsprovningar.

  • Här är den avgörande delen: testerna genereras från
  • förfinad specifikation och genomförande
  • , inte den första utforskningen.

Testerna omfattar följande:*Exempel på specificering (korrekthet)*Kantfall upptäckta under fas 2

Felhantering som tillkom under förfining

Prestandaegenskaper som mättesOch senEndast om proven godkänns

, koden är:

Förvaras i

RAG-minne

# 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]

(med inbäddningar)

Tillagd tillNodregister:

  1. (som en körbar artefakt)
  2. Görs tillgänglig för
  3. Återanvändning

i framtida arbetsflöden

Spårad med

kvalitetspoäng

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.