Come costruisco il software: fatelo funzionare, fatelo bello, bloccatelo giù (Italiano (Italian))

Come costruisco il software: fatelo funzionare, fatelo bello, bloccatelo giù

Wednesday, 19 November 2025

//

24 minute read

Perche' scrivo i test per ultimo, e perche' non e' quello che pensi significhi

**Prendiamo la controversia:**Test-Driven Development è una pratica brillante che risolve il problema sbagliato per la maggior parte del lavoro creativo.

Ecco cosa funziona meglio.

Le tre fasi del software di costruzione (che funziona effettivamente)

E' cosi' che costruisco il software.

E 'semplice, è efficace, e rende i puristi TDD gentilmente solcare le loro sopracciglia:

Fallo funzionare.*Rendilo carino.*Bloccalo con gli esami.

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

In quest'ordine.*Sempre.*Non perche' sono pigro.

Non perche' non valgo i test.

Ma perche'

questo è il modo in cui il lavoro creativo realmente accade

quando stai esplorando uno spazio problematico non riesci ancora a capire appieno.

  • Non perche' sono pigro.
  • Non perche' non valgo i test.
  • Ma perche'
  • questo è il modo in cui il lavoro creativo realmente accade

quando stai esplorando uno spazio problematico non riesci ancora a capire appieno.

Lascia che ti spieghi.

Fase 1: Farlo funzionare (Lo Schizzo Privato)

Questa e' la parte disordinata.La parte in cui non ho idea di cosa sto facendo.

Sto cercando di capire:

Questa API funziona davvero nel modo in cui i documenti sostengono?

Posso risolvere questo problema con gli strumenti che ho?

Che cosa significa "corretto" in questo contesto?*E' un problema di due ore o di due settimane?*Questa e' un'invenzione.

Questa è esplorazione.

Questo e' 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

E sai cosa uccide il jazz?**Qualcuno in piedi sopra la tua spalla che dice "scrivi prima il test."**Perche' ancora nessun test?

Perche'*la forma è ancora fluida.*Immagina di essere uno scultore.

Hai un blocco di marmo e un'idea vaga: "Voglio scolpire un uccello."

TDD dice: "Prima di toccare quel marmo, scrivi esattamente come appare un uccello.

Definisci la sua apertura alare.Specificare l'angolo di ciascuna piuma.

Ora scolpisci queste specifiche."*Non e' cosi' che gli uccelli vengono scolpiti (o il software viene scritto, a quanto pare.)*Scultori veri

Per prima cosa distruggi il modulo.Disegnano.

Fanno le maquette.*Tagliano via grossi pezzi di pietra per trovare la forma generale.*Solo più tardi affinano i dettagli.

Il codice e' lo stesso.

Nella Fase 1, scrivo:

Questo codice è

privata.

E' una conversazione con me stesso.

E' cosi' che capisco cosa significhi "lavorare."

Scrivere test per questo è peggio di inutile

attivamente dannoso.

Perché ogni volta che mi rendo conto che "oh aspetta, questo approccio è sbagliato," dovrei riscrivere anche i test.

Quello non e' rigore.

E' burocrazia.

La libertà di fallire velocemente

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

La fase 1 è circa

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

velocità di apprendimento.

Devo provare cinque diversi approcci in un'ora.

Devo scoprire che la libreria di terze parti che ho pensato di usare... non e' esattamente come pubblicizzato.*Devo capire che il problema che sto risolvendo non e' il problema che sto risolvendo.dovrebbeRisolvere.*I test rallentano.

Non perché il test è lento, ma perché

  • Le specifiche premature sono morte.
  • Se scrivo test prima di capire il problema, sto scrivendo test per il
  • Una cosa sbagliata.

Allora sono emotivamente coinvolto nella cosa sbagliata.

Allora difendo la cosa sbagliata nella revisione del codice.

Meglio fallire velocemente, in privato, senza test da mantenere e senza ego da proteggere.Cosa significa "lavorare" nella fase 1Significa: "L'ho gestita io.

Gli ho dato degli input.

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

Ha prodotto output che sembrano ragionevoli.

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

Sono sicuro al 60% di capire il problema ora."

