Cómo construyo software: Hacer que funcione, hacerlo bonito, bloquearlo (Español (Spanish))

Cómo construyo software: Hacer que funcione, hacerlo bonito, bloquearlo

Wednesday, 19 November 2025

//

24 minute read

¿Por qué escribo las últimas pruebas, y por qué eso no es lo que crees que significa

**Toma controversial:**Test-Driven Development es una práctica brillante que resuelve el problema equivocado para la mayoría del trabajo creativo.

Esto es lo que funciona mejor.

Las tres fases del software de construcción (que realmente funciona)

Así es como construyo el software.

Es simple, es efectivo, y hace que los puristas de TDD frunzan educadamente sus cejas:

Haz que funcione.*Que sea bonito.*Enciérrenlo con pruebas.

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

En ese orden.*Siempre.*No porque sea perezoso.

No porque no valoro las pruebas.

Pero porque

así es como el trabajo creativo realmente sucede

Cuando estás explorando un espacio problemático aún no lo entiendes completamente.

  • No porque sea perezoso.
  • No porque no valoro las pruebas.
  • Pero porque
  • así es como el trabajo creativo realmente sucede

Cuando estás explorando un espacio problemático aún no lo entiendes completamente.

Déjame explicarte.

Fase 1: Hacer que funcione (El bosquejo privado)

Esta es la parte desordenada.La parte en la que no tengo ni idea de lo que estoy haciendo.

Estoy tratando de entender:

¿Esta API realmente funciona de la manera en que los documentos afirman?

¿Puedo incluso resolver este problema con las herramientas que tengo?

¿Qué significa "corregir" incluso en este contexto?*¿Es un problema de dos horas o un problema de dos semanas?*Esto es invención.

Esto es exploración.

Esto es jazz.

# Ugly, exploratory code
def process_thing(data):
    # TODO: This is terrible, fix later
    result = []
    for item in data:
        # Not sure if this is the right approach...
        x = item.get('value')
        if x:  # Is this even the right condition?
            result.append(x * 2)  # Why multiply by 2? Testing something...
    return result

¿Y sabes lo que mata el jazz?Alguien parado sobre tu hombro diciendo "escribe la prueba primero".¿ Por qué no hay pruebas todavía?

Porque*la forma sigue siendo fluida.*Imagínate que eres escultor.

Tienes un bloque de mármol y una vaga idea: "Quiero tallar un pájaro".

TDD dice: "Antes de tocar ese mármol, escriba exactamente cómo es un pájaro.

Define su envergadura.Especifique el ángulo de cada pluma.

Ahora esculpe esas especificaciones".*Así... no es como se tallan los pájaros. (O el software se escribe, como resulta.)*Escultores reales

Primero desbaratar la forma.Ellos dibujan.

Hacen maquetas.*Cortan grandes pedazos de piedra para encontrar la forma general.*Sólo después refinan los detalles.

El código es el mismo.

En la Fase 1, estoy escribiendo:

Este código es

privado.

Es una conversación conmigo misma.

Así es como me doy cuenta de lo que significa "trabajar".

Escribir pruebas para esto es peor que inútil — es

activamente perjudiciales.

Porque cada vez que me doy cuenta de "oh espera, todo este enfoque está mal", tendría que reescribir las pruebas también.

Eso no es rigor.

Eso es burocracia.

La libertad de fallar rápidamente

# Before (Phase 1) - Exploratory code
def process_thing(data):
    result = []
    for item in data:
        x = item.get('value')
        if x:
            result.append(x * 2)
    return result

# After (Phase 2) - Refined code
def extract_doubled_values(items: list[dict]) -> list[int]:
    """Extract 'value' field from items and double each one.

    Only includes items where 'value' is present and truthy.
    """
    return [
        item['value'] * 2
        for item in items
        if item.get('value')
    ]

La fase 1 es sobre

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

velocidad de aprendizaje.

Necesito probar cinco enfoques diferentes en una hora.

