Miten rakennan ohjelmistoja: Tee siitä toimiva, tee siitä kaunis, lukitse se (Suomi (Finnish))

Miten rakennan ohjelmistoja: Tee siitä toimiva, tee siitä kaunis, lukitse se

Wednesday, 19 November 2025

//

20 minute read

Miksi kirjoitan testejä viimeiseksi, ja miksi se ei ole sitä, mitä luulet sen tarkoittavan

**Kiistanalainen otto:**Test-Driven Development on loistava harjoitus, joka ratkaisee väärän ongelman suurimmassa osassa luovaa työtä.

Tämä toimii paremmin.

Rakennusohjelmistojen kolme vaihetta (jotka todella toimivat)

Näin rakennan ohjelmistoja.

Se on yksinkertainen ja tehokas, ja se saa TDD-puristit kohteliaasti vapisemaan kulmakarvojaan:

Laita se toimimaan.*Tee siitä kaunis.*Lukitse se testeillä.

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

Siinä järjestyksessä.*Aina.*En siksi, että olisin laiska.

Ei siksi, etten arvostaisi testausta.

Mutta siksi, että

näin luovaa työtä oikeasti tapahtuu

Kun tutkii ongelmatilaa, sitä ei vielä täysin ymmärrä.

  • En siksi, että olisin laiska.
  • Ei siksi, etten arvostaisi testausta.
  • Mutta siksi, että
  • näin luovaa työtä oikeasti tapahtuu

Kun tutkii ongelmatilaa, sitä ei vielä täysin ymmärrä.

Anna kun selitän.

Vaihe 1: Make It work (The Private Sketch)

Tämä on sotkuinen kohta.Se kohta, jossa en tiedä, mitä teen.

Yritän ymmärtää:

Toimiiko tämä sovellusrajapinta niin kuin lääkärit väittävät?

Voinko edes ratkaista tämän ongelman työkaluilla, joita minulla on?

Mitä "oikea" edes tarkoittaa tässä yhteydessä?*Onko tämä kahden tunnin vai kahden viikon ongelma?*Tämä on keksintö.

Tämä on etsintää.

Tämä on jazzia.

# 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

Tiedätkö, mikä tappaa jazzin?**Olkapääsi takana seisoo joku, joka sanoo: "Kirjoita koe ensin."**Miksi kokeita ei ole vielä tehty?

Koska*muoto on edelleen nestemäinen.*Kuvittele olevasi kuvanveistäjä.

Sinulla on marmorilohkare ja epämääräinen ajatus: "Haluan kaivertaa linnun."

TDD sanoo: " Ennen kuin kosket siihen marmoriin, kirjoita ylös, miltä lintu näyttää.

Määrittele sen siipiväli.Määrittele kunkin höyhenen kulma.

Kaiverra nyt nuo tiedot pois."*Lintuja ei kaiverreta sillä tavalla. (Tai ohjelmisto kirjoitetaan, kuten kävi ilmi.)*Oikeat kuvanveistäjät

Karkeaa muotoa ensin.He piirtävät.

He tekevät makeisia.*He repivät pois isoja kivenmuruja löytääkseen yleismuodon.*Vasta myöhemmin he hiovat yksityiskohtia.

Koodi on sama.

Vaiheessa 1 kirjoitan:

Tämä koodi on

yksityinen.

Se on keskustelu itseni kanssa.

Siten keksin, mitä "työ" edes tarkoittaa.

Testien kirjoittaminen on pahempaa kuin hyödytöntä – se on

aktiivisesti haitallista.

Koska joka kerta, kun tajuan, että "odota, tämä koko lähestymistapa on väärä", minunkin pitäisi kirjoittaa kokeet uusiksi.

Se ei ole rasittavaa.

Se on byrokratiaa.

Vapaus epäonnistua nopeasti

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

Vaiheessa 1 on kyse

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

Oppimisnopeus.

Minun täytyy kokeilla viittä eri lähestymistapaa tunnissa.

Minun on saatava selville, että kolmannen osapuolen kirjasto, jota ajattelin käyttää, ei ole aivan niin mainostettu.*Minun on tajuttava, että ratkaisemani ongelma ei ole se ongelma, joka minulla on.pitääollaan ratkaisemassa.*Testit hidastavat tätä.