Tutto qui.**Niente bordelli.**Nessun errore di gestione.

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

Nessuna eleganza.

*Solo un picco.*Una prova di concetto.

Uno sketch.Fase 2: Rendilo gradevole (Il affinamento)

Ok, ora capisco il problema.Ho qualcosa che funziona, almeno per il percorso felice.

Ora posso iniziare a preoccuparmi della qualita'.

  • La fase 2 è dove io:
  • Pulisci il messaggio

Esempio di Python:*Esempio di C#:*Nome migliore.

Digitare annotazioni.*Documentazione XML.*Implementazione LINQ più pulita.

2.

Allinea con lo specifico

Ora che so cosa sto costruendo, torno ai requisiti originali e mi assicuro di risolvere il problema.

destra

problema, non solo

aProblema.

Spesso ciò rivela lacune:

"Oh, volevano che questo gestisse i numeri negativi in modo diverso."

  1. "Aspetta, c'e' un caso in cui il valore potrebbe essere Nessuno contro 0.""La specifica dice "doppio" ma penso che intendessero "scalare per un fattore configurabile."
  2. **Li sistemo io.**A volte rispondo alle specifiche: "In realtà, dovremmo fare X invece perché Y."
  3. **3.**Lucida la superficie delle API
  4. E' qui che penso alchiamante

esperienza.**Python:**C#:

Meglio chiamare.

  • Predefiniti sensibili.
  • Configurazione dove conta.
  • API fluenti, se del caso.
  • Questo e'
  • Un mestiere.

E' da qui che proviene un buon software.

Perche' ancora nessun test?

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)

Oh, lo gestisco sempre.

Ho una REPL aperta.

  • Sto provando ingressi diversi.
  • Sto esercitando tutte le strade.
  • Ma non lo sono.
  • Scrivo quegli esperimenti come test formali.

Perché?

Perche'

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

Sto ancora scoprendo cosa vale la pena testare.

Nella Fase 1, non sapevo nemmeno se questa funzione esistesse.Nella Fase 2, sto scoprendo:"Oh, il fattore di scala non può essere negativo.

E' una convalida."

"Se la lista è vuota, dovremmo tornare vuoti, non Nessuno.""Dobbiamo gestire con grazia i valori non numerici."

Queste intuizioni

emergono dal refactoring.

Non sono ovvi in anticipo.

  1. **Se avessi scritto dei test in fase 1, li riscriverei ora.**Se le scrivo
  2. dopoHo perfezionato l'implementazione, saranno corrette la prima volta.
  3. **Fase 3: Bloccarlo con i test (fase di condivisione)**Ok, ora e' reale.

Il codice e' pulito.*L'API ha senso.*Ho coperto le valigie.

Sono sicuro di quello che ho costruito.

Ora scrivo dei test.

Un sacco di test.

Test di copertura quasi completa come possibili.Perche' adesso?

Perche'

I test sono un meccanismo di condivisione.Non sono piu' per me.

Capisco già questo codice intimamente.

  • Ci ho vissuto per ore o giorni.
  • Le prove sono per:
  • Future Me
  • (che dimenticherà tutto in tre mesi)I miei compagni di squadra(chi ha bisogno di fidarsi di questo codice funziona)

Il gasdotto CI

(che deve prevenire le regressioni)

Manutentori futuri

(che hanno bisogno di rivalutare in modo sicuro questo)

Le prove sono le seguenti:livello di presentazione

per la mia attuazione.Essi documentano:

Cosa fa questo codiceQuali ingressi sono validiChe risultati ci aspettiamo?

Quali casi di bordo ho preso in considerazione

Che supposizioni sto facendo?

Copertura completa, senza compromessi

  • In fase 3, vado duro sul test:
  • Questa è la rete di sicurezza.
  • Ora i miei compagni di squadra possono:

Modifica con fiducia questa funzione

  • Comprendere i casi di bordo
  • Regressioni di cattura immediatamente
  • Refattore senza paura

Prove come documentazione

  • Buoni test sono una migliore documentazione rispetto ai commenti:*I test non mentono.*Non possono andare alla deriva.
  • Se il comportamento cambia, la prova si rompe.
  • E'