Necesito descubrir que la biblioteca de terceros que pensé usar... no es tan publicitada.*Necesito darme cuenta que el problema que estoy resolviendo no es el problema.deberesolverlo.*Las pruebas ralentizan esto.

No porque las pruebas sean lentas, sino porque

  • especificación prematura es la muerte.
  • Si escribo pruebas antes de entender el problema, estoy escribiendo pruebas para el
  • cosa equivocada.

Entonces estoy emocionalmente invertido en lo incorrecto.

Entonces estoy defendiendo lo equivocado en la revisión de código.

Es mejor fallar rápido, en privado, sin pruebas para mantener y sin ego para proteger.Lo que significa "trabajar" en la fase 1Significa: "Yo lo hice.

Le di algunas entradas.

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

Produjo productos que parecen razonables.

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

Tengo un 60% de confianza en que entiendo el problema ahora".

Eso es.**No hay casos de ventaja.**No hay manejo de errores.

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

Sin elegancia.

*Sólo un pico.*Una prueba de concepto.

Un sketch.Fase 2: Hacerlo bonito (el refinamiento)

Vale, ahora entiendo el problema.Tengo algo que funciona, al menos para el camino feliz.

Ahora puedo empezar a preocuparme por la calidad.

  • La fase 2 es donde yo:
  • Limpiar el desorden

Ejemplo de Python:*C# Ejemplo:*Mejor nombre.

Anotaciones de tipo.*Documentación XML.*Implementación de LINQ más limpia.

2.

Alinearse con el espectro

Ahora que sé lo que estoy construyendo, vuelvo a los requisitos originales y me aseguro de que estoy resolviendo el problema.

derecha

problema, no sólo

aproblema.

A menudo esto revela lagunas:

"Querían que esto manejara los números negativos de manera diferente".

  1. "Espera, hay un caso de borde donde el valor podría ser Ninguno vs. 0.""La especificación dice 'doble' pero creo que significa 'escala por un factor configurable.'"
  2. **Arreglaré esto.**A veces me retracto en la especificación: "En realidad, deberíamos hacer X en su lugar porque Y."
  3. **3.**Polaco de la superficie API
  4. Aquí es donde pienso en elLlamador

experiencia.**Python:**C#:

Mejor nombre.

  • Defaults sensibles.
  • Configuración donde importa.
  • APIs fluidas cuando proceda.
  • Esto es
  • artesanado.

Aquí es de donde viene el buen software.

¿ Por qué todavía no hay pruebas?

def test_extract_doubled_values_basic():
    items = [{'value': 5}, {'value': 10}]
    assert extract_doubled_values(items) == [10, 20]

def test_extract_doubled_values_skips_missing_values():
    items = [{'value': 5}, {'name': 'foo'}, {'value': 3}]
    assert extract_doubled_values(items) == [10, 6]

def test_extract_doubled_values_handles_zeros():
    items = [{'value': 0}, {'value': 5}]
    # By default, zeros are excluded (falsy)
    assert extract_doubled_values(items) == [10]

def test_extract_doubled_values_includes_zeros_when_configured():
    items = [{'value': 0}, {'value': 5}]
    assert extract_doubled_values(items, include_zeros=True) == [0, 10]

def test_extract_doubled_values_custom_scale_factor():
    items = [{'value': 5}]
    assert extract_doubled_values(items, scale_factor=3.0) == [15.0]

def test_extract_doubled_values_rejects_negative_scale_factor():
    with pytest.raises(ValueError):
        extract_doubled_values([], scale_factor=-1.0)

def test_extract_doubled_values_handles_empty_list():
    assert extract_doubled_values([]) == []

def test_extract_doubled_values_handles_non_numeric_values():
    items = [{'value': 'not a number'}]
    with pytest.raises(TypeError):
        extract_doubled_values(items)

Oh, lo estoy manejando constantemente.

Tengo una REPL abierta.

  • Estoy probando diferentes entradas.
  • Estoy ejercitando todos los caminos.
  • Pero no lo soy.
  • Escribiendo esos experimentos como pruebas formales todavía.