Ei siksi, että testaus olisi hidasta, vaan siksi, että

  • ennenaikainen tarkennus on kuolema.
  • Jos kirjoitan testejä ennen kuin ymmärrän ongelman, kirjoitan testejä
  • väärä juttu.

Sitten olen panostanut väärään asiaan.

Sitten puolustan koodikatsauksessa väärää asiaa.

Parempi epäonnistua nopeasti, yksityisesti, ilman kokeita ylläpitää ja ilman egoa suojella.Mitä "työ" tarkoittaa ensimmäisessä vaiheessaSe tarkoittaa: "Ajattelin sitä.

Annoin sille syötteitä.

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

Se tuotti järkeviä tuloksia.

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

Olen 60-prosenttisesti varma, että ymmärrän ongelman nyt."

Siinä kaikki.**Ei terälehtiä.**Ei virheiden käsittelyä.

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

Ei tyylikkyyttä.

*Vain piikki.*Se on todiste käsitteestä.

Piirros.Vaihe 2: Make It Pretty (The Refinement)

Nyt ymmärrän ongelman.Minulla on jotain, joka toimii ainakin iloiselle polulle.

Nyt voin alkaa välittää laadusta.

  • Toinen vaihe on se, jossa minä:
  • 1 Täysosuma
  • Siivotkaa sotkut

Python-esimerkki:*C# Esimerkki:*Parempaa nimeä.

Merkintöjä.*XML-dokumentaatio.*Cleaner LINQ -toteutus.

2.

Mukaudu spektaakkeliin

Nyt kun tiedän, mitä oikeasti rakennan, palaan alkuperäisiin vaatimuksiin ja varmistan, että ratkaisen

oikea

ongelma, ei vain

aongelma.

Usein tämä paljastaa aukkoja:

"He halusivat, että negatiiviset luvut käsiteltäisiin eri tavalla."

  1. "Odottakaa, tässä on eturistiside, jossa arvo voi olla None vs. 0.""Spekti sanoo "kaksois", mutta luulen, että ne tarkoittivat "skaalaa konfiguroitavalla tekijällä".
  2. **Korjaan nämä.**Joskus painotan takaisin spektaakkelia: " oikeastaan meidän pitäisi tehdä X sen sijaan, koska Y."
  3. **3.**Kiillota API-pinta
  4. **Tässä mielessä ajattelen:**soittajan

kokemusta.**Python:**C#:

Parempaa nimeämistä.

  • Järkeviä laiminlyöntejä.
  • Määrittely siellä, missä sillä on merkitystä.
  • Nestemäiset rajapinnat tarvittaessa.
  • Tämä on
  • käsityö.

Tästä hyvä ohjelmisto tulee.

Miksi testejä ei ole vieläkään tehty?

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)

Pyöritän sitä jatkuvasti.

Minulla on REPL auki.

  • Kokeilen eri panoksia.
  • Harjoittelen kaikkia polkuja.
  • Mutta en ole
  • Kokeiden kirjoittaminen virallisiksi testeiksi on vielä kesken.

Miksi?

Koska

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

Selvitän yhä, mikä on testaamisen arvoista.

Vaiheessa 1 en tiennyt, onko tätä toimintoa edes olemassa.Vaiheessa 2 huomaan:"Asteikollinen tekijä ei voi olla negatiivinen.

Se on varmennus."

"Jos lista on tyhjä, meidän pitäisi palata tyhjin käsin, ei None.""Meidän on käsiteltävä ei-numerollisia arvoja sulavasti."

Nämä oivallukset

nouse esiin refactoringista.

Ne eivät ole itsestään selviä etukäteen.

  1. **Jos olisin tehnyt testejä ensimmäisessä vaiheessa, kirjoittaisin ne uudelleen nyt.**Jos kirjoitan ne
  2. jälkeenOlen hionut toteutusta, he ovat oikeassa ensimmäisellä kerralla.
  3. **Vaihe 3: Lukitse se alas testeillä (Jakamisvaihe)**Nyt se on todellista.

