Back to "Comment bâtir un logiciel: Faire fonctionner, Favoriser la qualité de l’imageM SK2 Laisser tomber"

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

Comment bâtir un logiciel: Faire fonctionner, Favoriser la qualité de l’imageM SK2 Laisser tomber

Wednesday, 19 November 2025

Pourquoi écrire les tests last, et pourquoi cela' n’est pas ce que vous pensez qu’il signifie

Tenue controversée: Test-Développement dirigé est une pratique brillante qui permet de résoudre le problème erroné pour la plupart des travaux créatifs.

Les trois phases de l’élaboration du logiciel (qui fonctionne réellement)

C'est ainsi que je crée un logiciel.

Faire fonctionner. Favoriser la qualité de l’image. Fermer avec des essaisM SK2

Dans cet ordre. Toujours.

Ce n’est pas parce que je suis lazi, mais parce qu’il ne me plaît pas de faire des tests. c'est ainsi que le travail créatif se produit. Lorsqu’il s’agit d’explorer un espace de problèmes que vous ne connaissez pas,

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

Ce n’est pas parce que je suis lazi, mais parce qu’il ne me plaît pas de faire des tests. c'est ainsi que le travail créatif se produit. Lorsqu’il s’agit d’explorer un espace de problèmes que vous ne connaissez pas,

Permettez-moi d’expliquer.

Phase 1: Faire fonctionner (The Private Sketch)

Il s’agit de la partie messy, c’est-à-dire celle où je ne sais pas ce que je fais.

J’essaie de comprendre.

  • Cette API fonctionne-t-elle réellement comme le prétendent les docts?
  • Puis-je même résoudre ce problème avec les outils que j'ai?
  • Qu’est-ce que "correctM SK1 signifie même dans ce contexte
  • Est-ce un problème de deux heures ou d’une semaine?

Il s’agit d’une invention.

Et vous savez ce qui tue le jazz.

Pourquoi il n'y a pas encore de tests?

Parce que la forme est encore fluide.

Imaginez que vous êtes un sculpteur. Vous avez un bloc de marbre et une idée vague.

TDD dit: : " Avant de toucher ce marbre, écrivez exactement à quoi ressemble un oiseau.

Que ce n’est pas la façon dont les oiseaux sont sculptés, mais que le logiciel est écrit.

Sculpteurs réels tirer le formulaire de première ligne. Ils dessinent, ils fabriquent des maquettes, ils déchipent de gros morceaux de pierre pour trouver la forme générale.

Le code est le même.

En phase 1, IM SK1m écrit:

# 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

Ce code est privé. Il s’agit d’une conversation avec moi-même.

Les tests de rédaction pour cela sont pire que inutiles—it's activement nocif. Parce que chaque fois que je me rends compte "oh, attendez-vous , cette approche est tout à fait erronéeM SK2 Je' devrais réécrire les tests aussiMSC4

Ce n’est pas rigoureux, ce n’ est pas bureaucratique.

La liberté d’échouer rapidement

La phase 1 est approximative vitesse d’apprentissage.

Je dois essayer cinq approches différentes en une heure. Il faudrait être à résoudre.

Les essais ralentissent cela. Non parce que les tests sont lentes, mais parce spécification prématurée est décès.

Si je rédige des tests avant de comprendre le problème, mauvaise chose. Ensuite, je m’investis émotionnellement dans la chose erronée. Ensuite, j’ai défendu la chose erronée lors de l’examen du code.

Il est préférable de ne pas réussir rapidement, en privé , sans tests pour maintenir et sans ego pour protéger.

Qu’est-ce qui signifie "Working" en phase 1

Cela signifie:: "Je l’ai exécuté . Je lui ai donné quelques intrantsM SK3 Il a produit des extrants qui semblent raisonnablesMST4 IMSC5m MST6 je suis convaincu que j’en comprends maintenant le problème

Ce n’est pas le cas.

No edge cases. No error handling. No eleganceM SK2