documentazione eseguibile.

  • E' questo che rende preziosi i test.
  • Il confine della condivisione*Ecco il punto cruciale:*La fase 3 avviene prima che qualcun altro tocchi il codice.
  • Non condivido il codice senza test.

Mai.

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

**Ma non scrivo test prima che il codice sia pronto a condividere.**Le fasi sono:

Esplorazione privata(solo io, nessun test)

Rifinizione privata

(solo io, ancora nessun test) |-----|-------------| Condividere il pubblico (test, CI, revisione del codice, tutti i nove metri) Questo protegge sia la mia creativitàe | La sanita' mentale dei miei compagni di squadra. Perché questo protegge la creatività

I sostenitori del TDD dicono "i test ti danno fiducia nel refattore."

Vero!Ma solo se sai gia' cosa stai costruendo.

Quando sono in fase 1, non ho bisogno di fiducia per rifattore.

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

Mi serve il permesso di buttare via tutto.

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

I test creano debiti psicologici.

Una volta che li ho scritti, sono riluttante a cancellarli.

Anche se stanno testando la cosa sbagliata.Ritardando le prove fino alla fase 3, mantengo la fase 1 e 2

psicologicamente a buon mercato.

Posso:

  • Prova idee selvagge
  • Buttare via interi approcci
  • Cambiare radicalmente il design

Scopri il problema che sono*In realta'*soluzione

Tutto senza il senso di colpa di "ma ho scritto tutti quei test..."

È così che funziona la creatività.

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

Prima fai uno schizzo sciolto.

Non inizi con i dettagli.

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

Come questo si adatta con Agile e XP (e dove si diverte)

Cerchiamo di essere chiari su una cosa:

questo non è "test dello sviluppo successivo."

Questo e'

  • "provare durante lo sviluppo, ma al momento giusto."I tempi di
  • quando

aggiungere test unitari è fondamentale.

  • Troppo presto e stai specificando l'ignoto.
  • Troppo tardi e spedisci senza reti di sicurezza.

Le pratiche XP che tengo

  • Extreme Programming ha pratiche brillanti vale la pena di seguire:
  • Integrazione continua

Ogni funzione passa attraverso IC prima della fusione

  • Il test automatico viene eseguito su ogni commit alla mainNessun build rotto tolleratoCoppia programmazione / revisione del codice
  • Il codice di fase 3 viene rivisto prima della condivisione

I test fanno parte dell'artefatto rivisitabile

La raffinatezza collaborativa migliora il designDesign semplice

La fase 2 è

  • tutto su
  • semplificazione
  • Rimuovi ciò di cui non hai bisogno
  • Rendere il più semplice possibile, non più semplice

Rifattorizzazione

Fase 2 è dedicato tempo di refactoringPulisci il codiceprima

Bloccalo.

**I test proteggono il refactoring (nella fase 3+)**Dove diverge da rigido TDD

TDD dice:"Prima le prove.

Sempre.Il fattore verde-rosso è l'unico modo."

Io dico:"Prova quando sai cosa stai testando.

Explore-Refine-Lock è più naturale."

La differenza fondamentale:

| TDD | Trifase || Test definisce l'interfaccia | Esplorazione definisce l'interfaccia |

  • |Ciclo rosso-verde-refattore |Progressione dell'esplorazione-rifinitura-blocco |
  • | Prove scritte prima dell'implementazione | Prove scritte prima
  • condivisione

| Prova prima di tutto | Prova al momento giusto per ogni cosa |

  • | Microcicli (minuti) | Macrofasi (ore/giorni) |
  • Il tempismo è critico
  • Ecco l'intuizione cruciale:
  • Non e' "tests last," ma "tests before sharing."
  • Tempismo sbagliato (troppo presto):

Tempismo corretto (fase 3):

Stesso risultato qualitativo.*Meta' del churn.*Questo e' Agile.

Agile dice:

"Rispondi al cambiamento dopo aver seguito un piano."TDD può diventare la sua forma di piano-seguendo.