Koodi on puhdas.*API:ssä on järkeä.*Olen hoitanut erikoistapaukset.

Olen varma siitä, mitä olen rakentanut.

Nyt kirjoitan testejä.

Paljon testejä.

Mahdollisia testejä, jotka tehdään mahdollisimman lähellä täyttä peittoa.Miksi nyt?

Koska

testit ovat jakamismekanismi.Ne eivät ole enää minua varten.

Ymmärrän tämän koodin jo intiimisti.

  • Olen elänyt siinä tunteja tai päiviä.
  • Testit on tehty:
  • Tulevaisuus
  • (joka unohtaa kaiken kolmessa kuukaudessa)Joukkuetoverini(joiden täytyy luottaa tähän sääntöön toimii)

CI-putkijohto

(minkä on estettävä regressiot)

Tulevia ylläpitäjiä

(jonka täytyy turvallisesti toistaa tämä)

Testit ovatEsityskerros

Omalle toteutukselleni.He dokumentoivat:

Mitä tämä koodi tekeeMitkä syötteet ovat kelvollisiaMitä tuloksia odottaa

Mitä teräviä tapauksia olen pohtinut?

Mitä olettamuksia olen tekemässä

Täysi peitto, ei kompromisseja

  • Kolmannessa vaiheessa olen kova testaamaan:
  • Tämä on turvaverkko.
  • Nyt joukkuetoverini voivat:

Muokkaa tätä toimintoa luottavaisesti

  • Ymmärrä reunajutut
  • Saalisten regressiot välittömästi
  • Refactor ilman pelkoa

Testit dokumentoinnina

  • Hyvät testit ovat parempi dokumentointi kuin kommentit:*Testit eivät valehtele.*He eivät voi ajelehtia.
  • Jos käytös muuttuu, testi katkeaa.
  • Niin sitä pitää.

Suoritettavat asiakirjat.

  • Se tekee kokeista arvokkaita.
  • Jakamisraja*Ratkaiseva seikka on seuraava:*Vaihe 3 tapahtuu ennen kuin kukaan muu koskee koodiin.
  • En jaa koodia ilman testejä.

Ikinä.

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

**Mutta en myöskään kirjoita testejä ennen kuin koodi on valmis jakamaan.**Vaiheet ovat seuraavat:

Yksityisetsivä(Vain minä, ei testejä)

Yksityistä hienosäätöä

(Vain minä, ei vieläkään testejä) |-----|-------------| Julkinen jakaminen (testit, CI, koodin tarkistus, koko yhdeksän jaardia) Tämä suojelee molempia luovuuttani.sekä | joukkuetoverieni mielenterveyttä. Miksi tämä suojelee luovuutta

TDD:n kannattajien mukaan "testit antavat itseluottamusta refactorille".

Totta!Mutta vain, jos tiedät jo, mitä rakennat.

Kun olen ensimmäisessä vaiheessa, en tarvitse itseluottamusta.

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

Tarvitsen luvan heittää kaiken pois.

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

Testit luovat psykologista velkaa.

Kun olen kirjoittanut ne, en halua poistaa niitä.

Vaikka he testaisivatkin väärää asiaa.Lykkäämällä testejä kolmanteen vaiheeseen, pidän ensimmäisen ja toisen vaiheen.

psykologisesti halpaa.

Voin:

  • Kokeile villejä ideoita
  • Heitetään pois kokonaisia lähestymisiä
  • Muuta rakennetta radikaalisti

Selvittäkää ongelma, joka minulla onitse asiassaratkominen

Kaikki ilman syyllisyydentunnetta, "mutta kirjoitin kaikki ne kokeet..."

Näin luovuus toimii.

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

Ensin piirrät löyhästi.

Yksityiskohdista ei puhuta aluksi.

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

Miten tämä sopii agile ja XP (ja missä se diverges)

Tehdään yksi asia selväksi:

tämä ei ole "testaa myöhempää kehitystä".

Tämä on

  • "testaa kehittyessään, mutta oikeaan aikaan."Ajankohta:
  • milloin

yksikkötestien lisääminen on kriittistä.

  • Liian aikaista ja tarkennat tuntematonta.
  • Liian myöhäistä ja kuljetatte ilman turvaverkkoja.