Just a spike. A proof of conceptM SK1 A sketch.

Phase 2: Faire en sorte qu’il soit beau (Le raffinage)

Ok, donc maintenant je comprends le problème. J’ai quelque chose qui fonctionneM SK2 au moins pour la voie heureuseMSC3

Maintenant je peux commencer à prendre soin de la qualité.

La phase 2 est là où IM SK1

1. Nettoyage du message

Exemple de 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')
    ]

C# Exemple:

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

Améliorer le nom. Annotations de type. Documentation XML . Mise en œuvre de Cleaner LINQM SK3

2. Aligner avec la spécification

Maintenant que je sais ce que je fais en fait, j’en reviens aux exigences originales et je m’assure de résoudre le problème. de la droite problème, non seulement a) Le nombre d'enfants admissibles à l'éducation et au perfectionnement professionnel; problème.

Cela révèle souvent des lacunes.

  • "OhM SK1 ils voulaient que cela traite différemment les chiffres négatifs ."
  • "WaitM SK1 il y a là ' est un cas de bord où la valeur pourrait être Aucune vs.
  • Le spec dit 'double' mais je pense qu’il s’agissait d’une échelle par un facteur configurable .'"

Je les corrige. . Parfois, je renvoie à la spécification.

3. Polish the API Surface

C'est là que je pense au L'appelant's expérience.

Python:

# Phase 1: Works, but awkward to use
process_thing(data)

# Phase 2: Clear, flexible, composable
extract_doubled_values(
    items,
    scale_factor=2.0,
    include_zeros=False
)

C#:

// Phase 1: Works, but awkward to use
ProcessThing(data);

// Phase 2: Clear, flexible, composable
ExtractDoubledValues(
    items,
    scaleFactor: 2.0,
    includeZeros: false
);

// Or even better with a fluent API
items.ExtractValues()
     .Scale(by: 2.0)
     .ExcludingZeros()
     .ToList();

Amélioration de la désignation. Défaults sensibles . Configuration lorsque cela est important. API fluents, le cas échéantM SK3

Il s'agit de aéronautique. C'est là que le bon logiciel provient de.

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

Pourquoi il n'y a toujours pas de tests?

Oh, JeM SK1je le fais constamment . J’ai ouvert un REPL.

Mais je ne suis pas écrire ces expériences comme des tests formels encore.

Pourquoi? Parce I'Je suis toujours en train de découvrir ce que ' vaut la peine d’essayer

Dans la phase 1, je ne savais pas ' si cette fonction existerait même

  • Le facteur de l’échelle ne peut pas être négatif.
  • "Si la liste est videM SK1 nous devrions retourner vide , non Aucune."
  • "Nous devons traiter les valeurs numériques non -gracefullyM SK2

Ces perceptions apparaître à partir de la réfactorisation. Ils ne sont pas évidents à l’avant-plan.

Si j’ai écrit les tests en phase 1, IM SK2 je les réécrirai maintenant . si je les écris après J’ai amélioré la mise en œuvre.

Phase 3: Démarrage par des essais (Phase de partage)

Ok, maintenant c’est le vrai

Le code est propre.

Maintenant, je écrive des essais. Beaucoup d’essais . En tant que-fermerM SK3toMSC4peupleMST5couverteM ST6en tant qu’essai possibleM st8

Pourquoi maintenant?

Parce que Les essais sont un mécanisme de partage.

Ils ne sont plus pour moi.

Les tests sont pour:

  1. Future Me (qui oubliera tout en trois mois)
  2. Mes camarades d'équipe (qui a besoin de croire que ce code fonctionne
  3. Le pipeline CI (qui doit prévenir les régressions )
  4. Mainteneurs futurs (qui a besoin de le réfactoriser en toute sécuritéM SK1

Les tests sont les couche de présentation pour ma mise en œuvre.

Ils documentent:

  • Ce que ce code fait
  • Quels sont les intrants valides?
  • Quels résultats attendus
  • Quels sont les cas marginaux que j’ai pris en considération?
  • Quels sont les hypothèses que je fais?

Protection complète, Aucun compromis

En phase 3, Je fais de la difficulté à tester

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)