¿Por qué?

Porque

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

Todavía estoy descubriendo lo que vale la pena probar.

En la Fase 1, no sabía si esta función existiría.En la Fase 2, estoy descubriendo:"El factor escala no puede ser negativo.

Eso es una validación".

"Si la lista está vacía, deberíamos volver vacía, no Ninguno.""Necesitamos manejar los valores no numéricos con gracia".

Estos puntos de vista

emergen de la refactorización.

No son obvios por adelantado.

  1. **Si hubiera escrito pruebas en la Fase 1, las estaría reescribiendo ahora.**Si los escribo
  2. despuésHe perfeccionado la implementación, serán correctos la primera vez.
  3. **Fase 3: Bloquearlo con pruebas (la fase de compartir)**Bien, ahora es real.

El código está limpio.*La API tiene sentido.*He cubierto los estuches del borde.

Estoy seguro de lo que he construido.

Ahora escribo pruebas.

Muchas pruebas.

Pruebas tan cercanas a la cobertura completa como sea posible.¿Por qué ahora?

Porque

las pruebas son un mecanismo de compartir.Ya no son para mí.

Ya entiendo este código íntimamente.

  • He vivido en ella durante horas o días.
  • Las pruebas son para:
  • Futura yo
  • (que se olvidará de todo en tres meses)Mis compañeros de equipo(que necesitan confiar en este código funciona)

El oleoducto CI

(que necesita evitar regresiones)

Encargados del futuro

(que necesitan refactorizar esto con seguridad)

Las pruebas son las siguientes:capa de presentación

para mi implementación.Documentan:

¿Qué hace este código?Qué entradas son válidasQué productos esperar

¿Qué casos de borde he considerado

¿Qué suposiciones estoy haciendo?

Cobertura completa, sin compromisos

  • En la Fase 3, me esforzo mucho en las pruebas:
  • Esta es la red de seguridad.
  • Ahora mis compañeros de equipo pueden:

Modificar esta función con confianza

  • Entender los casos de borde
  • Regresiones de capturas inmediatamente
  • Refactorizar sin miedo

Pruebas como documentación

  • Las buenas pruebas son mejor documentación que comentarios:*Las pruebas no mienten.*No pueden irse a la deriva.
  • Si el comportamiento cambia, la prueba se rompe.
  • Eso es.

documentación ejecutable.

  • Eso es lo que hace que las pruebas sean valiosas.
  • La frontera compartida*Este es el punto crucial:*La fase 3 ocurre antes de que alguien más toque el código.
  • No comparto código sin pruebas.

Nunca.

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

**Pero tampoco escribo pruebas antes de que el código esté listo para compartir.**Las fases son las siguientes:

Exploración privada(solo yo, sin pruebas)

Refinamiento privado

(solo yo, todavía no hay pruebas) |-----|-------------| Participación del público (pruebas, CI, revisión de código, los nueve yardas enteros) Esto protege tanto mi creatividady | La cordura de mis compañeros. Por qué esto protege la creatividad

Los defensores de TDD dicen que "las pruebas te dan confianza para refactorizar".

¡Es verdad!Pero sólo si ya sabes lo que estás construyendo.

Cuando estoy en la Fase 1, no necesito confianza para refactorizar.

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

Necesito permiso para tirar todo por la borda.

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

Las pruebas crean deuda psicológica.

Una vez que los he escrito, soy reacio a borrarlos.

Incluso si están probando la cosa equivocada.Aplazando las pruebas hasta la fase 3, mantengo las fases 1 y 2

psicológicamente barato.

Puedo:

  • Prueba ideas salvajes
  • Deseche enfoques enteros
  • Cambiar radicalmente el diseño

Descubre el problema que soyEn realidadresolver el problema

Todo sin la culpa de "pero escribí todas esas pruebas..."

Así es como funciona la creatividad.

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

Usted bosqueja flojamente primero.