Pitämäni XP-harjoitukset

  • Extreme-ohjelmoinnissa on loistavia käytäntöjä, joita kannattaa seurata:
  • Jatkuva kotoutuminen

Jokainen ominaisuus menee CI:n läpi ennen yhdistämistä

  • Automaattiset testiajot kaikilla päätoimituksillaRikkinäisiä rakennelmia ei siedettyPariohjelma / koodikatsaus
  • Vaiheen 3 koodia tarkistetaan ennen jakamista

Testit ovat osa arvostelukelpoista esineistöä

Yhteistyön hiominen parantaa suunnitteluaYksinkertainen muotoilu

Vaihe 2 on

  • Kaikki noin
  • yksinkertaistaminen
  • Poista se, mitä et tarvitse
  • Tee siitä mahdollisimman yksinkertaista, ei yksinkertaisempaa

Uusintakerroin

Vaihe 2 on omistettu refactoring-ajallePuhdista koodiennen

lukitsen sen

**Testit suojaavat refaktorointia (vaiheessa 3+)**Missä eroan tiukasta TDD:stä

TDD sanoo:"Testaa ensin.

Aina.Punainen-vihreä-refactor on ainoa keino."

**Sanon:**Testaa, kun tietää, mitä testaa.

Explore-Refine-Lock on luonnollisempi."

Avainero:

"Kolmevaihetta"Test määrittelee rajapinnan, Exploration määrittelee rajapinnan.

  • Red-Green-Refactor-sykli Tutki-korjaa-Lockin etenemistä
  • Ennen toimeenpanoa kirjoitetut kokeet
  • jako

Testi ensin kaikelle Testi oikeaan aikaan jokaiselle asialle

  • Mikrosyklit (minuutteja) Makrovaiheet (tunteja/päiviä)
  • Ajoitus on kriittinen
  • Tässä on ratkaiseva näkemys:
  • Se ei ole "testit kestävät", vaan "testit ennen jakamista".
  • Väärä ajoitus (liian aikaista):

Oikea ajoitus (vaihe 3):

Sama laatutulos.*Puolet churnista.*TÄMÄ ON äkkipikainen

Agile sanoo:

"Vastasi muuttua suunnitelman mukaan."TDD:stä voi tulla oma suunnitelmallisen seuraamisen muotonsa.

Kun olet kirjoittanut testejä, olet psykologisesti sitoutunut tuohon malliin.

  • Lähestymistapani sisältää muutoksen:
  • Vaihe 1: Vaihda nopeasti, löydä oikea ratkaisu
  • Vaihe 2: Muuta tietoisesti, hio tyylikkyyttä kohti
  • Vaihe 3: Vaihda huolellisesti, suojaa mikä toimii

Tämä onlisää

Ketterä kuin tiukka TDD, koska se lykkää sitoutumista, kunnes sinulla on tietoa.

Vastakohtia: C#-esimerkki

Tiukka TDD-lähestymistapa:

Huomaatko kirvelyn?

Kolme testiä kirjoitetaan uudelleen mallin kehittyessä.

  1. **Kolmivaiheinen lähestymistapa:**Yksi testisarja.
  2. **Kirjoitettu kerran.**Korjaa ensimmäisellä kerralla.
  3. **Koska tiesin, mitä rakensin.**Agile Manifestin yhteys

Muista arvot:

"Työohjelmisto kattavassa dokumentaatiossa"

Vaihe 1 pääsee työohjelmistoon

nopea

  1. Vaihe 3 tekee testeistä kattavia asiakirjoja"Vastaava muutos suunnitelman mukaan"
  2. Vaiheet 1-2 käsittävät muutoksenVaihe 3 lukkiutuu lopulliseen malliin
  3. **"Yksilöt ja vuorovaikutus prosessien ja työkalujen välillä"**Älä anna TDD:n dogmin ohittaa hyvää arvostelukykyä

Tee yhteistyötä, kun se auttaa (vaihe 2-3), tutki yksin, kun se ei auta (vaihe 1)

"Asiakkaiden yhteistyö sopimusneuvotteluissa"