Una volta che hai fatto dei test, sei psicologicamente impegnata in quel progetto.

  • Il mio approccio abbraccia il cambiamento:
  • Fase 1: Cambiare rapidamente, scoprire la soluzione giusta
  • Fase 2: Cambiare deliberatamente, affinare verso l'eleganza
  • Fase 3: Cambiare con attenzione, proteggere ciò che funziona

Questo e'di più

agile che rigoroso TDD, perché rinvia l'impegno fino a quando non si dispone di informazioni.

Approcci di contrasto: un esempio di C#

Approccio rigoroso TDD:

Hai notato il hurn?

Tre riscritture di test come il design si è evoluto.

  1. **Approccio a tre fasi:**Una serie di test.
  2. **Scritto una volta.**Correggi la prima volta.
  3. **Perche' sapevo cosa stavo costruendo.**Il Manifesto Agile

Ricorda i valori:

"Software di lavoro su documentazione completa"

La fase 1 arriva al software di lavoro

veloce

  1. La fase 3 mette alla prova la documentazione completa"Rispondere al cambiamento dopo un piano"
  2. Fasi 1-2 abbracciano il cambiamentoSerrature di fase 3 nella progettazione finale
  3. **"Individuali e interazioni su processi e strumenti"**Non lasciare che TDD dogma override buon giudizio

Collaborare quando aiuta (fase 2-3), esplorare da solo quando non lo fa (fase 1)

"La collaborazione dei clienti sulla negoziazione di contratti"

La fase 2 si allinea con le specifiche

dopo

  1. capire il problemaMeglio consegnare quello di cui hanno bisogno che quello che hanno specificato
  2. Perché questo riduce l'inutile ChurnIl segreto sporco di TDD:
  3. **la maggior parte dei test scritti prima dell'implementazione vengono riscritti o cancellati.**Perché?

Perché:

Hai frainteso i requisiti

Non conoscevi ancora i casi piu' importanti.Hai scoperto un design API miglioreHai capito che la funzione non era necessaria per niente

Ogni test che scrivi in fase 1 è probabilmente uno sforzo sprecato.

Meglio scrivere

uno

serie di test in fase 3, quando si sa cosa si sta costruendo, piuttosto che scrivere-riscrivere-riscrivere durante l'esplorazione.Il TDD Promise vs. RealityTDD promette:

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

"I test guidano il tuo design!

  • Ti aiutano a scoprire buone API!"
  • Realtà TDD:
  • "Ho scritto dei test per un'API che pensavo avesse senso.
  • Poi l'ho implementato e ho capito che l'API era imbarazzante.

Ora sto riscrivendo sia i test che il codice."**Quello non e' design.**E'

Distruggere.Il mio approccio:

Progettare attraverso l'implementazione, quindi bloccarlo con i test.

  1. Il risultato finale è lo stesso codice ben collaudato e ben progettato.
  2. Ma ci sono arrivata con meta' del churn.
  3. Perché questo offre ancora il software disciplinare
  4. Ecco cos'è questo approccio
  5. non:
  6. "Codifica del cowboy"

"Nessun test"*"Spedite e pregate"*Quando ho finito con la Fase 3, il mio codice ha:

Copertura completa dei testCasi di bordo trattati

Implementazione pulita e leggibile

Pulisci il design delle APIDocumentazione (tramite prove)

Esattamente come TDD.

  1. La differenza è

    • quando
    • quei test sono stati scritti.
    • La disciplina viene dal confine
  2. La regola è semplice:

    - flake8 (style checking)
    - pylint (code quality)
    - mypy (type checking)
    - black (formatting)
    - radon (complexity analysis)
    
  3. **Nessun codice lascia la fase 2 senza la fase 3.**Io non

    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
    

Codice di commit non testatoApri PR senza testUnisce alla main senza passare CI

  • Distribuisci senza report di copertura
  • La disciplina non è nella scrittura test in anticipo.
  • E' dentro
  • non condividere codice senza test.
  • Le metafore dell'artigianato

Non e' una nuova idea.È così che il lavoro creativo ha sempre funzionato:

Sketch → Refine → Smalto

I pittori non iniziano con le pennellate finali.*Essi:*Sketchla composizione (fase 1)

Disegna*i livelli (fase 2)*Smalto

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

