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

<datetime class="hidden">2025-11-19T10:00</datetime>

<!-- category -- Software Development, Testing, TDD, Craftsmanship, Best Practices -->
**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ä.

```mermaid
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.

[TOC]

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

```python
# 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**

```python
# 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**

```csharp
// 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ä vaiheessa*Se tarkoittaa: "Ajattelin sitä.

**Annoin sille syötteitä.**

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

**Se tuotti järkeviä tuloksia.**

```csharp
// 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ä.

```mermaid
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

a**ongelma.**

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?

```python
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

```python
# 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älkeen**Olen 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 ovat**Esityskerros**

Omalle toteutukselleni.**He dokumentoivat:**

Mitä tämä koodi tekee*Mitkä syötteet ovat kelvollisia*Mitä 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ä.

```mermaid
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 on*itse asiassa*ratkominen

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

**Näin luovuus toimii.**

```csharp
// 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.**

```csharp
// 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äätoimituksilla*Rikkinäisiä rakennelmia ei siedetty*Pariohjelma / koodikatsaus
- Vaiheen 3 koodia tarkistetaan ennen jakamista

## Testit ovat osa arvostelukelpoista esineistöä

Yhteistyön hiominen parantaa suunnittelua**Yksinkertainen muotoilu**

Vaihe 2 on

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

Uusintakerroin

Vaihe 2 on omistettu refactoring-ajalle*Puhdista koodi*ennen

### 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ä on*lisää*

## 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 muutoksen**Vaihe 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äminen**Parempi toimittaa, mitä he tarvitsevat kuin mitä he ovat määrittäneet
2. **Miksi tämä vähentää hyödytöntä churnia**TDD:n likainen salaisuus:
3. **Useimmat ennen toteutusta kirjoitetut testit kirjoitetaan uudelleen tai poistetaan.**Miksi?

Koska:

## Ymmärsit vaatimukset väärin

Et vielä tiennyt eturistisidejuttuja*Löysit paremman API-suunnittelun*Tajusit, 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 todellisuus**TDD 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 kattavuus**Käsiteltävät reuna-asiat

### Puhdas, luettavissa oleva toteutus

Tyhjennä API-suunnittelu**Dokumentaatio (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:
   
   ```python
   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 koodi*Avoimet 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:*Piirros**kokoonpano (vaihe 1)**

Maalit*kerrokset (vaihe 2)*Varnish

```python
# 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:*Luonnos*sotkuisesti (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 jalosta**Lomake (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 -putkeen*Tässä kohtaa tämä käy mielenkiintoiseksi: olen itse asiassa*Toteutettu

**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ä*Sitten*Suorittaja
- 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

*tasan*Vaihe 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: Optimointivaihe**Kun koodi läpäisee perustoteutuksen, DiSE siirtyy**optimointi.

1. **Tämä on hiomisvaihe.**Järjestelmä:
2. **Puhdistaa toteutusta**Poistaa virheiden yhteydessä lisätyn vianetsintähavainnon
3. **Yksinkertaistaa liian monimutkaista koodia**Parantaa nimeämistä ja rakennetta
4. **Suorittaa staattisen analyysin**Iteratiivisesti optimoitu

(3 iterointia oletuksena)

Tämä on*tasan*

## Vaihe 2.

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

Tyyppivihjeet*Puhtaampi 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 varastointivaihe**Vain
3. **jälkeen**optimointi 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 suoritusominaisuudet*Sitten*vain, jos testit läpäisevät

, koodi on:

### Säilytä

RAG-muisti

```python
# 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ä