Vaihe 2 vastaa spektaakkelia

jälkeen

  1. ongelman ymmärtäminenParempi toimittaa, mitä he tarvitsevat kuin mitä he ovat määrittäneet
  2. Miksi tämä vähentää hyödytöntä churniaTDD:n likainen salaisuus:
  3. **Useimmat ennen toteutusta kirjoitetut testit kirjoitetaan uudelleen tai poistetaan.**Miksi?

Koska:

Ymmärsit vaatimukset väärin

Et vielä tiennyt eturistisidejuttujaLöysit paremman API-suunnittelunTajusit, ettei ominaisuutta tarvittu lainkaan

Jokainen vaiheessa 1 kirjoittamasi testi on luultavasti hukkaan heitetty.

Parempi kirjoittaa

yksi

Testisarja kolmannessa vaiheessa, kun oikeasti tietää, mitä rakentaa, kuin kirjoittaa uudelleen tutkimustyön aikana.TDD-lupaus vastaan todellisuusTDD lupaa:

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

"Testaukset ajavat suunnitteluasi!

  • Ne auttavat sinua löytämään hyviä API-rajapintoja!"
  • TDD-todellisuus:
  • "Kirjoitin testejä API-rajapintaan, joka oli mielestäni järkevä.
  • Sitten toteutin sen ja tajusin, että API on kiusallinen.

Nyt kirjoitan uudelleen sekä kokeet että koodin."**Se ei ole designia.**Niin sitä pitää.

puidaan.Lähestymistapani:

Suunnittele toteutuksen kautta ja lukitse se sitten testeillä.

  1. Lopputulos on sama – hyvin testattu, hyvin suunniteltu koodi.
  2. Mutta pääsin sinne, kun minulla oli puolet kranaatista.
  3. Miksi tämä tuottaa yhä kurinalaista ohjelmistoa
  4. Tämä lähestymistapa on seuraava:
  5. eivät:
  6. "Cowboy-koodi"

"Ei testejä"*"Lopeta ja rukoile"*Kun olen saanut kolmannen vaiheen valmiiksi, koodini on:

Kattava testien kattavuusKäsiteltävät reuna-asiat

Puhdas, luettavissa oleva toteutus

Tyhjennä API-suunnitteluDokumentaatio (testien avulla)

Aivan sama kuin TDD:ssä.

  1. Ero on

    • milloin
    • nuo testit oli kirjoitettu.
    • Kuri tulee rajasta
  2. Sääntö on yksinkertainen:

    - flake8 (style checking)
    - pylint (code quality)
    - mypy (type checking)
    - black (formatting)
    - radon (complexity analysis)
    
  3. **Yksikään koodi ei jätä kakkosvaihetta ilman kolmosvaihetta.**Minä en:

    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
    

Toimita testaamaton koodiAvoimet PR-tiedot ilman testejäYhdistä päävirtaan ilman, että CI on ohi

  • Käyttöönotto ilman kattausraportteja
  • Kuri ei ole kirjallisissa kokeissa aikaisin.
  • Se on sisällä
  • ei jaa koodia ilman testejä.
  • Käsityömetaforat

Tämä ei ole uusi idea.Niin luova työ on aina toiminut:

Sketch → Refine → Varnish

Maalaajat eivät aloita viimeisistä siveltimistä.*He:*Piirroskokoonpano (vaihe 1)

Maalit*kerrokset (vaihe 2)*Varnish

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

Suojata ja esitellä (vaihe 3)

  • Lakat eivät ole etusijalla.
  • Mutta se ei ole vapaaehtoista.
  • Piirros → Muokkaa → Julkaise
  • Kirjoittajat eivät editoi luonnoksiaan.

He:Luonnossotkuisesti (vaihe 1)

  1. Muokkaa**armottomasti (vaihe 2)**Julkaise
  2. itseluottamuksella (vaihe 3)**"Kirjoita humalassa, toimita selvin päin" on surkea elämänohje, mutta hyvä kirjoitusohje.**Clay → Lomake → Glaze
  3. Potterit eivät lasita ennen muotoilua.**He:**Heitä
  4. Savi (Pase 1)Jalosta ja jalostaLomake (vaihe 2)Jäähdytys ja tuli