proteggere e presentare (fase 3)

  • La vernice non viene prima.
  • Ma non e' facoltativo.
  • Bozza → Modifica → Pubblica
  • Gli scrittori non modificano mentre si disegnano.

Essi:Bozzadisordinato (fase 1)

  1. Modifica**spietatamente (fase 2)**Pubblica
  2. con fiducia (fase 3)**"Scrivi ubriaco, modifica sobrio" è un pessimo consiglio di vita, ma un ottimo consiglio di scrittura.**Clay → Modulo → Glaze
  3. I vasai non smalto prima di modellare.**Essi:**Lancia
  4. l'argilla (fase 1)Taglio e raffinazioneil modulo (fase 2)Glassa e fuoco

**per la durata (fase 3)**Lo smalto è cruciale.

Ma arriva l'ultima volta.

Come questo mappe per il DiSE Codegen PipelineQui è dove questo diventa interessante: In realtà hoimplementata

questa filosofia nel sistema DiSE (Directed Synthetic Evolution).

  • La conduttura di codegen incarna esplicitamente queste tre fasi:
  • Fase 1 in DiSE: Lo stadio di esplorazione
  • Quando chiedi a DiSE di risolvere un problema, non salta direttamente alla scrittura del codice perfetto e testato.
  • Invece, il

Generatore

  • (codellama o simili) ottiene un chiaro mandato:
  • Il sistema genera codice esplorativo:
  • Concentrati sul percorso felice
  • Gestione degli errori minimi

Nessuna ottimizzazione prematura

  • Abbastanza per testare l'approccioPoi ilEsecutore
  • Gestisce tutto.*Non con unità formali di prova [49] solo con gli input della specifica.*Se fallisce?
  • Nessun problema.*E'*l'apprendimento.
  • Il sistema ha un'escalation adattiva a 6 stadi:

Prova con un modello veloce (bassa temperatura)

Riprovare con maggiore creatività (temperatura più alta)

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

Scalare verso un modello più potente

  • Aggiungi la registrazione di debug per capire i guasti
  • Dare pieno contesto al modello potente
  • Utilizzare il modello "dio-livello" come ultima risorsa
  • Questo e'

esattamenteFase 1.

Il sistema sta esplorando lo spazio della soluzione, imparando ciò che funziona, fallendo velocemente e adattandosi.

In questa fase non sono state scritte prove.Il codice è privato del processo di generazione.

Fase 2 in DiSE: lo stadio di ottimizzazioneUna volta che il codice passa l'esecuzione di base, DiSE passa aottimizzazione.

  1. **Questa è la fase di perfezionamento.**Il sistema:
  2. Ripulisce l'attuazioneRimuove la registrazione del debug che è stata aggiunta durante i guasti
  3. Semplifica il codice eccessivamente complessoMigliora la denominazione e la struttura
  4. Esegue analisi staticheOttimizza in modo iterativo

(3 iterazioni per impostazione predefinita)

Questo e'esattamente

Fase 2.

Il codice è ancora privato (non ancora nel registro), ma lo stiamo lucidando:Nomi migliori

Digita suggerimentiStruttura più pulita

Abbassare la complessità

Migliorare le prestazioniLa forma non è più fluida.

Sappiamo cosa stiamo costruendo.

  1. **Ora ce la facciamo.**Bene.
  2. Fase 3 in DiSE: Lo stadio di prova e stoccaggioSolo
  3. dopoL'ottimizzazione di DiSE genera ed esegue

test formali dell'unità.

  • Ecco la parte cruciale: i test vengono generati dal
  • specifiche e attuazione perfezionate
  • , non l'esplorazione iniziale.

Le prove riguardano:*Esempi di specifiche (correttezza)*Casi di bordo scoperti durante la fase 2

Errore nel maneggiare che è stato aggiunto durante l'affinamento

Caratteristiche di prestazione misurateAlloraSolo se le prove passano

, il codice è:

Conservato nel

Memoria RAG

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

(con inserti)

Aggiunto alRegistro dei nodi:

  1. (come manufatto eseguibile)
  2. Realizzato per
  3. riutilizzo

nei flussi di lavoro futuri

Rintracciato con

punteggi di qualità

Finding related posts...
logo

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