No empiezas con los detalles.

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

Cómo encaja esto con Agile y XP (y dónde se desvía)

Seamos claros sobre algo:

esto no es "prueba de desarrollo posterior".

Esto es

  • "probar mientras se desarrolla, pero en el momento adecuado."El calendario de
  • cuando

para añadir pruebas unitarias es crítico.

  • Demasiado pronto y estás especificando lo desconocido.
  • Demasiado tarde y vas a enviar sin redes de seguridad.

Las prácticas de XP que mantengo

  • Extreme Programming tiene prácticas brillantes que vale la pena seguir:
  • Integración continua

Cada característica pasa por CI antes de fusionarse

  • Pruebas automatizadas en cada commit a mainNo se toleran construcciones rotasProgramación por parejas / Revisión de código
  • Fase 3 código se revisa antes de compartir

Las pruebas son parte del artefacto revisable

El refinamiento colaborativo mejora el diseñoDiseño sencillo

La fase 2 es

  • todo sobre
  • simplificación
  • Quitar lo que no necesita
  • Hacerlo tan simple como sea posible, no más simple

Refactorización

Fase 2 es tiempo de refactorización dedicadoLimpiar el códigoantes

cerrándolo.

**Los ensayos protegen la refactorización (en la fase 3+)**Donde me desvío de la estricta TDD

TDD dice:"Primero las pruebas.

Siempre.Red-Green-Refactor es la única manera".

Yo digo:"Pruebas cuando sabes lo que estás probando.

Explore-Refine-Lock es más natural".

La diferencia clave:

TDD Tres fasesLa prueba define la interfaz La exploración define la interfaz

  • Ciclo rojo-verde-refactor Explore-Refine-Lock progresión
  • Pruebas escritas antes de la implementación Pruebas escritas antes
  • compartir

Prueba primero para todo Prueba en el momento adecuado para cada cosa

  • Microciclos (minutos) Macrofases (horas/días)
  • El tiempo es crítico
  • Aquí está la visión crucial:
  • No son "pruebas últimas". Son "pruebas antes de compartir".
  • Momento equivocado (demasiado temprano):

Momento adecuado (etapa 3):

El mismo resultado de calidad.*La mitad del churn.*Esto es ágil.

Agile dice:

"Responda a cambiar siguiendo un plan."La TDD puede convertirse en su propia forma de seguimiento de planes.

Una vez que has escrito pruebas, estás comprometida psicológicamente con ese diseño.

  • Mi enfoque abarca el cambio:
  • Fase 1: Cambiar rápidamente, descubrir la solución correcta
  • Fase 2: Cambiar deliberadamente, refinar hacia la elegancia
  • Fase 3: Cambiar cuidadosamente, proteger lo que funciona

Esto esmás

ágil que estricta TDD, porque aplaza el compromiso hasta que tenga información.

Enfoques de contraste: Un ejemplo de C#

Estricto enfoque TDD:

¿Te fijas en el churn?

Tres reescrituras de prueba a medida que el diseño evolucionó.

  1. **Enfoque de tres fases:**Un conjunto de pruebas.
  2. **Escrita una vez.**Corregir la primera vez.
  3. **Porque sabía lo que estaba construyendo.**La conexión ágil del Manifiesto

Recuerde los valores:

"Software de trabajo sobre documentación completa"

Fase 1 llega al software de trabajo

rápido

  1. La fase 3 hace pruebas de la documentación completa"Respondiendo a cambiar siguiendo un plan"
  2. Las fases 1-2 abarcan el cambioCerraduras de fase 3 en el diseño final
  3. **"Individuales e interacciones sobre procesos y herramientas"**No dejes que el dogma TDD anule el buen juicio

Colaborar cuando ayuda (Fase 2-3), explorar solo cuando no lo hace (Fase 1)

"Colaboración de clientes en la negociación de contratos"

La fase 2 se alinea con la especificación