**kestävyys (vaihe 3)**Lasitus on ratkaisevan tärkeä.

Mutta se tulee viimeisenä.

Miten tämä kartta menee DiSE Codegen -putkeenTässä kohtaa tämä käy mielenkiintoiseksi: olen itse asiassaToteutettu

tämä filosofia DiSE (Directed Synthetic Evolution) -järjestelmässä.

  • Koodeiiniputki ilmentää nimenomaan näitä kolmea vaihetta:
  • DiSE:n ensimmäinen vaihe: Tutkimusvaihe
  • Kun DiSEä pyydetään ratkaisemaan ongelma, se ei hyppää suoraan kirjoittamaan täydellistä, testattua koodia.
  • Sen sijaan,

Generaattori

  • (Codellama tai vastaava) saa selkeän toimeksiannon:
  • Järjestelmä luo tunnustelukoodin:
  • Keskittynyt iloiselle tielle
  • Pienin virheiden käsittely

Ei ennenaikaista optimointia

  • Vain sen verran, että voimme testata lähestymistäSittenSuorittaja
  • pyörittää sitä.*Ei virallisilla yksikkötesteillä, vain eritelmän tuloksilla.*Jos se epäonnistuu?
  • Ei se mitään.*Niin sitä pitää.*Opetteleminen.
  • Järjestelmässä on 6-vaiheinen adaptiivinen kiihtyminen:

Kokeile nopealla mallilla (alhainen lämpötila)

Yritä uudelleen korkeammalla luovuudella (korkeammalla lämpötilalla)

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

Eskaloitua voimakkaampaan malliin

  • Lisää vianetsintäkirjaus virheiden ymmärtämiseksi
  • Anna vahvalle mallille täysi konteksti
  • Käytä "jumalan tasoa" -mallia viimeisenä keinona
  • Tämä on

tasanVaihe 1.

Järjestelmä tutkii ratkaisutilaa, oppii, mikä toimii, epäonnistuu nopeasti ja mukautuu.

Tässä vaiheessa ei ole tehty testejä.Koodi on yksityinen sukupolvenvaihdokselle.

Vaihe 2 DiSE: OptimointivaiheKun koodi läpäisee perustoteutuksen, DiSE siirtyyoptimointi.

  1. **Tämä on hiomisvaihe.**Järjestelmä:
  2. Puhdistaa toteutustaPoistaa virheiden yhteydessä lisätyn vianetsintähavainnon
  3. Yksinkertaistaa liian monimutkaista koodiaParantaa nimeämistä ja rakennetta
  4. Suorittaa staattisen analyysinIteratiivisesti optimoitu

(3 iterointia oletuksena)

Tämä ontasan

Vaihe 2.

Koodi on edelleen yksityinen (ei vielä rekisterissä), mutta kiillotamme sitä:Paremmat nimet

TyyppivihjeetPuhtaampi rakenne

Pienempi monimutkaisuus

Parempaa suorituskykyäMuoto ei ole enää nestemäinen.

Tiedämme, mitä rakennamme.

  1. **Nyt me selviämme.**Hyvä.
  2. Vaihe 3 DiSE: Testi- ja varastointivaiheVain
  3. jälkeenoptimointi luo ja pyörittää DiSE:tä

viralliset yksikkötestit.

  • Tässä ratkaiseva osa: kokeet syntyvät
  • tarkennettu määrittely ja täytäntöönpano
  • , ei alustavaa tutkimusmatkaa.

Testit kattavat seuraavat:*Erittelyesimerkit (korjattavuus)*Vaiheen 2 aikana havaitut reunatapaukset

Tarkennuksen aikana lisätty virhekäsittely

Mitatut suoritusominaisuudetSittenvain, jos testit läpäisevät

, koodi on:

Säilytä

RAG-muisti

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

(jossa on upotuksia)

Lisänäsolmurekisteri:

  1. (suoritettavana esineenä)
  2. Saatettu saataville
  3. uudelleenkäyttö

tulevaisuuden työnkulussa

Seurattu

laatupisteitä

Finding related posts...
logo

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