Il s’agit du réseau de sécurité.

Maintenant, mes collègues de l’équipe peuvent

  • Modifier cette fonction avec confiance
  • Comprendre les cases de bord
  • Prendre immédiatement les régressions
  • Réfacteur sans peur

Examens en tant que documentation

Les bons essais sont une meilleure documentation que les commentaires:

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

Les essais ne font pas de fausses déclarations.

Que's Documentation exécutable. C’est ce qui fait que les tests sont précieux.

Le partage des frontières

Il s’agit là de la question cruciale. La phase 3 se produit avant que personne n’ait touché le code.

Je ne partage pas le code sans épreuves. EverM SK2

Mais je ne écrivais pas les tests avant que le code soit prêt à être partagé.

Les phases sont:

  1. Exploration privée (juste moiM SK1 pas de tests )
  2. Perfectionnement privé (just meM SK1 pas encore de tests)
  3. Partage public (tests , CIM SK2 révision du code, neuf yards entiersMSC4

Cela protège à la fois ma créativité et mes collègues de l’équipe' sainetéM SK1

Pourquoi cela protège la créativité

Les défenseurs de la DDT disent que les tests « donnent confiance au réfactor »

Oui! Mais seulement si vous savez déjà ce que vous construisez

Lorsque I'm en phase M SK1 Je n’ai pas besoin de confiance pour réfactor ' J’ai besoin d’une permission pour jeter tout de côté.

Les tests créent une dette psychologique. Une fois que je les ai écrits, j’ai refusé de les supprimer.

En reportant les essais jusqu’à la phase 3,, je maintiens la phase psychologiquement bon marché.

Je peux:

  • Essayez des idées sauvages
  • Mettre à l'écart des approches entières
  • Modifier le design de façon à ce qu'il soit
  • Trouver le problème I'm en fait Résultats obtenus

Tous sans la culpabilité de " mais j’ai écrit tous ces tests..."

C'est ainsi que fonctionne la créativité.

Vous ébauchez de façon loose d’abord. Vous ne le faites pasM SK1 vous ne commencez pas avec les détails.

Comment cela convient avec Agile et XP (et où il diffère)

Nous devons être clairs sur quelque chose. Ce n’est pas "test de développement ultérieur."

Il s'agit de "test pendant la mise au pointM SK1 mais à la bonne heure ."

Le moment de la mise en œuvre Lorsque : pour ajouter les essais d’unité est essentiel. Trop tôt et vous 'vous spécifiez l’inconnuM SK2 trop tard et vous~ 'vous expédiez sans filets de sécurité.

Les pratiques XP que je garde

La programmation extrême a des pratiques brillantes à suivre:

Intégration continue

  • Chaque fonction passe par la CI avant d'être fusionnée
  • Des essais automatisés sont effectués sur chaque engagement au principal
  • Aucune construction défectueuse n'est tolérée

Programmation par pair / Examen du code

  • Le code de la phase 3 est examiné avant le partage
  • Les essais font partie de l'objet révisable.
  • Le perfectionnement collaboratif améliore la conception

Modèle simple

  • La phase 2 est sur tout simplification
  • Retirer ce que vous n’avez pas besoin
  • Faites-le aussi simple que possible, pas plus simple

Réfactorisation

  • La phase 2 est consacrée à la réfactorisation
  • Nettoyage du code précédemment c'est ce qu'il faut faire.
  • Les essais protègent le réfactorage (en phase 3+)

Où suis-je différent de la TDD stricte?

graph TB
    subgraph "Traditional TDD (Red-Green-Refactor)"
        TDD1[Write Failing Test] --> TDD2[Write Minimal Code to Pass]
        TDD2 --> TDD3[Refactor]
        TDD3 --> TDD1
    end

    subgraph "Three-Phase Approach (Explore-Refine-Lock)"
        P1[Explore Solution Space] --> P2[Refine Implementation]
        P2 --> P3[Write Comprehensive Tests]
        P3 --> P4[Share With Team]
    end

    style TDD1 stroke:#ffaa00,stroke-width:2px
    style TDD2 stroke:#ffaa00,stroke-width:2px
    style TDD3 stroke:#ffaa00,stroke-width:2px
    style P1 stroke:#ff6b6b,stroke-width:3px
    style P2 stroke:#4ecdc4,stroke-width:3px
    style P3 stroke:#95e1d3,stroke-width:3px

TDD dit: "Tests d’abordM SK1 Toujours. rouge - vertMSC4Le réfacteur est le seul moyen de corriger les erreurs.

Je dis: "Tests lorsque vous savez ce que vous testez're testingM SK2 Explorer -Réfinir

La différence clé:

TDD Trois
Test définit l'interface Exploration définit les interfaces
rouge - vert M SK2 cycle de réfacteur Explorer-RéfinirM SK5Progression du bouclet M
Les essais écrits avant la mise en œuvre Partage
Évaluer tout d’abord Étudier à la bonne heure pour chaque chose
MicroM SK1cycles (minutes) MacroMSC5phases ≥heuresMNK7jours MNK8 |

Le moment est essentiel

C’est là le point crucial de l’interprétation. Il n’est pas "tests la dernière fois." Il est 's "test avant de partagerM SK5

Incorrect timing ( trop tôtM SK1

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

Calendrier correct (Phase 3):

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

Résultat de la même qualité. La moitié de l’échantillonnage

C'est Agile

Agile dit: "Responder au changement après avoir suivi un plan ."

Le TDD peut devenir sa propre forme de plan.

Mon approche englobe le changement:

  • Phase 1: Changer rapidementM SK1 découvrir la bonne solution
  • Phase 2: Modifier délibérémentM SK1 raffiner vers l’élégance
  • Phase 3: Changer soigneusement , protéger ce qui fonctionne

Il s'agit de plus agile que le strict TDD, parce qu’il retarde l’engagement jusqu’à ce que vous ayez de l’information

Approches contradictoires: A CM SK1 Exemple

Approche stricte du TDD:

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

Remarque de la boucle? Trois essais réécrits au fur et à mesure que le design évolueM SK1

Trois -Approche de phaseM SK1

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

Un ensemble de tests. Rédigé une foisM SK1 Correction la première fois . Parce que je savais ce que j’étais en train d’élaborer

Connexion du Manifeste Agile

Rappeler les valeurs:

"Software de travail sur une documentation complète"

  • La phase 1 arrive au logiciel de travail le plus rapidement possible
  • La phase 3 rend les essais la documentation complète

"Réponse au changement par suite d’un plan

  • Les phases 1-2 acceptent le changement
  • Locks de phase 3 dans la conception finale

"Individuels et interactions sur les processus et les outils "

  • Don't let TDD dogma override good judgment
  • Collaborer lorsque cela aide (Phase 2-3), Explorer seul lorsqu’il ne le fait pasM SK2t

" Collaboration entre les clients et la négociation des contratsM SK1

  • La phase 2 est conforme à la spécification après Comprendre le problème
  • Il est préférable de fournir ce dont ils ont besoin plutôt que ce qu'ils ont spécifié.

Pourquoi cela réduit la chute inutile

TDD's dirty secret: La plupart des tests écrits avant la mise en œuvre sont rédigés ou supprimés.

Pourquoi? ParceM SK1

  • Vous avez mal compris les exigences
  • Vous ne connaissez pas encore les cases de bord
  • Vous avez découvert une meilleure conception API
  • Vous vous êtes rendu compte que la fonction n’était pas nécessaire

Tous les tests que vous écrivez dans la phase 1 sont probablement dépensés.

Il est préférable d'écrire un de tests dans la phase 3, lorsque vous savez réellement ce que vous construisez ', plutôt qu’à écrireM SK3rewriteMSC4rewrrite pendant l’explorationMST5

La promesse du TDD contre la réalité de .

Promesses de TDD: "Les essais déterminent votre conception! Ils vous aident à découvrir de bonnes APIsM SK2

Réalité TDD: "J’ai écrit des tests pour un API que j’avais jugé logiques. Puis je l’ai mis en œuvre et j’ai réalisé que le API était incommodeM SK2 Maintenant, j’en rédige les deux tests et le code ."

Que ce n’est pas le design du ' que c’est le . thrashing.

Mon approche: Conception par la mise en oeuvre, puis fermeture avec les essais.

Le résultat final est le même :

Pourquoi ce logiciel offre-t-il encore des logiciels disciplinaires

Voici ce qu’est cette approche : non:

  • "Cowboy coding"
  • "Sans essaisM SK1
  • "Armée de terre et prièreM SK1

Au moment où j’achève la phase ', mon code a :

  • La couverture complète des essais
  • Cases à bord traitées
  • Mise en œuvre lisible Clean,
  • Éclaircissement de la conception d'API
  • Documentation (par l'entremise des essaisM SK1

Exactement le même que TDD.

La différence est Lorsque : Ces tests ont été écrits.

La discipline vient de la frontière

La règle est simple: Aucun code ne laisse la phase 2 sans la phase

I don'tM SK1

  • Envoyer un code non testé
  • Ouverture des relations publiques sans tests
  • Merger au principal sans passer la CI
  • Déploiement sans rapports de couverture

La discipline est la suivante : ne partage pas le code sans tests.

Les métaphores de l'artisanat

Ce n’est pas une nouvelle idée, c’est la façon dont le travail créatif a toujours fonctionné.

Maquette → Refine → Varnish

Les peintres ne commencent pas avec les dernières coupes de pinceaux.

Ils:

  1. Sketch La composition (Phase 1)
  2. Peinture Les couches (Phase 2)
  3. Varnish pour protéger et présenter (Phase 3)

Le vernis n’apparaît pas en premier, mais il n’est pas facultatif.

Projet → Modifier → Publier

Les écrivains n’éditent pas les textes qu’ils rédigent.

Ils:

  1. Ébauche (Phase 1)
  2. Édition sans cruauté (Phase 2)
  3. Publier avec confiance (Phase 3)

"Write drunkM SK1 edit sober" est un terrible conseil pour la vie, mais un grand conseil pour l’écriture .

Clay → Forme → Glaze

Les potteurs n’utilisent pas de glace avant la façonnement.

Ils:

  1. Écraser l'argile (Phase 1)
  2. Trim et raffinage le formulaire (Phase 2)
  3. Glaze et feu pour la durabilité (Phase 3)

La glace est cruciale. Mais elle arrive la dernièreM SK1

Comment ce document sert à cartographier le pipeline DiSE Codegen

C’est là où cela devient intéressant. a été appliquée Cette philosophie dans le système DiSE (Directed Synthetic EvolutionM SK1

Le pipeline de codage englobe explicitement ces trois phases:

Phase 1 dans DiSE: Étape d'exploration

Lorsque vous demandez à DiSE de résoudre un problème, il n’y a pas de lisià ̈re direct pour écrire le code parfaitM SK2 testé .

Au lieu de Générateur (codellama ou similaire ) obtient un mandat clairM SK2

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

Le système génère le code exploratoire:

  • Concentré sur la voie heureuse
  • Traitement des erreurs minimales
  • Aucune optimisation prématurée
  • Juste assez pour tester l'approche

En outre, les Exécuteur fonctionne. Non avec les essais d’unité officiels—justement avec les entrées de la spécificationM SK2

Si ce n'est pas le cas? Aucun problème. QueM SK2s apprentissage.

Le système a une escalation adaptative de phase 6-

  1. Essayez avec un modèle rapide (température basseM SK1
  2. Essayez de nouveau avec une plus grande créativité (une température plus élevéeM SK1
  3. Évacuer à un modèle plus puissant
  4. Ajouter l'enregistrement de débug pour comprendre les défaillances
  5. Donnez un contexte complet au modèle puissant
  6. Utiliser le modèle "godM SK1level" comme dernier recours

Il s'agit de exactement Phase 1. Le système étudie l’espace de la solution , apprend ce qui fonctionne M SK2 faille rapidement , et s’adapte

Aucun test n’est écrit durant cette phase. Le code est privé du processus de production.

Phase 2 dans DiSEM SK1 Étape d'optimisation

Une fois que le code passe l'exécution de base, DiSE se déplace vers optimisation.

Il s'agit de la phase d'affinage. Le systèmeM SK1

  1. Nettoyage de la mise en œuvre

    • Supprimer l'enregistrement de débugging qui a été ajouté pendant les défaillances
    • Simplifie le code trop complexe
    • Améliore le nom et la structure
  2. Effectuer une analyse statique

    - flake8 (style checking)
    - pylint (code quality)
    - mypy (type checking)
    - black (formatting)
    - radon (complexity analysis)
    
  3. Optimisation itérative (3 iterations par défaut)

    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
    

Il s'agit de exactement Phase 2. Le code est toujours privé (il n'est pas encore dans le registre

  • Noms meilleurs
  • Typer des indications
  • Structure plus propre
  • Complexité inférieure
  • Améliorer la performance

La forme n’est plus fluide. Nous savons ce que nous construisons. bon.

Phase 3 dans DiSE: Étape d'essai et de stockage

Seuls après optimisation DiSE génère et fonctionne Essais d'unité formels.

Voici la partie cruciale de l’essai : amélioration des spécifications et de la mise en œuvre, non l’exploration initiale

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

Les essais portent sur

  • Exemples de spécifications (correctnessM SK1
  • Cas à bord découverts durant la phase 2
  • Utilisation d'une erreur qui a été ajoutée au cours de la mise à niveau
  • Caractéristiques de rendement mesurées

Then, seulement si les tests réussissent, le code estM SK1

  1. Entreposé dans le Mémoire RAG (avec embeddingsM SK1
  2. Ajout à la Registre des nodes (en tant qu’artefact exécutable)
  3. Mise à disposition pour réutiliser dans les flux de travail futurs
  4. Suivi avec Évaluation de la qualité et Mesures de rendement

C’est la phase de partage. Le code est maintenant public (pour le système ). Il a des essais completsM SK2 Il'est ferméMSC4

Pourquoi cela fonctionne dans un système de production de code

L’approche en trois phases est la suivante : essentiels pour la génération de code dirigée par AI- car:

Phase 1: Les modèles ne comprennent pas au départ le problème

  • Ils doivent explorer l'espace de la solution.
  • Les tentatives initiales échouent souvent de façon intéressante
  • Les défaillances fournissent de l'information pour mieux essayer
  • Des essais prématurés obligeraient une spécification prématurée

Phase 2: Les modèles optimisent mieux avec le rétroaction

  • L'analyse statique révèle des problèmes réels
  • Amélioration des lignes directrices sur les mesures de rendement
  • Les cotes de complexité mettent en évidence les possibilités de simplification
  • C'est là que la qualité apparaît

Phase 3: Les essais documentent ce qui a été appris

  • Les essais captent les État de l'avancement la compréhension
  • Ils reflètent les Actualités Cases à bord
  • Ils encodent les refinés Conception de l'API
  • Ils corrigent la première fois.

La preuve: Il fonctionne en fait

Voici un exemple réel du système DiSE :

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

Le code final est:

  • Biens-testés M SK1essais, tous réussis )
  • Bon-optimisé (15% meilleur que la version initialeM SK2
  • Bien-documenté M SK1par des tests et des conseils de type)
  • Préparé à partager ( dans le registreM SK1

Exactement comme le promet TDD. Juste par un chemin plus naturel.

Le système se optimise lui-même

C’est là qu’il devient vraiment intéressant d’appliquer cette approche de trois phases, : et -. à elle-même.

L'article 5 de la Loi sur l'immigration et le statut des réfugiés Optimisateur de système récursif a des niveaux de 4

  1. Niveau 0: Optimiser les artefacts de code (en faire fonctionner,en les rendre bellesM SK2en en les immobiliser )
  2. Niveau 1: Optimiser les flux de travail qui utilisent ces artefacts (les mêmes trois phasesM SK1
  3. Niveau 2: Optimiser les outils et la logique de sélection (vous l’avez jugé )
  4. Niveau 3: Optimiser l'optimisateur lui-même (meta!)

À chaque niveau: explorer M SK1 raffiner → tester.

C’est le mantra : tout en descendant.

La véritable raison pour laquelle cela fonctionne

C’est là la vérité incommode. La plupart des codes sont écrits par des gens qui ne comprennent pas encore le problème.

Que ce n’est pas un échec réalité.

Les exigences sont ambiguës. Les spécifications sont incomplètes . TroisièmesM SK2 les API des parties sont mal documentés. Les cas de bord apparaissent du tout-outre-mer

Vous ne pouvez ' concevoir l’API parfaite à l’avance parce que vous n’avez pas de renseignements suffisants'

Les trois phases reconnaissent ce qui suit :

  1. Phase 1: Apprenez ce que vous ne savez pas
  2. Phase 2: Appliquer ce que vous avez appris
  3. Phase 3: Protéger ce que vous avez construit

En séparant l’exploration de la spécification, Je peuxM SK1

  • Explorer librement (sans dette d'essaiM SK1
  • Réfinir avec confiance (connaissant ce qui importeM SK1
  • Partage sécuritaire (avec des essais completsM SK1

Cela est vrai pour les humains qui écrivent un code. aussi vrai pour les LLM générant le code. Et pour les systèmes optimisant eux-mêmes.

Quand le TDD a vraiment un sens

I'm n’est pas anti--TDDM SK2 I 'm anti--prématuré TDD.

TDD fonctionne bien lorsque:

1. Le problème est bien

Si j’implique un algorithme connu (sorting,parsingM SK3 etcMSC4 le DDT est parfait . Je sais exactement ce que signifie "correct+" à l’avant-plan=.

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

2. Vous' corrigez un bogue

Si j'ai une régression, TDD est à juste titre:

  1. Rédiger un test qui reproduit le bug (red)
  2. Fixer le code (green)
  3. Réfacteur s’il y a lieu (réfacteur)

Perfect.

3. Vous' réfactorez le code existant

Si j'ai déjà une mise en œuvre et je veux changer sa structure, les tests (non comportementM SK1 sont essentiels précédemment I start.

Mais ce n’est pas la phase ' 1. Celle-ci ' est la phase

La vérité honnête

C’est ainsi que je travaille en fait. La plupart des les développeurs travaillent en fait, même ceux qui prétendent faire un TDD strict.

Étant donné que la créativité est compliquée. L’apprentissage est itératif . La compréhension émerge du fait de faire

Vous ne pouvez pas vous-même spécifier par ignorance.

Vous devez construire la chose pour comprendre la chose.

Je l’ai donc construit (Phase 1), je l’enrichis (Phasie \2), puis j’y mets le cap.

Dans cet ordre. Chaque fois.

Et vous savez ce que je veux dire.

Tout comme TDD l’a promis.

J’y suis arrivé par un autre chemin, —, qui respecte la façon dont le travail créatif se produit.


Qu’est-ce que le mantra de la mise en place de logiciels ?1 Do you TDD everything?2 Do you have a different approach?3 I’aimerais vraiment entendre vos commentaires.

Si vous êtes un puriste TDD et que ce poste vous a fait grimper l’œil, , est excellent. ! signifie que nous avons une discussion technique appropriée.

logo

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