después

  1. comprender el problemaMejor entregar lo que necesitan que lo que especificaron
  2. ¿Por qué esto reduce el churn inútilEl sucio secreto de TDD:
  3. la mayoría de las pruebas escritas antes de la aplicación se reescriben o eliminen.¿Por qué?

Porque:

Usted malinterpretó los requisitos

Aún no conocías los casos del borde.Descubriste un mejor diseño de APITe diste cuenta de que la función no era necesaria.

Cada prueba que escribas en la Fase 1 es probablemente un esfuerzo desperdiciado.

Mejor escribir

uno

conjunto de pruebas en la Fase 3, cuando realmente sabes lo que estás construyendo, que escribir-reescribir-reescribir durante la exploración.La promesa TDD vs. la realidadTDD promete:

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

"¡Las pruebas impulsan tu diseño!

  • ¡Te ayudan a descubrir buenas API!"
  • La realidad del TDD:
  • "Escribí pruebas para una API que pensé que tenía sentido.
  • Luego lo implementé y me di cuenta de que la API era incómoda.

Ahora estoy reescribiendo tanto las pruebas como el código".**Eso no es diseño.**Eso es.

Golpeando.Mi enfoque:

Diseño a través de la implementación, luego bloquearlo con pruebas.

  1. El resultado final es el mismo código bien probado y bien diseñado.
  2. Pero llegué allí con la mitad del churn.
  3. Por qué esto todavía ofrece software disciplinado
  4. Esto es lo que es este enfoque
  5. no:
  6. "Codificación de vaqueros"

"Sin pruebas"*"Enviar y rezar"*Para cuando termine con la Fase 3, mi código tiene:

Cobertura completa de los ensayosCasos de borde manejados

Aplicación limpia y legible

Borrar el diseño de APIDocumentación (por medio de pruebas)

Exactamente lo mismo que TDD.

  1. La diferencia es

    • cuando
    • esas pruebas fueron escritas.
    • La disciplina viene de la frontera
  2. La regla es simple:

    - flake8 (style checking)
    - pylint (code quality)
    - mypy (type checking)
    - black (formatting)
    - radon (complexity analysis)
    
  3. **Ningún código deja la Fase 2 sin la Fase 3.**Yo no:

    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
    

Código de commit no probadoAbrir PRs sin pruebasFusionar a la principal sin que pase IC

  • Despliegue sin informes de cobertura
  • La disciplina no está en las pruebas de escritura temprano.
  • Está dentro.
  • no compartir código sin pruebas.
  • Las metáforas artesanales

Esta no es una idea nueva.Es como el trabajo creativo siempre ha funcionado:

Sketch → Refinar → Barniz

Los pintores no comienzan con las pinceladas finales.*Ellos:*Sketchla composición (fase 1)

Pintura*las capas (Fase 2)*Barniz

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

proteger y presentar (Fase 3)

  • El barniz no es lo primero.
  • Pero no es opcional.
  • Borrador → Editar → Publicar
  • Los escritores no editan como se dibujan.

Ellos:Proyectodesordenado (Fase 1)

  1. Editar**despiadadamente (Fase 2)**Publicar
  2. con confianza (Fase 3)**"Escribir borracho, editar sobrio" es un consejo de vida terrible, pero un gran consejo de escritura.**Arcilla → Forma → Vidrio
  3. Los potters no se esmaltan antes de dar forma.**Ellos:**Lanzar
  4. la arcilla (Fase 1)Recortar y refinarla forma (fase 2)Vidrio y fuego

**para la durabilidad (Fase 3)**El esmalte es crucial.

Pero es el último.

Cómo se hace este mapa para el oleoducto DiSE Codegen*Aquí es donde esto se pone interesante:*aplicada

esta filosofía en el sistema DiSE (Directed Synthetic Evolution).

  • La tubería de códice encarna explícitamente estas tres fases:
  • Fase 1 en DISE: La etapa de exploración
  • Cuando le pides a DiSE que resuelva un problema, no salta directamente a escribir un código perfecto y probado.
  • En su lugar, el

Generador

  • (codellama o similar) tiene un mandato claro:
  • El sistema genera código exploratorio:
  • Centrado en el camino feliz
  • Manejo de errores mínimos

No hay optimización prematura

  • Lo suficiente para probar el enfoqueEntonces elEjecutador
  • Lo dirige.No con pruebas de unidades formales, sólo con las entradas de la especificación.¿Si falla?
  • No hay problema.*Eso es.*aprender.
  • El sistema tiene una escalada adaptativa de 6 etapas:

Pruebe con un modelo rápido (temperatura baja)

Inténtalo de nuevo con mayor creatividad (temperatura más alta)

User: "Calculate fibonacci numbers"

[Phase 1: Exploration - 3 attempts]
  Attempt 1: Works for small inputs, explodes on large ones (no safety limit)
  Attempt 2: Adds safety limit, but inefficient recursive approach
  Attempt 3: Switches to iterative DP (PASS)

[Phase 2: Optimization - 3 iterations]
  Iteration 1: Add type hints, improve naming (Score: 1.05)
  Iteration 2: Optimize memory usage with generator (Score: 1.10)
  Iteration 3: Add input validation (Score: 1.15)

[Phase 3: Testing and Storage]
  Generated 8 unit tests covering:
    - Basic cases (n=0, n=1, n=5, n=10)
    - Edge cases (n=negative, n=100, n=None)
    - Type validation

  All tests pass ✓

  Stored in RAG with quality score: 1.15
  Available for reuse: YES

Escalá a un modelo más poderoso

  • Añadir el registro de depuración para entender los fallos
  • Dar contexto completo al poderoso modelo
  • Utilice el modelo "nivel-dios" como último recurso
  • Esto es

*Exactamente.*Fase 1.

El sistema está explorando el espacio de solución, aprendiendo lo que funciona, fallando rápidamente y adaptándose.

No se han escrito pruebas durante esta fase.El código es privado para el proceso de generación.

Fase 2 en diSE: la fase de optimizaciónUna vez que el código pasa la ejecución básica, DiSE se mueve aoptimización.

  1. **Esta es la fase de refinamiento.**El sistema:
  2. Limpia la implementaciónElimina el registro de depuración que se añadió durante fallas
  3. Simplifica código demasiado complejoMejora el nombre y la estructura
  4. Ejecuta análisis estáticoOptimiza iterativamente

(3 iteraciones por defecto)

Esto esExactamente.

Fase 2.

El código sigue siendo privado (todavía no está en el registro), pero lo estamos puliendo:Mejores nombres

Escribir sugerenciasEstructura más limpia

Menor complejidad

Mejor rendimientoLa forma ya no es fluida.

Sabemos lo que estamos construyendo.

  1. **Ahora lo hacemos.**Bien.
  2. Fase 3 en DiSE: la fase de pruebas y almacenamientoSólo
  3. despuésOptimización DiSE genera y ejecuta

pruebas de unidades formales.

  • Aquí está la parte crucial: las pruebas se generan a partir de la
  • especificación y aplicación perfeccionadas
  • , no la exploración inicial.

Los ensayos cubren:*Ejemplos de especificación (corrección)*Casos de borde descubiertos durante la Fase 2

Manejo de errores que se añadió durante el refinamiento

Características de rendimiento que se midieron*Entonces,*sólo si se aprueban los ensayos

, el código es:

Almacenado en el

Memoria RAG

# I know what sort() should do. Tests first? Sure!
def test_sort_empty_list():
    assert sort([]) == []

def test_sort_single_element():
    assert sort([5]) == [5]

def test_sort_multiple_elements():
    assert sort([3, 1, 2]) == [1, 2, 3]

(con incrustaciones)

Agregado a laregistro de nodos:

  1. (como un artefacto ejecutable)
  2. Se pone a disposición de
  3. reutilización

en futuros flujos de trabajo

Rastreado con

calificaciones de calidad

Finding related posts...
logo

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