<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fran Iglesias</title>
    <description>The latest articles on DEV Community by Fran Iglesias (@franiglesias).</description>
    <link>https://dev.to/franiglesias</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F16833%2F21f3b8c3-034c-49c4-a515-0c7440369d24.png</url>
      <title>DEV Community: Fran Iglesias</title>
      <link>https://dev.to/franiglesias</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/franiglesias"/>
    <language>en</language>
    <item>
      <title>Golden, librería de snapshot testing en Go</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/franiglesias/golden-libreria-de-snapshot-testing-en-go-34o5</link>
      <guid>https://dev.to/franiglesias/golden-libreria-de-snapshot-testing-en-go-34o5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Golden&lt;/strong&gt; es una librería de &lt;em&gt;snapshot testing&lt;/em&gt; que he creado para Go. Después de unas semanas de trabajo, ya está declarada estable y he publicado la versión v1.0.0.&lt;/p&gt;

&lt;p&gt;En parte, como ejercicio de aprendizaje. En parte, porque no me acaban de encajar otras librerías disponibles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Snapshot testing&lt;/em&gt; es una técnica bastante usada en desarrollo frontend que consiste en guardar el output de nuestro código y usarlo como criterio para ejecutar futuros tests. De este modo, creamos un test de regresión que nos asegure que mantenemos el comportamiento actual de una unidad de software.&lt;/p&gt;

&lt;p&gt;En backend, el &lt;em&gt;snapshot testing&lt;/em&gt; no es tan usado, pero hay muchos casos de uso para esta técnica: objetos complejos, generación de archivos de todo tipo (JSON, CSV, XML, etc.) para los que es costoso desarrollar un test basado en aserciones.&lt;/p&gt;

&lt;p&gt;Además, esta técnica es muy potente usada con código legacy o, en general, con código que no tiene tests. Nos permite obtener una buena cobertura rápidamente, antes de intervenir en un código.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Golden&lt;/strong&gt; , además de &lt;em&gt;snapshot testing&lt;/em&gt;, nos permite trabajar con &lt;em&gt;approval testing&lt;/em&gt;. En esta modalidad, lo que hacemos es mantener el test fallando a propósito hasta que el snapshot que se ha generado sea revisado por nosotras o por una experta del dominio que nos pueda decir si el output es correcto o no. Cuando nos satisface, “aprobamos” el snapshot y lo usamos como criterio en los tests futuros.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Approval testing&lt;/em&gt; es una técnica adecuada cuando estamos escribiendo código nuevo que genera objetos complejos o documentos.&lt;/p&gt;

&lt;p&gt;Finalmente, &lt;strong&gt;Golden&lt;/strong&gt; ofrece la posibilidad de realizar los tests combinatorios de la técnica &lt;em&gt;Golden Master&lt;/em&gt;. Esta técnica consiste en bombardear el código a base de llamadas con distintas combinaciones de sus parámetros, de tal modo que lo forcemos a recorrer todos sus posibles flujos de ejecución.&lt;/p&gt;

&lt;p&gt;Para ello, no tenemos más que indicarle a &lt;strong&gt;Golden&lt;/strong&gt; listas de valores para cada parámetro de entrada de la unidad bajo test y generará todas las combinaciones posibles. Esta técnica puede ayudarnos a obtener una cobertura completa de un código existente sin tener que preocuparnos de entenderlo en profundidad. Una vez que hemos generado el “golden master” y estamos protegidas por el test, podemos empezar a aplicar técnicas de refactor para mejorar su diseño.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/franiglesias/golden" rel="noopener noreferrer"&gt;Golden: librería de snapshot testing en Go&lt;/a&gt;&lt;/p&gt;

</description>
      <category>articles</category>
      <category>tblist</category>
      <category>go</category>
      <category>testing</category>
    </item>
    <item>
      <title>Refactoring para quienes no refactorizan 3</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Sun, 03 Dec 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/franiglesias/refactoring-para-quienes-no-refactorizan-3-51m6</link>
      <guid>https://dev.to/franiglesias/refactoring-para-quienes-no-refactorizan-3-51m6</guid>
      <description>&lt;p&gt;En las entregas anteriores hemos mencionado varias veces la necesidad de mantener el tiempo de refactoring bajo control, evitando la tentación de llevarlo demasiado lejos.&lt;/p&gt;

&lt;p&gt;Esta precaución es necesaria porque corremos el riesgo de dar al código una forma que no sea adecuada a su evolución futura. Esto es, aunque podamos tener unas expectativas razonables sobre la evolución de nuestro negocio, en realidad no sabemos que nos deparará el futuro. Tomar decisiones sobre la estructura del código que no estén apoyadas por una necesidad puede llevarnos a mayores costes cuando tengamos que desandar ese camino.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuando un refactor llama a tu puerta
&lt;/h2&gt;

&lt;p&gt;De todos modos, y teniendo esta advertencia en mente, muchas veces el propio código nos va a dar indicaciones de que necesita refactoring. Estas indicaciones suelen venir en forma de &lt;em&gt;code smells&lt;/em&gt;, algunos de los cuales pueden gestionarse independientemente del significado del código. Más bien, esos smells apuntan a que la estructura de conocimiento está reflejada en el código, pero de una forma defectuosa.&lt;/p&gt;

&lt;p&gt;Voy a intentar poner un ejemplo. Los siguientes métodos operan sobre un mismo objeto, el cual no es el objeto en el que están definidos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def standard_deviation(consumptions)
        Math.sqrt(variance(consumptions))
    end

    def variance(consumptions)
        sum = consumptions.sum(0.0) { |element| (element - average(consumptions)) ** 2 }
        sum / (consumptions.size - 1)
    end

    def average(consumptions)
        consumptions.sum(0.0) / consumptions.size
    end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dicho de otra forma. Los comportamientos representados por estos métodos en realidad pertenecen a un objeto consumptions que aún no tenemos, pero del cual el código nos está diciendo que, al menos, deberíamos considerar su existencia.&lt;/p&gt;

&lt;p&gt;Este consumptions es actualmente un array que agrega los consumos de una oficina. O, en general, representa una colección de consumos que nos interesa para hacer un análisis. Tratarlo como un array es propio de un enfoque procedural de la programación, pero en orientación a objetos, consumption debería ser un objeto con sus propios comportamientos. En este caso: agregar los consumos y proporcionarnos ciertos índices estadísticos que nos interesan y que se obtienen a partir de sus datos.&lt;/p&gt;

&lt;p&gt;La señal que nos indica que hay una posibilidad de refactoring es bastante visible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hay un grupo de métodos de un objeto que no llaman a otros métodos del propio objeto, excepto a los que forman parte del mismo grupo. Por ejemplo, &lt;code&gt;standard_deviation&lt;/code&gt; usa &lt;code&gt;variance&lt;/code&gt;, el cual usa &lt;code&gt;average&lt;/code&gt;, pero no usan otros métodos del objeto.&lt;/li&gt;
&lt;li&gt;Esos métodos tienen un parámetro en común, que es sobre el que trabajan. Todos trabajan sobre&lt;code&gt;consumptions&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ahora bien, si nos fijamos, &lt;code&gt;consumptions&lt;/code&gt; solo guarda las lecturas de consumo. En el artículo anterior mencionamos que podría ser interesante guardar todo el objeto &lt;code&gt;Consumption&lt;/code&gt;. En primer lugar, porque es un objeto y así mantenemos su integridad. En segundo lugar, nos aporta más información que podría llegar a ser útil en algún momento.&lt;/p&gt;

&lt;p&gt;¿A dónde quiero llegar? Si hago este refactor ahora mismo, encapsulando el array &lt;code&gt;consumptions&lt;/code&gt; en un objeto estoy tomando decisiones que pueden condicionar el desarrollo futuro del software. Ahora me parece muy claro que podría encapsular &lt;code&gt;consumptions&lt;/code&gt; y beneficiarme de sus comportamientos. Pero ¿y si en el futuro lo que necesito es tener los objetos &lt;code&gt;Consumption&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Suele ser preferible esperar a tener más contexto antes de proceder a un refactor, incluso aunque sea muy evidente. Por ejemplo, que tengamos una tarea que toca esa área.&lt;/p&gt;

&lt;h2&gt;
  
  
  Una fuente de datos alternativa
&lt;/h2&gt;

&lt;p&gt;Ahora que hemos cambiado la forma de agregar los datos por oficina, nos dicen que algunas oficinas podrían obtener la información en formato JSON. Hay que tener en cuenta que a partir de ahora se nos proporcionarán varios archivos con los datos, pero en los distintos formatos. De hecho, es perfectamente posible que sean varias decenas de archivos si cada oficina nos enviase uno diferente.&lt;/p&gt;

&lt;p&gt;Así que tenemos que hacer estos cambios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poder procesar varios archivos&lt;/li&gt;
&lt;li&gt;Agrupar toda la información&lt;/li&gt;
&lt;li&gt;Tener un procesador extra para Json&lt;/li&gt;
&lt;li&gt;Elegir el procesador según el archivo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los dos últimos puntos encajan con un patrón &lt;em&gt;Strategy&lt;/em&gt;: necesitamos poder escoger entre varios algoritmos en tiempo de ejecución (un lector de CSV y un lector de JSON). Para ello, tenemos que disponer de esos distintos algoritmos y un mecanismo que sepa cuál escoger en cada caso.&lt;/p&gt;

&lt;p&gt;La cuestión ahora es hacer un refactoring del código actual hasta introducir este patrón para una sola estrategia, que es la que tenemos ahora. Comprobamos que todo el comportamiento actual se mantiene y una vez consolidados los cambios, introducimos la estrategia para otros formatos de archivo. Y, como veremos, este último paso esta vez será muy sencillo y tendrá poco riesgo.&lt;/p&gt;

&lt;p&gt;¿Por qué proceder así? La idea de no mezclar las fases de refactoring e introducción de nuevas features busca reducir el riesgo de mezclar regresiones en el comportamiento actual y la introducción de nuevos bugs.&lt;/p&gt;

&lt;p&gt;El refactor preparatorio estaría protegido por los tests existentes, de modo que si provocamos una regresión la detectaremos y podremos corregirla, deshaciendo el cambio que la provocó o haciendo las modificaciones necesarias. Por otro lado, una vez consolidado el refactor, la introducción de código nuevo puede ser asegurada mediante TDD o, si no, con test posteriores. De este modo, las fuentes de posibles errores se mantienen separadas y son fáciles de identificar y corregir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extrayendo un objeto colaborador
&lt;/h3&gt;

&lt;p&gt;Así que vamos a empezar. En OOP preferimos objetos pequeños con responsabilidades bien definidas que trabajan colaborando. En el ejemplo que tenemos, tendría sentido un objeto encargado de leer los datos de los archivos. Ahora mismo, eso ocurre en el método:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def obtain_consumptions(file_name)
    data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)
    data.map do |row|
        Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate que en el cuerpo del método no tenemos llamadas a otros métodos del objeto &lt;code&gt;ConsumptionAnalyser&lt;/code&gt; lo que nos indicaría que tiene sentido extraerlo. Este refactor se llama &lt;code&gt;Extract class&lt;/code&gt; y consiste básicamente en crear una clase nueva a la que se mueven los métodos deseados y usándolo donde la necesitemos.&lt;/p&gt;

&lt;p&gt;Algunos IDE ofrecen una automatización de este refactor dependiendo del lenguaje. Pero esencialmente se hace asé:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creamos la nueva clase&lt;/li&gt;
&lt;li&gt;Copiamos y pegamos en ella los métodos escogidos&lt;/li&gt;
&lt;li&gt;Adaptamos lo que sea necesario&lt;/li&gt;
&lt;li&gt;Reemplazamos los métodos originales con llamadas a esta clase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La nueva clase se llamará CSVConsumptionsProvider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CsvConsumptionsProvider

end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora, copiamos y pegamos el método &lt;code&gt;obtain_consumptions&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CsvConsumptionsProvider
    def obtain_consumptions(file_name)
        data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)
        data.map do |row|
            Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
        end
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puede ser el momento de revisar el nombre del método y realizar otros ajustes que veamos necesarios. Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CsvConsumptionsProvider
    def from_file(file_name)
        data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)
        data.map do |row|
            Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
        end
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El último paso será introducir este objeto como colaborador de &lt;code&gt;ConsumptionAnalyzer&lt;/code&gt;. Antes de eso, nos aseguramos de que los tests están pasando.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
    def initialize(provider = CsvConsumptionsProvider.new)
        @provider = provider
    end

    CONSUMPTIONS_A_YEAR = 12

    def execute(file_name, deviation_factor = 1.4)
        normalized = @provider.from_file(file_name)
        offices = offices(normalized)
        outliers = outliers(deviation_factor, offices)

        puts outliers
        puts "Data sample #{normalized.size} rows"
        puts "Found #{outliers.size} outliers"
        puts "Found #{outliers.size / offices.size} per office"
    end

    # Code removed for clarity

end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este cambio debería permitir que los tests pasen sin problema.&lt;/p&gt;

&lt;p&gt;En este ejemplo, estamos haciendo que &lt;code&gt;provider&lt;/code&gt; sea opcional, creando una instancia por defecto. En otros lenguajes, podemos hacer algo similar a lo que sigue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def initialize(provider = nil)
    if provider.nil?
        @provider = CsvConsumptionsProvider.new
    else
        @provider = provider
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Preparándose para el patrón Strategy
&lt;/h3&gt;

&lt;p&gt;Extraer funcionalidad a objetos colaboradores es una buena forma de darle estructura al código. Pero nuestro analizador todavía depende que la fuente de datos sea CSV.&lt;/p&gt;

&lt;p&gt;Como no queremos depender directamente de una tecnología o formato específico, necesitamos introducir un &lt;em&gt;Mediador&lt;/em&gt;. Un &lt;em&gt;Mediador&lt;/em&gt; es un objeto que se introduce para romper la dependencia directa entre dos objetos. De este modo, uno puede evolucionar sin saber nada del otro. Ambos quedarán acoplados al mediador, pero es una dependencia más ligera.&lt;/p&gt;

&lt;p&gt;Nuestro mediador representa la idea abstracta de un proveedor de consumos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionProvider
    def from_file(file_name)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y en su primera implementación, simplemente hace uso del &lt;code&gt;CSVConsumptionProvider&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize(provider = CsvConsumptionsProvider.new)
        @provider = provider
    end
    def from_file(file_name)
        @provider.from_file(file_name)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por supuesto, tenemos que cambiar la dependencia en &lt;code&gt;ConsumptionAnalyzer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
    def initialize(provider = ConsumptionsProvider.new)
        @provider = provider
    end

    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Todos estos cambios no alteran el comportamiento y los tests siguen pasando. Estamos casi terminando el refactor. El beneficio que hemos conseguido es que ahora, &lt;code&gt;ConsumptionAnalyzer&lt;/code&gt; no tiene ni idea de que está leyendo datos de un archivo CSV, no hay ninguna referencia que haga pensar en ello.&lt;/p&gt;

&lt;h3&gt;
  
  
  Csv como Strategy
&lt;/h3&gt;

&lt;p&gt;El siguiente paso sucede en &lt;code&gt;ConsumptionsProvider&lt;/code&gt; y consiste en dejar de usar incondicionalmente &lt;code&gt;CsvConsumptionsProvider&lt;/code&gt;. De momento, sabemos que el criterio para escoger un Provider concreto es el tipo de archivo, que podemos determinar por la extensión de su nombre. Eso es lo que vamos a introducir ahora:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize(provider = CsvConsumptionsProvider.new)
        @provider = provider
    end
    def from_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            return @provider.from_file(file_name)
        end
        raise NotImplementedError.new , "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto puede parecer innecesario en este punto, ya que solo tenemos un tipo de proveedor. Sin embargo, creo que se puede entender por donde vamos. Este refactor nos ha dejado en una situación en la que introducir otro proveedor simplemente requeriría escribir una clase nueva y modificar la condición para que el programa lo reconozca.&lt;/p&gt;

&lt;p&gt;Vamos a arreglar un poquito el código dado que sigue muy condicionado por tener una sola estrategia. Por ejemplo, así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize

    end
    def from_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        raise NotImplementedError.new , "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto nos permitirá crear un nuevo &lt;code&gt;JsonConsumptionsProvider&lt;/code&gt;, por ejemplo, e incluirlo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize

    end
    def from_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        if extension == ".json"
            provider = JsonConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        raise NotImplementedError.new , "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero no nos adelantemos. Primero necesitamos tener un proveedor y antes de eso hay que prepararse para otro comportamiento.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agregar todos los resultados
&lt;/h3&gt;

&lt;p&gt;Uno de los requisitos que nos piden es agregar todos los resultados, lo que significa que nos pasarán una lista de archivos de los que obtener datos y debemos proporcionar una salida única.&lt;/p&gt;

&lt;p&gt;Así que tenemos que dar soporte a poder indicar varios archivos en &lt;code&gt;ConsumptionAnalyzer.execute&lt;/code&gt; y en &lt;code&gt;ConsumptionsProvider.from_file&lt;/code&gt;. Además, en este último, tenemos que obtener los datos y agregarlos antes de entregarlos.&lt;/p&gt;

&lt;p&gt;Vamos por partes. Una forma fácil de permitir varios archivos en &lt;code&gt;ConsumptionsProvider.from_file&lt;/code&gt; es cambiar el parámetro &lt;code&gt;file_name&lt;/code&gt; con splat operator. De ese modo, podemos pasarle una lista de nombres de archivo y se comportará como un array.&lt;/p&gt;

&lt;p&gt;Para eso, nos viene bien extraer el procesamiento de cada archivo individual en un método. Es decir. Ahora estamos así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize

    end
    def from_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        raise NotImplementedError.new , "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y nos preparamos haciendo esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize

    end
    def from_file(file_name)
        read_file(file_name)
    end

    def read_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        raise NotImplementedError.new, "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este cambio, que es un &lt;em&gt;Extract method&lt;/em&gt;, no afecta al comportamiento actual. Ahora vamos con &lt;code&gt;from_file&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider

    def from_file(*file_names)
        data = []
        file_names.each do |file_name|
            data = read_file(file_name)
        end
        data
    end

    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora, &lt;code&gt;from_file&lt;/code&gt; acepta cualquier número de parámetros y los empaqueta como un array. Sencillamente, recorremos el array de nombres de archivo y vamos leyendo cada uno. Este cambio es temporal, porque aún no hemos cambiado el Analizador para dar soporte a múltiples archivos.&lt;/p&gt;

&lt;p&gt;En este ejemplo de código hemos asumido que solo se va a pasar un archivo, pero lo más adecuado sería hacer lo siguiente: Añadir cada conjunto de datos que leemos al array que vamos a devolver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def from_file(*file_names)
        data = []
        file_names.each do |file_name|
            data.push(*read_file(file_name))
        end
        data
    end

    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cambiar el método &lt;code&gt;execute&lt;/code&gt; de &lt;code&gt;ConsumptionAnalyzer&lt;/code&gt; va a ser un poco más complicado. En este caso, el operador &lt;em&gt;splat&lt;/em&gt; requiere que cambiemos la signatura del método, puesto que este operador solo puede usarse en el último parámetro. Por esa razón, tendríamos que invertir el orden de los parámetros.&lt;/p&gt;

&lt;p&gt;En lenguajes como Ruby este refactor puede no estar automatizado, debido a su naturaleza dinámica. En Java, nos bastaría con hacer sobrecarga del método, añadiendo otra signatura. En otros lenguajes, con el refactor automatizado no hay mucho de qué preocuparse.&lt;/p&gt;

&lt;p&gt;Sin embargo, podemos hacer el refactor paso a paso. Dependiendo de los usos que tengamos actualmente del método puede ser más o menos complicado. De hecho, puede haber diferentes formas de hacerlo.&lt;/p&gt;

&lt;p&gt;Lo primero que hacemos es verificar los usos que tenemos ahora. Básicamente son dos. En ambos casos pasamos los dos parámetros, incluyendo deviation_factor que es opcional. Podríamos eliminar la opcionalidad, ya que no estamos haciendo uso de ella.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def execute(file_name, deviation_factor)
    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo siguiente sería introducir un parámetro extra a través del cual podamos pasar los nombres de archivo. El operador &lt;code&gt;splat&lt;/code&gt; hace que el parámetro actual como si fuese opcional, permitiéndonos no pasar nada en su lugar. De este modo, se respeta el uso que estamos haciendo actualmente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def execute(file_name, deviation_factor, *files)
    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A continuación, voy a hacer un cambio temporal que nos prepare el camino para dejar de usar el primer parámetro. En caso de que no pasemos nada en &lt;code&gt;files&lt;/code&gt;, se usa lo que venga en &lt;code&gt;file_name&lt;/code&gt; y se pasa como un array deconstruido.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def execute(file_name, deviation_factor, *files)
    files.append(file_name) if files.size == 0
    normalized = @provider.from_file(*files)

    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora podríamos ir sustituyendo los usos de este método para que pasen los nombres de archivo por &lt;code&gt;files&lt;/code&gt;, en lugar de por &lt;code&gt;file_name&lt;/code&gt;. En nuestro ejemplo son dos usos, por lo que es algo que podemos hacer de inmediato. Gracias al último cambio, sabemos que estamos usando los archivos pasados a través de &lt;code&gt;files&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = ConsumptionAnalyzer.new
a.execute('../sample.csv', deviation, '../sample.csv')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El otro uso es el test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe 'Consumer Analyzer' do
    context "Default behaviour" do
        it "should generate report" do
            a = ConsumptionAnalyzer.new
            result = capture_stdout {a.execute('sample.csv', 1.4, 'sample.csv')}
            expect(result).to match_snapshot('default_snapshot')
        end
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente, una vez comprobado que todo funciona correctamente, eliminamos el uso del parámetro &lt;code&gt;file_name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = ConsumptionAnalyzer.new
a.execute(deviation, '../sample.csv')


RSpec.describe 'Consumer Analyzer' do
    context "Default behaviour" do
        it "should generate report" do
            a = ConsumptionAnalyzer.new
            result = capture_stdout {a.execute(1.4, 'sample.csv')}
            expect(result).to match_snapshot('default_snapshot')
        end
    end
end


    def execute(deviation_factor, *files)
        normalized = @provider.from_file(*files)

        # Code removed for clarity
    end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten en cuenta que todos estos pasos los hemos dado sin que en ningún momento los tests dejasen de funcionar. En un ejemplo tan pequeño como este, podríamos haberlo completado sin tanta ceremonia, pero en un proyecto medianamente grande, proceder paso a paso te garantiza que el refactor sea seguro, dando pequeños pasos que no tienen efectos negativos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crear una nueva estrategia
&lt;/h3&gt;

&lt;p&gt;Para esta serie de artículos preparé un generador de datos aleatorios que ahora tendré que modificar para que guarde los archivos en json. Con esto, puedo generar un ejemplo sencillo. Me da igual el contenido porque solo necesito que se puedan leer los datos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "office": 1,
    "year": 2023,
    "month": 1,
    "consumption": 8379097
  },
  {
    "office": 1,
    "year": 2023,
    "month": 2,
    "consumption": 9539936
  },
  {
    "office": 1,
    "year": 2023,
    "month": 3,
    "consumption": 2025802
  },
  {
    "office": 1,
    "year": 2023,
    "month": 4,
    "consumption": 1398801
  },
  {
    "office": 1,
    "year": 2023,
    "month": 5,
    "consumption": 6572861
  },
  {
    "office": 1,
    "year": 2023,
    "month": 6,
    "consumption": 7942753
  },
  {
    "office": 1,
    "year": 2023,
    "month": 7,
    "consumption": 2569213
  },
  {
    "office": 1,
    "year": 2023,
    "month": 8,
    "consumption": 4575579
  },
  {
    "office": 1,
    "year": 2023,
    "month": 9,
    "consumption": 5742751
  },
  {
    "office": 1,
    "year": 2023,
    "month": 10,
    "consumption": 6769903
  },
  {
    "office": 1,
    "year": 2023,
    "month": 11,
    "consumption": 6564423
  },
  {
    "office": 1,
    "year": 2023,
    "month": 12,
    "consumption": 2062790
  }
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para crear este provider, haré un test leyendo de este archivo que se llama &lt;code&gt;example.json&lt;/code&gt;. Lo más correcto sería utilizar una librería como &lt;a href="https://github.com/fakefs/fakefs" rel="noopener noreferrer"&gt;FakeFS&lt;/a&gt;, que nos permite trabajar en un sistema de archivos virtual, o aplicar alguna otra idea que nos evitase tener que tocar el sistema de archivos. Pero puesto que añade una complejidad que va más allá de los objetivos de estos artículos, prefiero usar el método más sencillo.&lt;/p&gt;

&lt;p&gt;En principio sería un poco absurdo testear esto a base de conseguir leer el archivo, obtener el output en forma de array de &lt;code&gt;Consumption&lt;/code&gt; y verificar que cada uno de los objetos se ha creado bien. Así que básicamente, lo que quiero es comprobar que se leen todos los registros del archivo y que se pueblan correctamente.&lt;/p&gt;

&lt;p&gt;Este debería servir para probar el primer punto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe JsonConsumptionProvider do
    it "should read all records in file" do
        provider = JsonConsumptionProvider.new
        consumptions = provider.from_file("example.json")
        expect(consumptions.size).to eq(12)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hagamos una implementación fake, solo para probar que el test funciona:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class JsonConsumptionProvider
    def from_file(filename)
        Array.new(12, Consumption.new)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora que tenemos una línea base de comportamiento, vamos introduciendo cambios. Primero queremos leer los datos del archivo. Si podemos abrir el archivo en modo de lectura, ya tenemos un paso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class JsonConsumptionProvider
    def from_file(filename)
        f = File.new(filename, "r")
        Array.new(12, Consumption.new)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora, obtengamos los datos. Hasta aquí todo parece funcionar y data debería contener un array de hashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class JsonConsumptionProvider
    def from_file(filename)
        f = File.new(filename, "r")
        raw = f.read
        data = JSON.parse(raw)
        f.close
        Array.new(12, Consumption.new)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos a ver si son 12:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class JsonConsumptionProvider
    def from_file(filename)
        f = File.new(filename, "r")
        raw = f.read
        data = JSON.parse(raw)
        f.close
        Array.new(data.size, Consumption.new)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resulta que sí. La primera parte parece conseguida. Nuestro provider es capaz de leer datos del archivo y, aparentemente, logra leer los 12 registros. Introducimos tests para ver si los lee correctamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe JsonConsumptionProvider do
    it "should read all records in file" do
        provider = JsonConsumptionProvider.new
        consumptions = provider.from_file("example.json")
        expect(consumptions.size).to eq(12)
    end

    it "should read data in first record" do
        provider = JsonConsumptionProvider.new
        consumptions = provider.from_file("example.json")
        expected = Consumption.new
        expected.office = 1
        expected.year = 2023
        expected.month = 1
        expected.consumption = 8379097

        expect(consumptions[0]).to eq(expected)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como es de esperar, este test no va a pasar. Los datos los hemos copiado del archivo, porque el comportamiento que esperamos es que se generen los objetos &lt;code&gt;Consumption&lt;/code&gt; con esos mismos datos.&lt;/p&gt;

&lt;p&gt;Lo hacemos pasar con este código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class JsonConsumptionProvider
    def from_file(filename)
        f = File.new(filename, "r")
        raw = f.read
        data = JSON.parse(raw)
        f.close

        consumptions = []

        data.each do |h|
            c = Consumption.new
            c.office = h["office"]
            c.year = h["year"]
            c.month = h["month"]
            c.consumption = h["consumption"]
            consumptions.append(c)
        end
        consumptions
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este código es lo bastante general como para convertir correctamente todos los registros leídos. Podemos introducir otro test, pero no va a aportarnos información nueva:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe JsonConsumptionProvider do
    it "should read all records in file" do
        provider = JsonConsumptionProvider.new
        consumptions = provider.from_file("example.json")
        expect(consumptions.size).to eq(12)
    end

    it "should read data in first record" do
        provider = JsonConsumptionProvider.new
        consumptions = provider.from_file("example.json")
        expected = Consumption.new
        expected.office = 1
        expected.year = 2023
        expected.month = 1
        expected.consumption = 8379097

        expect(consumptions[0]).to eq(expected)
    end

    it "should read data in last record" do
        provider = JsonConsumptionProvider.new
        consumptions = provider.from_file("example.json")
        expected = Consumption.new
        expected.office = 1
        expected.year = 2023
        expected.month = 12
        expected.consumption = 2062790

        expect(consumptions[11]).to eq(expected)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto, ya tenemos un nuevo proveedor. Y sabemos que funciona correctamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Juntarlo todo
&lt;/h3&gt;

&lt;p&gt;Ya casi estamos listas para unir todas las piezas. Todo el trabajo que nos quedaría lo podemos hacer aquí:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    # Code removed for clarity

    def read_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        raise NotImplementedError.new, "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A primera vista, una opción posible es reproducir la estructura if para introducir el proveedor JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def read_file(file_name)
    extension = File.extname(file_name)
    if extension == ".csv"
        provider = CsvConsumptionsProvider.new
        return provider.from_file(file_name)
    end
    if extension == ".json"
        provider = JsonConsumptionsProvider.new
        return provider.from_file(file_name)
    end
    raise NotImplementedError.new, "#{extension} file support not implemented"
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto debería funcionar. De hecho, el snapshot test original sigue pasando. Lo adecuado sería introducir un nuevo test para verificar que todo funciona. En este caso, generaría un nuevo archivo de ejemplo en formato json.&lt;/p&gt;

&lt;p&gt;Este es el nuevo test, donde se puede ver como paso los dos nombres de archivo con el diferente formato de datos. Por cierto, que me ha servido para corregir algunos errores de nombres por todo el código.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe 'Consumer Analyzer' do
    # Code removed for clarity

    context "Two sources" do
        it "should generate mixed report" do
            a = ConsumptionAnalyzer.new
            result = capture_stdout {a.execute( 1.4, 'sample.csv', 'sample_2.json')}
            expect(result).to match_snapshot('two_sources')
        end
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dado que sabemos que el proveedor de Json lee correctamente los datos, por el test unitario, y que el análisis también funciona correctamente, por el primer test de &lt;em&gt;snapshot&lt;/em&gt;, podemos confiar en que el comportamiento es correcto y este test nos vale.&lt;/p&gt;

&lt;p&gt;Ya hemos desarrollado la funcionalidad deseada y, si te das cuenta, hemos pasado más trabajo refactorizando que implementando las nuevas capacidades. Podrías pensar que es un desperdicio pero ten en cuenta que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El refactor nos ha garantizado que añadir la nueva funcionalidad no iba a perjudicar el comportamiento existente&lt;/li&gt;
&lt;li&gt;El trabajo de crear el nuevo proveedor de datos ha sido muy sencillo&lt;/li&gt;
&lt;li&gt;En el futuro, será igualmente sencillo añadir soporte para nuevos formatos de archivo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pero es que incluso puede ser más sencillo si hacemos un poco de refactor a posteriori.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rematando el trabajo con otro refactoring
&lt;/h3&gt;

&lt;p&gt;Echemos un vistazo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize

    end
    def from_file(*file_names)
        data = []
        file_names.each do |file_name|
            data.push(*read_file(file_name))
        end
        data
    end

    def read_file(file_name)
        extension = File.extname(file_name)
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        if extension == ".json"
            provider = JsonConsumptionsProvider.new
            return provider.from_file(file_name)
        end
        raise NotImplementedError.new, "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si quisiera añadir soporte para un nuevo tipo de archivo, por ejemplo, un XML, tengo que crear una clase &lt;code&gt;Provider&lt;/code&gt; y modificar &lt;code&gt;ConsumptionsProvider&lt;/code&gt;. Cierto que esta modificación está bastante controlada, pero imagina no tener que tocarla para nada. Veamos otra forma de organizar este código.&lt;/p&gt;

&lt;p&gt;Empecemos por hacer un cambio en la forma en que tratamos el provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def read_file(file_name)
    extension = File.extname(file_name)
    provider = nil
    if extension == ".csv"
        provider = CsvConsumptionsProvider.new
        return provider.from_file(file_name)
    end
    if extension == ".json"
        provider = JsonConsumptionsProvider.new
        return provider.from_file(file_name)
    end
    if provider.nil?
        raise NotImplementedError.new, "#{extension} file support not implemented"
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora, podemos mover el return, que es igual en todas las ramas fuera de la estructura condicional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def read_file(file_name)
    extension = File.extname(file_name)
    provider = nil
    if extension == ".csv"
        provider = CsvConsumptionsProvider.new
    end
    if extension == ".json"
        provider = JsonConsumptionsProvider.new
    end
    if provider.nil?
        raise NotImplementedError.new, "#{extension} file support not implemented"
    end
    provider.from_file(file_name)
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al fin y al cabo, el método &lt;code&gt;read_file&lt;/code&gt; hace dos cosas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decidir qué proveedor utilizar.&lt;/li&gt;
&lt;li&gt;Ejecutar el proveedor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Así que separemos ambas responsabilidades.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def read_file(file_name)
    provider = select_provider(file_name)
    provider.from_file(file_name)
end

def select_provider(file_name)
    extension = File.extname(file_name)
    provider = nil
    if extension == ".csv"
        provider = CsvConsumptionsProvider.new
    end
    if extension == ".json"
        provider = JsonConsumptionsProvider.new
    end
    if provider.nil?
        raise NotImplementedError.new, "#{extension} file support not implemented"
    end
    provider
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El método &lt;code&gt;select_provider&lt;/code&gt; es básicamente una factoría, la cual podríamos extraer a otro objeto. Ya hemos visto el refactor &lt;em&gt;Extract class&lt;/em&gt;, por lo que te voy a mostrar el resultado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ProviderFactory
    def make_provider(file_name)
        extension = File.extname(file_name)
        provider = nil
        if extension == ".csv"
            provider = CsvConsumptionsProvider.new
        end
        if extension == ".json"
            provider = JsonConsumptionsProvider.new
        end
        if provider.nil?
            raise NotImplementedError.new, "#{extension} file support not implemented"
        end
        provider
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y aquí su uso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionsProvider
    def initialize(factory = ProviderFactory.new)
        @factory = factory
    end
    def from_file(*file_names)
        data = []
        file_names.each do |file_name|
            data.push(*read_file(file_name))
        end
        data
    end

    def read_file(file_name)
        provider = select_provider(file_name)
        provider.from_file(file_name)
    end

    def select_provider(file_name)
        @factory.make_provider(file_name)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los objetos factoría están, por decirlo así, en las fronteras del dominio, así que la vida allí es un poco más salvaje. Por ejemplo, es más tolerable modificar la factoría que modificar &lt;code&gt;ConsumptionsProvider&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Podemos replantear un poco el código de &lt;code&gt;ProviderFactory&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ProviderFactory
    def initialize
        @providers = {
          ".csv": CsvConsumptionsProvider.new,
          ".json": JsonConsumptionsProvider.new,
        }
    end

    def make_provider(file_name)
        extension = File.extname(file_name).to_sym
        unless @providers.key? extension
            raise NotImplementedError.new, "#{extension} file support not implemented"
        end
        @providers[extension]
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este cambio mantiene el mismo comportamiento del programa y simplifica enormemente su mantenimiento, ya que basta con añadir una entrada al diccionario de &lt;code&gt;@providers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Una posible mejora sería añadir un método &lt;code&gt;register(extension, provider)&lt;/code&gt;, que nos permitiría añadir nuevos proveedores sin tocar esta clase, manteniendo, o no, el soporte por defecto a los actuales &lt;code&gt;.json&lt;/code&gt; y &lt;code&gt;.csv&lt;/code&gt;. O un poco de meta-programación para añadirlos automáticamente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ProviderFactory
    def initialize
        @providers = {
          ".csv": CsvConsumptionsProvider.new,
          ".json": JsonConsumptionsProvider.new,
        }
    end

    def register(extension, provider)
        @providers[extension] = provider
    end

    def make_provider(file_name)
        extension = File.extname(file_name).to_sym
        unless @providers.key? extension
            raise NotImplementedError.new, "#{extension} file support not implemented"
        end
        @providers[extension]
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se podría utilizar así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;factory = ProviderFactory.new
factory.register(".csv", CsvConsumptionsProvider.new)
factory.register(".json", JsonConsumptionsProvider.new)

provider = ConsumptionsProvider.new(factory)

a = ConsumptionAnalyzer.new(provider)
a.execute(deviation, '../sample.csv')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para resumir, con este refactor dejamos todo preparado para que en el futuro, añadir un nuevo tipo de fuente de datos requiera solo añadir código.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;Al principio del artículo señalábamos una línea de refactor que resultó irrelevante para la feature que nos habían pedido desarrollar. Empezar a trabajar en esa línea hubiera supuesto una pérdida de tiempo, sin aportar valor.&lt;/p&gt;

&lt;p&gt;La tentación de refactorizar un código que sabemos que no está muy bien diseñado es muy fuerte. Sin embargo, en equipos de trabajo orientados a producto, el foco debe estar puesto en las mejoras y corrección de errores.&lt;/p&gt;

&lt;p&gt;Por tanto, el refactor debería estar supeditado a estas necesidades. No solo para priorizar la entrega de valor, sino para que el refactor contribuya a ella de manera efectiva.&lt;/p&gt;

&lt;p&gt;Un refactor aplicado sin contexto puede llevarnos por un camino indeseable, que haga más cara la entrega de valor porque hemos aplicado criterios a ese refactor que no se han visto confirmados por la evolución del negocio.&lt;/p&gt;

&lt;p&gt;Por su parte, son los cambios en nuestro conocimiento del negocio los que deberían guiar el refactor. Si el conocimiento que adquirimos nos apunta en una dirección, el refactor debería seguirla.&lt;/p&gt;

&lt;p&gt;Por otro lado, las acciones de refactor pueden suponer una buena parte del tiempo de desarrollo, pero asumiendo que tenemos el código protegido por tests, debería ser un tiempo de trabajo seguro que nos facilite introducir la nueva funcionalidad.&lt;/p&gt;

&lt;p&gt;El refactor a posteriori, por su parte, es una inversión para el futuro, ya que puede ahorrarnos tiempo cuando tengamos que tocar de nuevo en esa área.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/franiglesias/ruby-dojo" rel="noopener noreferrer"&gt;Puedes ver el código de ejemplo en este repositorio&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>articles</category>
      <category>goodpractices</category>
      <category>ruby</category>
      <category>refactroring</category>
    </item>
    <item>
      <title>Refactor para quienes no refactorizan 2</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Wed, 29 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/franiglesias/refactor-para-quienes-no-refactorizan-2-3deo</link>
      <guid>https://dev.to/franiglesias/refactor-para-quienes-no-refactorizan-2-3deo</guid>
      <description>&lt;p&gt;Segunda entrega de esta serie de introducción al refactoring, si es que no refactorizas. O si quieres impulsar esta práctica en tu equipo.&lt;/p&gt;

&lt;p&gt;En la entrada anterior he intentado caracterizar el refactoring como una práctica técnica, deliberada, sistemática y metódica. En esta entrega me gustaría hablar de la oportunidad de la misma.&lt;/p&gt;

&lt;p&gt;También en la entrada anterior he mencionado que en muchos equipos se habla de refactoring como una tarea específica para escribir desde otras bases un proyecto o parte de él, ya sea porque no se ha tocado en mucho tiempo, ya porque se es consciente de que su diseño no es bueno o por la razón que sea.&lt;/p&gt;

&lt;p&gt;Para ello se intenta conseguir momentos o espacios, paralizando a veces el desarrollo de nuevas prestaciones. En parte porque a lo mejor el deterioro del código es tan grande que resulta carísimo en esfuerzo, tiempo y riesgo, añadir esas prestaciones.&lt;/p&gt;

&lt;p&gt;Pero hemos dicho también que no podemos considerar refactoring esa forma de trabajar. Llámalo reescritura, rediseño o como quieres. Si implica, por así decir, parar el ritmo de entrega de valor, no es refactoring. Es otra cosa. Y no te digo que no sea necesaria, pero es otra cosa.&lt;/p&gt;

&lt;p&gt;Decíamos que el refactoring consiste en realizar cambios pequeños e inocuos en un código a fin de mejorar su diseño. Por supuesto, podrías utilizar las técnicas de refactoring para abordar esos trabajos de reescritura de proyectos. O, dicho de otra forma, podrías abordarlo como refactoring, realizando cambios pequeños a medida que los necesitas sin romper el comportamiento actual. Pero eso es otra historia.&lt;/p&gt;

&lt;p&gt;Hablemos de cuando se debería hacer realmente el refactoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  El refactoring oportunista
&lt;/h2&gt;

&lt;p&gt;Se puede hacer refactoring cuando estamos estudiando un código y observamos algo que nos hace enarcar una ceja, leer varias veces un trozo de código o necesitar un tiempo para interpretar qué está pasando ahí. Idealmente, deberíamos poder leer un código y entender qué está haciendo y cómo lo está haciendo.&lt;/p&gt;

&lt;p&gt;El refactoring oportunista ocurre cuando nos encontramos con un fragmento de código que muestra claramente un smell y corregirlo nos puede ayudar a que el código esté en mejor estado. Pero, como veremos, es importante resistir la tentación de seguir profundizando en la madriguera del conejo.&lt;/p&gt;

&lt;p&gt;El contexto habitual es estar leyendo código por la razón que sea. Imagina el refactor como una nota que añades en el margen para ayudarte en la lectura, sobre todo en la lectura futura. Pero no estás buscando cosas que arreglar en el código. Simplemente te lo encuentras.&lt;/p&gt;

&lt;p&gt;Lo malo es que, a veces, encuentras cosas graves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Un ejemplo con números mágicos
&lt;/h3&gt;

&lt;p&gt;Veamos esta línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next if consumptions.size &amp;lt; 12

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta línea significa que vamos a la siguiente fila si el array de &lt;code&gt;consumptions&lt;/code&gt; tiene menos de &lt;code&gt;12&lt;/code&gt; elementos. Pero, ¿por qué &lt;code&gt;12&lt;/code&gt;? Vale: 12 consumos por oficina en un año. De nuevo, tenemos un ejemplo del &lt;em&gt;code smell&lt;/em&gt; &lt;em&gt;número mágico&lt;/em&gt;. Una solución es darle un nombre:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This goes outside of the class
CONSUMPTIONS_A_YEAR = 12

next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por desgracia, esta línea nos indica la existencia de otros problemas más graves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿Qué pasa si una oficina tiene menos de 12 consumos en un año porque abrió sus puertas en el segundo o tercer trimestre?&lt;/li&gt;
&lt;li&gt;¿Y si el archivo de datos está desordenado?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pero eso lo trataremos más adelante. Quizá lo más interesante es que al hacer el código más legible es también más fácil identificar aquellos casos en que el código no refleja bien el conocimiento del negocio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un ejemplo con condiciones complejas
&lt;/h2&gt;

&lt;p&gt;Otro ejemplo es esta línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next unless (consumption - average).abs &amp;gt; standard_deviation * deviation_factor

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La línea se lee: pasar al siguiente consumo &lt;em&gt;a menos que&lt;/em&gt; se cumpla la condición &lt;code&gt;(consumption - average).abs &amp;gt; standard_deviation * deviation_factor&lt;/code&gt;, en cuyo caso seguimos en el bloque. Ahora bien, para entender la condición puede que tengas que pensar un ratito en qué significa.&lt;/p&gt;

&lt;p&gt;La condición establece que si la diferencia de ese consumo con la media es mayor que la desviación típica multiplicada por un cierto factor entonces debemos considerar ese consumo como un &lt;em&gt;outlier&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Las expresiones condicionales complejas no se suelen catalogar como &lt;em&gt;code smells&lt;/em&gt;, pero teniendo en cuenta que introducen una dificultad para seguir el flujo o relato del código, suele ser recomendable esconder es complejidad bajo un nombre descriptivo. Para eso, podríamos extraer toda la expresión condicional a un método de la clase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next unless consumption_is_outlier(average, consumption, deviation_factor, standard_deviation)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claro que esta solución no es mucho mejor. Un método con cuatro parámetros es también un &lt;em&gt;smell&lt;/em&gt;, especialmente si son parámetros posicionales. Un cambio de orden, puede generar un error muy difícil de depurar.&lt;/p&gt;

&lt;p&gt;Podemos probar con un refactor un poco más sencillo: introducir variable para reducir la complejidad de la expresión. El lado izquierdo representa la diferencia de consumo con la media:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(consumption - average).abs

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mientras que el lado derecho representa el límite para considerar esa diferencia como demasiado grande:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;standard_deviation * deviation_factor

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podríamos hacer esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;difference = (consumption - average).abs
boundary = standard_deviation * deviation_factor

next unless difference &amp;gt; boundary

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora la expresión condicional es mucho más clara y la línea se puede leer como: pasar al siguiente a menos que la diferencia sea mayor que el límite.&lt;/p&gt;

&lt;p&gt;Podría ser más correcto formalmente mover estos cálculos a métodos, ya que nos ahorramos las variables temporales, pero entonces volvemos a tener problemas. Fíjate lo que pasa con &lt;code&gt;difference&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next unless difference(average, consumption) &amp;gt; standard_deviation * deviation_factor

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Humm… es como un sí, pero no, ¿verdad? No ganamos mucho. Es preferible pagar el precio de tener un par de variables temporales.&lt;/p&gt;

&lt;p&gt;La lección es que, si bien podemos lograr pequeños triunfos a base de estos pequeños refactors oportunistas es importante no dejarse llevar por el entusiasmo e intentar refactorizarlo todo. Momentos adecuados no nos van a faltar, como veremos a continuación.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aislar lo que está mejor aislado
&lt;/h3&gt;

&lt;p&gt;Consideremos ahora esta línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada vez que pasamos por ella se nos enciende una pequeña alarma: seguramente algún día nos pedirán leer archivos XML, o Json, o algún otro formato. Está claro que aquí necesitaremos aplicar un patrón &lt;em&gt;Strategy&lt;/em&gt; y tener diversos adaptadores, así como devolver los datos leídos en una estructura que no sea dependiente del soporte del que se ha leído.&lt;/p&gt;

&lt;p&gt;Resiste el deseo de meterte en ese refactor hasta el fondo. ¿Qué ocurriría si nunca se da el caso de tener que leer otros formatos de archivo? Pues que habríamos perdido el tiempo programando cosas que no vamos a necesitar.&lt;/p&gt;

&lt;p&gt;Sin embargo, hay un punto de razón en esa alarma. La variable &lt;code&gt;data&lt;/code&gt; es de un tipo específico de una librería de Ruby para leer archivos CSV. Y el hecho de que la lectura del archivo esté plantada ahí, en el cuerpo del método principal resulta como poco molesto.&lt;/p&gt;

&lt;p&gt;Pero podemos aplicar un refactor tan sencillo como &lt;em&gt;Extract method&lt;/em&gt; para dejar las cosas un poquito mejor de lo que estaban:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def initialize

  end

  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4) 
    data = obtain_readings(file_name)
    # Code removed for clarity  
  end

  def obtain_readings(file_name)
    CSV.parse(File.read(file_name), headers: true, converters: :numeric)
  end

  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto ya nos quita de delante el detalle de como obtenemos las lecturas. Tener esto separado nos facilitará hacer cambios en el futuro.&lt;/p&gt;

&lt;p&gt;Otro cambio interesante sería introducir nuestros propios tipos para los datos leídos, de modo que seamos independientes del uso de una u otra librería. Pero la magnitud del cambio es demasiado grande para un refactor oportunista.&lt;/p&gt;

&lt;p&gt;De nuevo, no nos conviene comprometer más esfuerzo en profundizar en los cambios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recomendaciones sobre el refactor oportunista
&lt;/h3&gt;

&lt;p&gt;Es muy importante tener la disciplina de decir que no al refactor cuando nos aleja del objetivo que estábamos persiguiendo en primer lugar.&lt;/p&gt;

&lt;p&gt;El refactor oportunista se aplica cuando estamos leyendo código y encontramos pequeños escollos para entender lo que hace. El tipo de arreglos que podemos aplicar en ese momento tiene que ser muy limitado. Lo suficiente para resolver esa dificultad puntual que nos hacía difícil entender un fragmento en particular. Nada más.&lt;/p&gt;

&lt;h2&gt;
  
  
  El refactor preparatorio
&lt;/h2&gt;

&lt;p&gt;El siguiente momento del refactor también requiere disciplina. El refactor preparatorio se da en el contexto de una intervención en el código para añadir o modificar funcionalidades o para corregir errores.&lt;/p&gt;

&lt;p&gt;Una vez que tenemos claro qué debe hacerse, la intervención tendría tres partes separadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactor preparatorio&lt;/li&gt;
&lt;li&gt;La intervención que añade, modifica o corrige&lt;/li&gt;
&lt;li&gt;Refactor posterior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voy a intentar explicarlo.&lt;/p&gt;

&lt;p&gt;Cuando vamos a intervenir en el código puede ocurrir que nos demos cuenta de que para introducir el cambio deseado sería deseable que el código existente tuviese otro diseño. Esto es, con un diseño mejor, el cambio podría ser trivial, o podría consistir en añadir código en vez de modificar lo que hay, etc.&lt;/p&gt;

&lt;p&gt;Por tanto, deberíamos ocuparnos primero de refactorizar el código actual para tener ese mejor diseño que hemos podido visualizar. Dicho de otra manera: rediseñamos el código actual manteniendo el comportamiento que tiene ahora. Eso es el refactor preparatorio.&lt;/p&gt;

&lt;p&gt;Una vez hecho eso, aplicamos el cambio que deseábamos originalmente, que ahora será mucho más fácil y seguro.&lt;/p&gt;

&lt;p&gt;El refactor preparatorio puede ser trabajoso. Durante el tiempo de refactor no hacemos nada por añadir la funcionalidad nueva o corregir el bug que teníamos o cualesquiera que fuese el objetivo de la intervención. Todo nuestro foco es mejorar la situación del código. Por eso, no debería sorprendernos que nos ocupe bastante tiempo y esfuerzo. Pero como podríamos estar protegidas por tests el riesgo es reducido.&lt;/p&gt;

&lt;p&gt;No es nuestro caso, por cierto, así que igual deberíamos añadir un paso previo. Si no tenemos tests que protejan ese aspecto concreto, lo primero sería introducirlos de la mejor manera posible.&lt;/p&gt;

&lt;h2&gt;
  
  
  El refactor posterior
&lt;/h2&gt;

&lt;p&gt;Una vez que hemos realizado la intervención deseada en el código puede ocurrir que veamos nuevas oportunidades de refactorizar a fin de facilitar nuevas intervenciones en el futuro.&lt;/p&gt;

&lt;p&gt;Pero como hemos dicho otras veces, no se trata de imaginar todos los futuros posibles y empezar a programar cosas por si acaso algún día se usan. Se trata de asegurar que el código que dejamos se entiende bien y, en su caso, es fácil de cambiar cuando sea necesario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Veamos algunos ejemplos
&lt;/h2&gt;

&lt;p&gt;Teniendo claro que el refactor forma parte de la rutina de desarrollo, lo interesante es que sean las necesidades del negocio nos guíen a la hora de decidir qué y cuando refactorizar. Así que vamos a ver algunos ejemplos de trabajo realista con el código de nuestro analizador y de qué manera usamos los distintos momentos de refactoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analizar correctamente cada oficina
&lt;/h2&gt;

&lt;p&gt;Ahora que la gente de negocio ha estado probando el script se han dado cuenta de algunos problemas. El análisis se está haciendo por oficina y año y ellos quieren que se haga por la totalidad de consumos de una oficina a lo largo de su historia. Es más: una oficina podría no tener doce lecturas de consumo por año.&lt;/p&gt;

&lt;p&gt;Esto es lo mismo que detectamos al hacer el primer refactor oportunista, pero ahora está claro cómo se hace la colección de datos en el programa y cómo se debería estar haciendo. Podemos ver la diferencia y entender por qué se está haciendo mal, lo que es el primer paso para arreglarlo.&lt;/p&gt;

&lt;p&gt;La cuestión es que la situación actual del código no es muy adecuada para resolver el problema. En el mismo bucle coleccionamos las lecturas que vamos a analizar y con cada una de estas colecciones realizamos el análisis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def initialize

  end

  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

      average = average(consumptions)
      standard_deviation = standard_deviation(consumptions)

      consumptions.each do |consumption|
        difference = (consumption - average).abs
        boundary = standard_deviation * deviation_factor

        next unless difference &amp;gt; boundary

        outlier = Outlier.new
        outlier.office = row["office"]
        outlier.consumption = consumption
        outlier.deviation = (consumption - average) / standard_deviation

        outliers.append(outlier)
      end

      consumptions = []
    end
    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end

  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nos convendría separar ambas responsabilidades: por una parte, recorrer los datos brutos para extraer las colecciones de lecturas, y luego recorrer esas colecciones y hacer el análisis de cada una. Este sería el &lt;strong&gt;refactor preparatorio&lt;/strong&gt;. Una vez hecha esta separación, podríamos centrarnos en la forma en que se coleccionan los datos de cada oficina.&lt;/p&gt;

&lt;p&gt;El problema es que realmente no es posible hacer este refactor de manera automática o probada. Para tejer una red de seguridad necesitamos introducir algún test. Ciertamente, va a suponer un coste grande, pero eso que nos ahorramos en el futuro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Poniendo el código bajo test
&lt;/h3&gt;

&lt;p&gt;Tenemos dos dificultades para poner el código bajo test. La primera es el hecho de que leemos un CSV directamente del sistema de archivos. No es lo más complicado, ya que afortunadamente podemos especificar su nombre. Gracias a eso, podríamos preparar uno a medida para el test que queremos hacer.&lt;/p&gt;

&lt;p&gt;La segunda dificultad es el conseguir el &lt;em&gt;output&lt;/em&gt;. Ahora mismo lo estamos enviando a la consola o &lt;code&gt;stdout&lt;/code&gt;. En Ruby es posible hacer un apaño que nos permite capturar el output a &lt;code&gt;stdout&lt;/code&gt; y así poder verificarlo. Podemos usar esta función invocando el código del cual queremos capturar el output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def capture_stdout
  original = $stdout
  foo = StringIO.new
  $stdout = foo
  yield
  $stdout.string
ensure
  $stdout = original
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quedaría algo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = ConsumptionAnalyzer.new
result = capture_stdout {a.execute('../test.csv', 1.4)}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y luego podríamos verificar el resultado contra un ejemplo creado a mano. O generado por la propia aplicación. Bienvenidas al test de caracterización.&lt;/p&gt;

&lt;p&gt;Los tests de caracterización son tests que en lugar de verificar el output de una función o de un programa contra un criterio que hayamos definido previamente, generamos un output ejecutando el código. Ese output o snapshot será el criterio contra el que verificaremos en el test.&lt;/p&gt;

&lt;p&gt;Es decir: tomamos una muestra de lo que hace el código en su estado actual y usamos eso como criterio para asegurar que el refactoring que hagamos provoca cambios en él.&lt;/p&gt;

&lt;p&gt;Obviamente, una vez que empecemos a introducir nuevos comportamientos o modificarlo, tendremos que actualizar el snapshot o desecharlo en cuanto podamos usar otro tipo de tests.&lt;/p&gt;

&lt;p&gt;Existen diversas librerías en todos los lenguajes para hacer tests de snapshot. &lt;a href="https://approvaltests.com/" rel="noopener noreferrer"&gt;Approval tests&lt;/a&gt; nos proporciona varias, pero puedes encontrar alternativas fácilmente, aunque varía el grado de soporte. En este caso voy a probar &lt;a href="https://github.com/levinmr/rspec-snapshot" rel="noopener noreferrer"&gt;rspec-snapshot&lt;/a&gt;, de Mike Levin, que debería ser suficiente para nuestro propósito. Una vez instalada en el proyecto, el test queda así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# frozen_string_literal: true

require 'rspec'
require "rspec/snapshot"

require_relative '../lib/energy/consumption_analyzer'

RSpec.describe 'Consumer Analyzer' do
    context "Default behaviour" do
        it "should generate report" do
            a = ConsumptionAnalyzer.new
            result = capture_stdout {a.execute('sample.csv', 1.4)}
            expect(result).to match_snapshot('default_snapshot')
        end
    end
end

def capture_stdout
    original = $stdout
    foo = StringIO.new
    $stdout = foo
    yield
    $stdout.string
ensure
    $stdout = original
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se generará un archivo llamado &lt;code&gt;default_snapshot.snap&lt;/code&gt;, que captura el output generado. Para controlar que todo está bien, introduzco un cambio tonto en el código, como es sumar una cantidad arbitraria a los consumos, y compruebo que el test falla.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;outlier = Outlier.new
outlier.office = row["office"]
outlier.consumption = consumption + 123123
outlier.deviation = (consumption - average) / standard_deviation

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este caso, nos basta con este test. Pero es habitual que tengamos que hacer test combinatorios. Los test combinatorios nos permiten &lt;em&gt;bombardear&lt;/em&gt; la unidad bajo test con cientos de ejemplos, combinando los parámetros que tenemos que pasarle. En ese sentido, la librería ApprovalTests, nos aporta una forma fácil de conseguirlo. &lt;a href="https://dev.to/approval_testing/"&gt;Puedes leer más sobre eso en este otro artículo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Elimino ese cambio y ya estamos listas para empezar a trabajar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cambio paralelo al rescate
&lt;/h3&gt;

&lt;p&gt;De momento, vamos a seguir coleccionando los datos con el mismo criterio, pero con el código mejor organizado. Después, cambiaremos el criterio.&lt;/p&gt;

&lt;p&gt;Para evitar que algo se rompa por el camino, vamos a usar una estrategia de cambio paralelo. Consiste en introducir código nuevo que no se usará hasta que tengamos la certeza de que funciona. En ese momento, dejamos de usar la forma antigua y pasamos a la nueva.&lt;/p&gt;

&lt;p&gt;Mi primer paso va a ser coleccionar los consumos en un nuevo array. Lo que haré será guardarme todos los array &lt;code&gt;consumptions&lt;/code&gt; que vaya generando.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    offices = []
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR
      offices.append(consumptions)

      # Code removed for clarity
    end
    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al final debería tener un array de offices con todos esos consumptions que he obtenido. Este cambio no debería afectar al test.&lt;/p&gt;

&lt;p&gt;Ahora, voy a introducir un nuevo bucle que recorra el array de &lt;code&gt;offices&lt;/code&gt;, aunque sin hacer nada, por el momento. Esto debería permitirme mover el código marcado, separando la parte de agrupar los consumos por oficina, que es la que queremos llegar a corregir, de la parte de hacer el análisis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    offices = []
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR
      offices.append(consumptions)

      # Code to move 
      average = average(consumptions)
      standard_deviation = standard_deviation(consumptions)

      consumptions.each do |consumption|
        difference = (consumption - average).abs
        boundary = standard_deviation * deviation_factor

        next unless difference &amp;gt; boundary

        outlier = Outlier.new
        outlier.office = row["office"]
        outlier.consumption = consumption
        outlier.deviation = (consumption - average) / standard_deviation

        outliers.append(outlier)
      end
      # End of code to move

      consumptions = []
    end

    offices.each do | consumptions|

    end

    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end

  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tras cada uno de estos movimientos ejecuto el test para asegurarme de que no cambio el comportamiento.&lt;/p&gt;

&lt;p&gt;Ahora muevo el código de un bucle a otro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    offices = []
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR
      offices.append(consumptions)

      consumptions = []
    end

    offices.each do | consumptions|
        # Code to move 
        average = average(consumptions)
        standard_deviation = standard_deviation(consumptions)

        consumptions.each do |consumption|
            difference = (consumption - average).abs
            boundary = standard_deviation * deviation_factor

            next unless difference &amp;gt; boundary

            outlier = Outlier.new
            outlier.office = row["office"]
            outlier.consumption = consumption
            outlier.deviation = (consumption - average) / standard_deviation

            outliers.append(outlier)
        end
        # End of code to move     
    end

    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end

  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tengo un problema. En esta línea hago referencia a una variable interna al bucle anterior. Necesito arreglar esto antes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;outlier.office = row["office"]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Así que deshago el cambio para ver primero como dejar de depender de ese dato y poder mover el bloque.&lt;/p&gt;

&lt;p&gt;Esta forma de proceder tiene un nombre: método mi-ka-do. Brevemente explicado, consiste en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introducir un cambio que deseamos en nuestro código y ejecutarlo o ejecutar sus tests.&lt;/li&gt;
&lt;li&gt;Tomar nota del error que salga.&lt;/li&gt;
&lt;li&gt;Deshacer el cambio y asegurarnos de que todo vuelve a funcionar como antes.&lt;/li&gt;
&lt;li&gt;Modificar el código para prevenir que salga el error anterior, pero sin alterar el comportamiento actual.&lt;/li&gt;
&lt;li&gt;Introducir de nuevo el cambio que queríamos.&lt;/li&gt;
&lt;li&gt;Si todo va bien, hemos terminado.&lt;/li&gt;
&lt;li&gt;Si aparece otro error, deshacemos el cambio y volvemos a realizar el proceso.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Me doy cuenta de que podría solventar el problema si en vez de un array utilizo un hash o diccionario, guardando los identificadores de la oficina como claves. Luego solo tendría que cambiar el segundo bucle para que tenga en cuenta la clave.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    offices = {}
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

      office_id = row["office"]
      offices[office_id] = consumptions

      # Code to move 

      average = average(consumptions)
      standard_deviation = standard_deviation(consumptions)

      consumptions.each do |consumption|
        difference = (consumption - average).abs
        boundary = standard_deviation * deviation_factor

        next unless difference &amp;gt; boundary

        outlier = Outlier.new
        outlier.office = office_id
        outlier.consumption = consumption
        outlier.deviation = (consumption - average) / standard_deviation

        outliers.append(outlier)
      end

      # End of code to move     
      consumptions = []
    end

    offices.each do | office_id, consumptions|

    end

    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end

  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez hecha esta modificación, intentamos el cambio de nuevo. Pero falla. La intención del cambio es correcta, pero dado que limitamos los conjuntos de consumos a los de un año (12 consumos), los datos se van machacando y el resultado es incorrecto.&lt;/p&gt;

&lt;p&gt;La clave del hash o diccionario tendría que contemplar esto, asi que la cambiamos para que nos proporcione la resolución necesaria:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer

  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    offices = {}
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

      office_id = "#{row["office"]}-#{row["year"]}"
      offices[office_id] = consumptions

      # Code to move 

      average = average(consumptions)
      standard_deviation = standard_deviation(consumptions)

      consumptions.each do |consumption|
        difference = (consumption - average).abs
        boundary = standard_deviation * deviation_factor

        next unless difference &amp;gt; boundary

        outlier = Outlier.new
        outlier.office = office_id.split('-')[0].to_i
        outlier.consumption = consumption
        outlier.deviation = (consumption - average) / standard_deviation

        outliers.append(outlier)
      end
      # End of code to move
      consumptions = []
    end

    offices.each do | office_id, consumptions|

    end

    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end
  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí se genera el id&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;office_id = "#{row["office"]}-#{row["year"]}"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y aquí se recupera el id “real” de la oficina&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;outlier.office = office_id.split('-')[0].to_i

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aplicamos el cambio y movemos el bloque al otro bucle. Esta vez, los tests pasan, confirmando que el cambio no altera el comportamiento actual. Y, además, durante el proceso hemos podido aprender algo sobre la solución de nuestro problema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer

  CONSUMPTIONS_A_YEAR = 12

  def execute(file_name, deviation_factor = 1.4)

    data = obtain_readings(file_name)
    consumptions = []
    outliers = []
    offices = {}
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

      office_id = "#{row["office"]}-#{row["year"]}"
      offices[office_id] = consumptions

      consumptions = []
    end

    offices.each do | office_id, consumptions|
        # Code to move 

        average = average(consumptions)
        standard_deviation = standard_deviation(consumptions)

        consumptions.each do |consumption|
            difference = (consumption - average).abs
            boundary = standard_deviation * deviation_factor

            next unless difference &amp;gt; boundary

            outlier = Outlier.new
            outlier.office = office_id.split('-')[0].to_i
            outlier.consumption = consumption
            outlier.deviation = (consumption - average) / standard_deviation

            outliers.append(outlier)
        end
        # End of code to move
    end

    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end
  # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora sería el momento adecuado de terminar este refactor con otro que nos ayude a limpiar un poco el código y separar los intereses de cada parte, aislando lo que queremos corregir. El paso lógico a continuación es separar las distintas fases del proceso en métodos con un nombre significativo. Ahora el método &lt;code&gt;execute&lt;/code&gt; describe bastante mejor lo que hace, y tenemos separadas las distintas partes o fases del proceso. Esto suena a un refactor &lt;em&gt;Split phase&lt;/em&gt;, que consiste básicamente en identificar y separar las fases de un proceso.&lt;/p&gt;

&lt;p&gt;De hecho, parece que nos pide hacer algo con toda la zona de generar el &lt;em&gt;output&lt;/em&gt;, aunque solo fuese por mantener la simetría.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
    def initialize

    end

    CONSUMPTIONS_A_YEAR = 12

    def execute(file_name, deviation_factor = 1.4)
        data = obtain_readings(file_name)
        offices = offices(data)
        outliers = outliers(deviation_factor, offices)
        puts outliers
        puts "Data sample #{data.size} rows"
        puts "Found #{outliers.size} outliers"
        puts "Found #{outliers.size / 300} per office"
    end

    def outliers(deviation_factor, offices)
        outliers = []
        offices.each do |office_id, consumptions|
            average = average(consumptions)
            standard_deviation = standard_deviation(consumptions)

            consumptions.each do |consumption|
                difference = (consumption - average).abs
                boundary = standard_deviation * deviation_factor

                next unless difference &amp;gt; boundary

                outlier = Outlier.new
                outlier.office = office_id.split('-')[0].to_i
                outlier.consumption = consumption
                outlier.deviation = (consumption - average) / standard_deviation

                outliers.append(outlier)
            end
        end
        outliers
    end

    def offices(data)
        offices = {}
        consumptions = []
        data.each do |row|
            consumptions.append(row["consumption"])

            next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

            office_id = "#{row["office"]}-#{row["year"]}"
            offices[office_id] = consumptions
            consumptions = []
        end
        offices
    end

    # Removed code for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Todavía nos queda un refactor
&lt;/h3&gt;

&lt;p&gt;Pero debemos centrarnos en la tarea que veníamos a realizar: lo que queremos es recopilar los datos por oficinas. Por un lado, tenemos la ventaja de haber aislado esa fase del proceso.&lt;/p&gt;

&lt;p&gt;Pero, por otro, todavía arrastramos las consecuencias de que &lt;code&gt;data&lt;/code&gt; es un objeto muy acoplado al tipo de archivo CSV que estamos usando como fuente de datos. La consecuencia es que el método &lt;code&gt;offices&lt;/code&gt; está también acoplado a ese formato concreto de archivo. En realidad, &lt;code&gt;offices&lt;/code&gt; siempre va a estar acoplado al tipo de datos que produzca &lt;code&gt;obtain_readings&lt;/code&gt;, que es de donde viene &lt;code&gt;data&lt;/code&gt;. A este tipo de acoplamiento se le llama &lt;em&gt;Connascence&lt;/em&gt; y hace referencia al grado en que dos componentes del sistema comparten un conocimiento que obligaría a uno de ellos a cambiar si el otro lo hace.&lt;/p&gt;

&lt;p&gt;El problema es que el tipo de dato (CSV::Table y CSV::Row) está definido por una dependencia externa a nuestro código (la librería CSV). Si el tipo de datos es definido por nosotras, ese acoplamiento es menos grave. Se trataría de un principio similar al de Inversión de Dependencias.&lt;/p&gt;

&lt;p&gt;Sería preferible que &lt;code&gt;offices&lt;/code&gt; recibiera los datos con una estructura diferente así que antes de empezar a cambiar su comportamiento, sería importante refactorizar para que &lt;code&gt;obtain_readings&lt;/code&gt; produzca esa estructura y &lt;code&gt;offices&lt;/code&gt; la consuma.&lt;/p&gt;

&lt;p&gt;¿Cómo hacer este refactoring sin tener problemas? Creo que lo primero sería definir la estructura de datos básica que queremos obtener. Esta estructura puede basarse en la que tenemos en el archivo CSV, pero podría ser diferente si nos va mejor o pensamos que no necesitamos todos los datos. Si en el futuro añadimos otras fuentes de datos ya las adaptaremos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Consumption = Struct.new(:office, :year, :date, :consumption)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El segundo paso será normalizar los datos obtenidos del archivo CSV, recorriendo el array y generando uno nuevo con los datos convertidos. Para eso, introducimos un método con el que obtenemos un array con los datos de consumo normalizados a una estructura de datos definida por nosotras.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def normalize(data)
    data.map do |row|
        Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí podrían plantearse cuestiones de performance, pero sería un momento prematuro para la optimización. Es mejor perder un poco ahora, conseguir un código mejor organizado y buscar luego las optimizaciones.&lt;/p&gt;

&lt;p&gt;Finalmente, tendremos que pasarle los datos con la nueva estructura a &lt;code&gt;offices&lt;/code&gt;, que es la parte interesada. Ya que este caso es bastante trivial podemos usar el método mi-ka-do: hacemos el cambio y si todo va bien, ya nos vale. En caso de ocurrir un error, deshacemos y lo volvemos a intentar. Y todo ha ido bien a la primera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
    CONSUMPTIONS_A_YEAR = 12

    def execute(file_name, deviation_factor = 1.4)
        data = obtain_readings(file_name)
        normalized = normalize(data)
        offices = offices(normalized)
        outliers = outliers(deviation_factor, offices)

        puts outliers
        puts "Data sample #{data.size} rows"
        puts "Found #{outliers.size} outliers"
        puts "Found #{outliers.size / 300} per office"
    end

    def normalize(data)
        data.map do |row|
            Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
        end
    end

    # Code removed for clarity

    def offices(data)
        offices = {}
        consumptions = []
        data.each do |row|
            consumptions.append(row.consumption)

            next if consumptions.size &amp;lt; CONSUMPTIONS_A_YEAR

            office_id = "#{row.office}-#{row.year}"
            offices[office_id] = consumptions
            consumptions = []
        end
        offices
    end

    # Code removed for clarity
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Por fin, introducir la funcionalidad nueva
&lt;/h3&gt;

&lt;p&gt;Consolidamos el cambio con un nuevo commit y ahora es cuando podemos empezar a trabajar en los cambios que nos han pedido, que se pueden resumir en cambiar la forma en que recolectamos los datos de las oficinas, y no separarlas por años.&lt;/p&gt;

&lt;p&gt;Esto nos va a plantear un par de problemas. Tenemos el test de caracterización, el cual habíamos decidido introducir para protegernos durante el refactor y no cambiar el comportamiento de forma accidental.&lt;/p&gt;

&lt;p&gt;Ahora queremos lo contrario, o sea, cambiar el comportamiento. Es más que posible que el resultado del análisis sea diferente cuando hagamos los cambios, por lo que tendríamos que verificar que el cambio funciona bien de otra forma. Y el test tal como está actualmente ya no nos serviría. Por una parte, podríamos prescindir de él. Por otra, podemos dejarlo en espera hasta realizar el cambio de algoritmo y generar el snapshot del nuevo comportamiento.&lt;/p&gt;

&lt;p&gt;Pero para desarrollar este nuevo comportamiento, nos vendría bien hacer test unitarios que verifiquen que la selección de oficinas es correcta. Lo bueno, es que los refactors que hemos hecho nos han llevado a una situación en la que esto es posible, ya que hemos aislado ese proceso en un método de la clase cuyo input es fácil de manipular porque no es más que un array de objetos que hemos definido nosotras.&lt;/p&gt;

&lt;p&gt;Hay un problemilla. Este método debería ser privado y, por tanto, no deberíamos testearlo directamente. Sin embargo, venimos de una situación mucho peor así que es preferible ignorar esto de momento, avanzar en la funcionalidad y ver si en el futuro podemos llegar a un mejor diseño.&lt;/p&gt;

&lt;p&gt;Empecemos con los tests. Para empezar, voy a verificar que si solo tengo datos de una oficina, incluso de distintos años o meses, se agrupan todos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe ConsumptionAnalyzer do
    it "should aggregate same office data" do
        data = [
          Consumption.new(1, 2021, 1, 123456),
          Consumption.new(1, 2023, 1, 134143),
          Consumption.new(1, 2023, 2, 542543),
          Consumption.new(1, 2021, 2, 123454),
          Consumption.new(1, 2021, 3, 123345),
          Consumption.new(1, 2022, 5, 534542),
          Consumption.new(1, 2022, 6, 534542),
          Consumption.new(1, 2022, 7, 534542),
          Consumption.new(1, 2022, 8, 534542),
          Consumption.new(1, 2022, 9, 534542),
          Consumption.new(1, 2022, 10, 534542),
          Consumption.new(1, 2022, 11, 534542),
        ]

        analyzer = ConsumptionAnalyzer.new

        offices = analyzer.offices(data)

        expect(offices.size).to eq(1)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este test no sirve de mucho porque pasa tal cual con el código actual. Así que vamos a hacer un test que sí debería fallar. La opción más fácil es poner menos de 12 consumos. Ahora mismo, la recolección se hace literalmente por docenas, pero en la especificación de la tarea, nos debería haber quedado claro que se quiere permitir que el número de datos de cada oficina no tenga que coincidir con el número de meses del año.&lt;/p&gt;

&lt;p&gt;Así que, si tenemos una muestra de 3 ó 4 ejemplos ya nos valdría para provocar el fallo del test. De hecho, con un solo ejemplo nos llegaría para empezar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe ConsumptionAnalyzer do
    it "should aggregate same office data" do
        data = [
          Consumption.new(1, 2021, 1, 123456),
        ]

        analyzer = ConsumptionAnalyzer.new

        offices = analyzer.offices(data)

        expect(offices.size).to eq(1)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El test falla diciendo que no se ha recopilado nada en offices.&lt;/p&gt;

&lt;p&gt;Hagamos un cambio un poco drástico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    consumptions = []
    data.each do |row|
        consumptions.append(row.consumption)
        office_id = "#{row.office}-#{row.year}"
        offices[office_id] = consumptions
        consumptions = []
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con este cambio, el test pasará. Claro que ahora el resultado tiene pinta de que puede ser un poco caos. Pero vamos paso a paso.&lt;/p&gt;

&lt;p&gt;Ahora que me fijo en el código, me interesa cambiar la clave &lt;code&gt;office_id&lt;/code&gt;, que debería ser solo el número de oficina contenido en &lt;code&gt;Consumpion.office&lt;/code&gt;. Hagamos otro test para verificarlo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe ConsumptionAnalyzer do
    it "should aggregate same office data" do
        data = [
          Consumption.new(1, 2021, 1, 123456),
        ]

        analyzer = ConsumptionAnalyzer.new
        offices = analyzer.offices(data)
        expect(offices.size).to eq(1)
    end

    it "should identify office by its number" do
        data = [
          Consumption.new(1, 2021, 1, 123456),
        ]

        analyzer = ConsumptionAnalyzer.new
        offices = analyzer.offices(data)
        expect(offices.key? "1").to be_truthy
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y este es el cambio que necesitamos para que el test pase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    consumptions = []
    data.each do |row|
        consumptions.append(row.consumption)
        office_id = "#{row.office}"
        offices[office_id] = consumptions
        consumptions = []
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos a ver ahora como discriminar entre oficinas. O si el sistema es capaz de discriminar entre ellas. Si tenemos datos de varias oficinas, deberíamos tener eso en el resultado, podemos empezar por tan solo dos. De paso, nos aseguramos de tener ambas claves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe ConsumptionAnalyzer do

    # Code removed for clarity

    it "should separate data from different offices" do
        data = [
          Consumption.new(1, 2021, 1, 123456),
          Consumption.new(2, 2022, 4, 154325),
        ]

        analyzer = ConsumptionAnalyzer.new
        offices = analyzer.offices(data)
        expect(offices.size).to eq(2)
        expect(offices.key? "1").to be_truthy
        expect(offices.key? "2").to be_truthy
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora necesitaríamos saber que se añaden todas las lecturas de la misma oficina:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RSpec.describe ConsumptionAnalyzer do

    # Code removed for clarity

    it "should aggregate all data of an office" do
        data = [
          Consumption.new(1, 2021, 1, 123456),
          Consumption.new(2, 2022, 4, 154325),
          Consumption.new(1, 2021, 3, 173412),
          Consumption.new(1, 2021, 7, 109324),
        ]

        analyzer = ConsumptionAnalyzer.new
        offices = analyzer.offices(data)
        expect(offices["1"].size).to eq(3)
        expect(offices["1"][0]).to be(123456)
        expect(offices["1"][1]).to be(173412)
        expect(offices["1"][2]).to be(109324)
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este test no pasa. Nos dice que falla porque la oficina “1” solo tiene una entrada y nosotros esperamos 3.&lt;/p&gt;

&lt;p&gt;Si estudiamos el código, vemos que el problema puede ser que estamos acumulando consumos en el array consumptions, que es vaciado cada vez en el bucle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    consumptions = []
    data.each do |row|
        consumptions.append(row.consumption)
        office_id = "#{row.office}"
        offices[office_id] = consumptions
        consumptions = []
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En su lugar, deberíamos coleccionar esos datos directamente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    data.each do |row|
        office_id = "#{row.office}"
        offices[office_id].append(row.consumption)
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero esto tiene otro problema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     NoMethodError:
       undefined method `append' for nil:NilClass

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Necesitamos inicializar un array si la clave no existe todavía en el hash de oficinas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    data.each do |row|
        office_id = "#{row.office}"
        if !offices.key? office_id
            offices[office_id] = []
        end
        offices[office_id].append(row.consumption)
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y ahora el test pasa. En principio, con esto debería ser suficiente para tener lista la funcionalidad. Sin embargo, hay dos detalles que revisar.&lt;/p&gt;

&lt;p&gt;El primero es que hay un cambio que podría afectar a la función &lt;code&gt;outliers&lt;/code&gt;. Es otro caso de &lt;em&gt;connascence&lt;/em&gt;: ambas funciones tienen que saber como se &lt;em&gt;forma&lt;/em&gt; una clave. Si lo cambiamos en una, tenemos que cambiarlo en la otra. Voy a hacer el cambio sin más, porque es trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;outlier.office = office_id.split('-')[0].to_i

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pasaría a ser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;outlier.office = office_id

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Da la casualidad de que funciona igualmente sin cambios. Pero no deberíamos fiarnos de las casualidades y, menos aún, dejar código que puede llevar a confusión.&lt;/p&gt;

&lt;p&gt;El otro detalle es una preferencia personal: Ahora solo guardamos el valor del consumo, pero ¿por qué no guardar el objeto entero? Sería una idea interesante, pero implica un montón de cambios en &lt;code&gt;outliers&lt;/code&gt;. Esta idea podría ser el objetivo de refactor posterior, pero puede que sea demasiado ambiciosa para este momento.&lt;/p&gt;

&lt;p&gt;Nos queda regenerar el test de caracterización. Si lo tiramos vemos que da resultados distintos. Al acumular todas las medidas de cada oficina en una sola colección los índices estadísticos cambian y se detectan &lt;em&gt;outliers&lt;/em&gt; diferentes.&lt;/p&gt;

&lt;p&gt;Aprovecho para hacer un pequeño cambio en el resumen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;puts outliers
puts "Data sample #{data.size} rows"
puts "Found #{outliers.size} outliers"
puts "Found #{outliers.size / offices.size} per office"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El test de caracterización se puede regenerar simplemente borrando el snapshot existente. Al volver a ejecutar el test, se creará con los nuevos resultados.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data sample 18000 rows
Found 3051 outliers
Found 10 per office

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Refactor posterior y entrega
&lt;/h3&gt;

&lt;p&gt;Ahora podemos realizar arreglos que dejen el código en mejor estado.&lt;/p&gt;

&lt;p&gt;En Ruby existe la estructura &lt;code&gt;unless&lt;/code&gt;, que nos permite invertir ciertas condicionales y dejarlas más legibles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    data.each do |row|
        office_id = "#{row.office}"
        unless offices.key? office_id
            offices[office_id] = []
        end
        offices[office_id].append(row.consumption)
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O incluso usarlas como modificadores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def offices(data)
    offices = {}
    data.each do |row|
        office_id = "#{row.office}"
        offices[office_id] = [] unless offices.key? office_id
        offices[office_id].append(row.consumption)
    end
    offices
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los métodos &lt;code&gt;obtain_readings&lt;/code&gt; y &lt;code&gt;normalize&lt;/code&gt; están fuertemente acoplados. Tanto es así que sería mejor unificarlos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def obtain_readings(file_name)
    CSV.parse(File.read(file_name), headers: true, converters: :numeric)
end

def normalize(data)
    data.map do |row|
        Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En principio, puede parecer que se podría simplemente encapsular ambas en un único método, pero resulta que usamos &lt;code&gt;data&lt;/code&gt; un poco más abajo para reportar el tamaño de la muestra de datos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def execute(file_name, deviation_factor = 1.4)
    data = obtain_readings(file_name)
    normalized = normalize(data)
    offices = offices(normalized)
    outliers = outliers(deviation_factor, offices)

    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / offices.size} per office"
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin embargo, eso podría obtener de &lt;code&gt;normalized&lt;/code&gt;, que tiene el mismo número de filas. Una vez arreglado eso, la extracción del método queda así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def execute(file_name, deviation_factor = 1.4)
    normalized = obtain_consumptions(file_name)
    offices = offices(normalized)
    outliers = outliers(deviation_factor, offices)

    puts outliers
    puts "Data sample #{normalized.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / offices.size} per office"
end

def obtain_consumptions(file_name)
    data = obtain_readings(file_name)
    normalize(data)
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo que ocurre es que tanto &lt;code&gt;obtain_readings&lt;/code&gt; como &lt;code&gt;normalize&lt;/code&gt; son métodos que tengo mayor interés en tener aislados. Así que pienso que sería una buena idea aplicar el refactoring &lt;em&gt;Inline function&lt;/em&gt; que, básicamente consiste en reemplazar una llamada a una función por el cuerpo de esa función. Justo lo contrario de &lt;em&gt;Extract function&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def obtain_consumptions(file_name)
    data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)
    data.map do |row|
        Consumption.new(row["office"], row["year"], row["month"], row["consumption"])
    end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto nos facilitará dos cosas en el futuro: optimizar la lectura del archivo y dar soporte a otros tipos de archivos o proveedores de datos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin, por ahora
&lt;/h2&gt;

&lt;p&gt;Aunque veo algunas posibilidades de refactoring más, voy a dejar el artículo en este punto. Mi objetivo era mostrar cómo se integra el refactoring en el trabajo del día a día, cuando estamos creando un producto o servicio basado en software y queremos priorizar la entrega de nuevas funcionalidades, pero también la mejora de la calidad del código y el testing.&lt;/p&gt;

&lt;p&gt;Nos vendría bien empezar a estructurar el código en archivos, etc. Pero de momento, no hemos tenido esa necesidad.&lt;/p&gt;

&lt;p&gt;Mi plan es hacer algunas entregas más en las que, basándonos en posibles peticiones de negocio para iterar el producto, seguiremos añadiendo funcionalidad y refactorizando para que hacerlo sea más fácil y seguro cada vez.&lt;/p&gt;

</description>
      <category>articles</category>
      <category>goodpractices</category>
      <category>refactoring</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Refactoring para quienes no refactorizan</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Sun, 26 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/franiglesias/refactoring-para-quienes-no-refactorizan-55oa</link>
      <guid>https://dev.to/franiglesias/refactoring-para-quienes-no-refactorizan-55oa</guid>
      <description>&lt;p&gt;Me he dado cuenta de que no había escrito nunca un artículo sobre &lt;em&gt;refactoring&lt;/em&gt; dirigido a personas que no saben lo que es, o que no lo entienden correctamente.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactoring&lt;/em&gt; es una práctica de ingeniería de software que consiste en modificar un código de tal forma que no se altera lo que hace. El objetivo es conseguir que ese código tenga un mejor diseño, que se entienda mejor cómo funciona y que sea más barato modificar su comportamiento o añadir funcionalidades en el futuro.&lt;/p&gt;

&lt;p&gt;Como no se modifica el comportamiento, el refactoring puede desplegarse a producción, incluso aunque se trate de un cambio muy pequeño. Estos cambios pequeños, a medida que se van acumulando, consiguen transformar una base de código con defectos de diseño en otra más fácil de comprender, mantener y modificar.&lt;/p&gt;

&lt;p&gt;El beneficio buscado con el refactoring es económico, no es una supuesta mejora estética y subjetiva del código. El refactoring persigue que la evolución futura de un código sea lo más barata posible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manteniendo mínimo el riesgo asociado al cambio al introducir cambios frecuentes, pequeños e inocuos y así reducir o minimizar la necesidad de introducir grandes cambios de código en producción cuando llega el momento de añadir, modificar o corregir funcionalidad.&lt;/li&gt;
&lt;li&gt;Habilitando puntos de cambio en el código que nos permitan añadir funcionalidad o modificar la existente con el mínimo esfuerzo y tiempo posible.&lt;/li&gt;
&lt;li&gt;Reflejando nuestro conocimiento de negocio en el código de la manera más actualizada y fiel, lo que facilita el &lt;em&gt;on boarding&lt;/em&gt; de nuevas desarrolladoras, la introducción de nuevas prestaciones e incluso cambios grandes en la arquitectura.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La idea básica es que un esfuerzo de refactoring pequeño, pero constante, puede facilitarnos grandes cambios en el futuro. Es como cuando tenemos un jardín: si trabajamos unos minutos todos los días en él no nos encontraremos teniendo que hacer grandes esfuerzos para limpiarlo cuando pasado el tiempo vemos que se ha convertido en una jungla. A su vez, el tenerlo ordenado y limpio, nos facilitará introducir plantas nuevas o hacer reformas de su diseño.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendiendo el &lt;em&gt;refactoring&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Cuando escribimos un código por primera vez, especialmente si tenemos poca experiencia, lo más seguro es que no consigamos hacerlo de la mejor forma posible. Aunque funcione correctamente, puede que resulte difícil entender cómo hace lo que hace, o que si necesitamos hacer alguna modificación nos cueste mucho trabajo. Puede que incluso, al principio cualquier cambio genere errores inesperados.&lt;/p&gt;

&lt;p&gt;El &lt;em&gt;refactoring&lt;/em&gt; es la práctica en la que aplicamos diversas técnicas para conseguir ese cambio a mejor del código, sin que deje de hacer lo que estaba haciendo hasta ahora, evitando errores que hagan que el programa deje de funcionar o genere resultados incorrectos. El &lt;em&gt;refactoring&lt;/em&gt; consiste en realizar pequeños cambios inocuos de tal forma que el programa siempre pueda funcionar, incluso poniendo estos cambios uno a uno en producción.&lt;/p&gt;

&lt;p&gt;No es fácil encontrar una analogía de la práctica de refactoring en otras disciplinas. En ingeniería es habitual crear modelos o prototipos para validar hipótesis de diseño, pero también para refinar detalles. Sin embargo, esto no se hace con el producto final. En el software, en cambio, refactorizamos sobre un código que, de hecho, está en producción.&lt;/p&gt;

&lt;p&gt;La mejor analogía que me viene a la mente es la de la escritura. Las escritoras revisan constantemente el texto para encontrar formas mejores de organizarlo, utilizar un vocabulario que exprese mejor la idea, mueven palabras, frases o párrafos para estructurar mejor el discurso y, en general, manipulan el texto hasta lograr que cumpla el fin deseado: informar, conmover, divertir, denunciar, movilizar, entretener…&lt;/p&gt;

&lt;p&gt;Personalmente, el &lt;em&gt;refactoring&lt;/em&gt; es un proceso que me resulta muy familiar, pues mucho antes de profundizar en el desarrollo de software, tenía experiencia en la escritura. Por tanto, la idea de revisar el código de forma contínua a fin de lograr que expresase una idea de la mejor manera me resultaba natural y casi automática. En mi cabeza no existía la posibilidad de escribir el código correcto a la primera, sino que la versión inicial siempre sería una especie de boceto o borrador que iría puliendo a medida que aumentaba mi entendimiento de la tarea.&lt;/p&gt;

&lt;p&gt;Con el tiempo descubrí el libro fundacional de M. Fowler, &lt;em&gt;Refactoring&lt;/em&gt;, que me ayudó a entender lo que estaba haciendo como una práctica intencional y metódica. Hasta entonces, podría decirse que mi proceso de refactoring era intuitivo. En la primera edición del libro, de hecho, hay algunas colaboraciones de otros autores que reflejan también la novedad que suponía abordar esta práctica de manera sistemática en el momento de su publicación. Aún hoy, que ya tenemos una segunda edición y decenas de recursos de otras muchas autoras, el &lt;em&gt;refactoring&lt;/em&gt; no es una práctica de la industria tan implantada, sistemática y metódica como debiera.&lt;/p&gt;

&lt;h2&gt;
  
  
  El refactoring como práctica consciente y metódica
&lt;/h2&gt;

&lt;p&gt;El refactoring no es solo el hecho de modificar partes del código con la finalidad de mejorar su diseño. Es necesario subrayar que se trata de una práctica intencional, consciente y metódica.&lt;/p&gt;

&lt;p&gt;Es intencional y consciente porque debería formar parte de la rutina de trabajo. Refactorizamos para preparar el código cuando necesitamos introducir algún cambio. Antes de intentar siquiera añadir una modificación a un algoritmo, reorganizamos el código para que la forma de introducir esa modificación esa sencilla. Es un poco como cuando una cocinera corta y prepara todos los ingredientes antes de empezar a elaborar un plato. Al tener a mano todo lo necesario, el proceso de cocinado resulta sencillo y la cocinera puede centrarse en controlar los tiempos, punto de cocción y sazonado. Si tuviese que picar una verdura mientras se sofríe la cebolla, es posible que esta se pase y que el resultado sepa a quemado.&lt;/p&gt;

&lt;p&gt;La práctica es metódica porque no se refactoriza de cualquier manera. Existen técnicas y mecánicas específicas para refactorizar. De hecho, algunas están lo bastante bien definidas como para que se puedan automatizar. De este modo, los mejores entornos de desarrollo nos permiten ejecutar estas transformaciones de manera automática, lo que garantiza que se realizan sin errores y sin afectar al funcionamiento del código transformado.&lt;/p&gt;

&lt;p&gt;También es metódica porque el refactoring se puede aplicar a patrones que podemos encontrar en el código a los que llamamos &lt;em&gt;code smells&lt;/em&gt;. Los &lt;em&gt;code smells&lt;/em&gt; son ciertos síntomas que indican defectos de diseño de software que, aunque no perjudican la funcionalidad del código, sí que dificultan su comprensión y su mantenimiento. Muchos de estos &lt;em&gt;smells&lt;/em&gt; se pueden resolver aplicando técnicas de refactoring específicas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que no es refactoring
&lt;/h2&gt;

&lt;p&gt;Un cambio que modifica el comportamiento del código no es refactoring. Los cambios del refactoring se refieren a la estructura y el diseño del código, no a lo que hace. Por tanto, en un código razonablemente cubierto de tests, los cambios de refactoring no tienen efectos visibles. Nadie debería percibir que se ha realizado algún tipo de modificación.&lt;/p&gt;

&lt;p&gt;Un cambio de grandes dimensiones en el código que afecta a numerosos archivos tampoco es refactoring. A ese tipo de cambios prefiero llamarlos reescrituras. Aunque mantengan el comportamiento, desde el punto de vista de los tests o de las usuarias de la aplicación, las reescrituras suelen ser un intento de volver a escribir el mismo software partiendo de distintos principios o intentando plasmar un nuevo diseño. Este tipo de cambios requieren tiempo y, si no se hacen aplicando las tácticas adecuadas, pueden introducir mucho riesgo, bloquear el desarrollo de nuevas prestaciones, etc.&lt;/p&gt;

&lt;p&gt;De hecho, el refactoring puede conducir al mismo rediseño, pero en lugar de hacer un gran salto de fe, transforma el código aplicando pasos tan pequeños que no suponen riesgo.&lt;/p&gt;

&lt;p&gt;En general, se podría decir que no es refactor si:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cambiamos comportamiento&lt;/li&gt;
&lt;li&gt;El cambio hace que fallen los tests&lt;/li&gt;
&lt;li&gt;Se tiene que planificar&lt;/li&gt;
&lt;li&gt;Obstaculiza el desarrollo&lt;/li&gt;
&lt;li&gt;Los cambios afectan a más de dos o tres archivos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hagamos un ejemplo
&lt;/h2&gt;

&lt;p&gt;Creo que lo mejor es verlo con un ejemplo. Voy a intentar ponerme en la piel de una persona que está empezando a aprender a programar. Tal vez alguien que acaba de empezar en la industria a la que le encargan su primera tarea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejercicio y primera iteración
&lt;/h2&gt;

&lt;p&gt;Imaginemos que trabajamos para una compañía que tiene cientos de sucursales repartidas por todo el mundo y quiere optimizar su consumo de energía. En una primera fase se va a estudiar si hay consumos anormales dentro de cada una de esas oficinas, lo que podría indicar instalaciones defectuosas, mal uso, etc. El objetivo es detectar las que podrían requerir una atención urgente.&lt;/p&gt;

&lt;p&gt;El criterio para clasificar un consumo com anormal es que se encuentra más de una desviación estándar por encima o por debajo de la media de consumo de ese local. Como el estudio está en sus primeros pasos, no se descarta modificar este criterio en el futuro, ni que se apliquen otros análisis a los mismos datos.&lt;/p&gt;

&lt;p&gt;Para ello se recoge una muestra de datos en un archivo csv. Nuestro trabajo es procesar ese archivo y extraer una lista indicando todas las oficinas que presentan problemas, indicando sus consumos anómalos y el grado de desviación expresado como el número de desviaciones estándar.&lt;/p&gt;

&lt;p&gt;Para preparar el artículo he añadido un generador aleatorio que nos permite generar archivos con datos aleatorios para un número dado de oficinas y años. Para cada oficina genera valores de consumo en tres rangos diferentes.&lt;/p&gt;

&lt;p&gt;Aquí tenemos un ejemplo de los datos, para una única oficina durante un año.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;office,year,month,consumption
1,2023,1,268199
1,2023,2,99242
1,2023,3,245126
1,2023,4,88012
1,2023,5,394065
1,2023,6,579409
1,2023,7,909539
1,2023,8,891502
1,2023,9,550299
1,2023,10,423113
1,2023,11,326505
1,2023,12,172286

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En fin. La solución parece relativamente sencilla. Se trataría de leer un archivo CSV y obtener los datos, extraer los datos de cada oficina, calcular su media, su desviación típica, realizar las comparaciones adecuadas e ir guardando todos los hallazgos sospechosos.&lt;/p&gt;

&lt;p&gt;Así que empezamos por crear este código, que consigue realizar la tarea requerida:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# frozen_string_literal: true
require "csv"

Outlier = Struct.new(:office, :consumption, :deviation)

class ConsumptionAnalyzer
  def initialize

  end

  def execute(file_name)

    data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)
    consumptions = []
    outliers = []
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; 12

      average = consumptions.sum(0.0) / consumptions.size
      sum = consumptions.sum(0.0) { |element| (element - average)**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      consumptions.each do |consumption|
        next unless (consumption - average).abs &amp;gt; standard_deviation

        outlier = Outlier.new
        outlier.office = row["office"]
        outlier.consumption = consumption
        outlier.deviation = (consumption - average) / standard_deviation

        outliers.append(outlier)
      end

      consumptions = []
    end
    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end
end

a = ConsumptionAnalyzer.new
a.execute("sample.csv")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No es ninguna maravilla, pero hace su trabajo. Aunque no teniendo tests, puede ser difícil de asegurar.&lt;/p&gt;

&lt;p&gt;Así que cuando nos dan un archivo con algo más de 18.000 filas, que serían 300 oficinas durante 5 años, encuentra alrededor de 6.790 consumos problemáticos. Esto son cuatro o cinco lecturas sospechosas por oficina y año.&lt;/p&gt;

&lt;h2&gt;
  
  
  Segunda iteración, criterios algo más laxos
&lt;/h2&gt;

&lt;p&gt;Nuestro pequeño programa funciona y hace su trabajo. Ahora bien, a la vista de los resultados, parece que una sola desviación típica para marcar un consumo como sospechoso pueda darnos muchos falsos positivos, por lo que nos piden cambiar el cálculo de forma que detecte desviaciones con un factor de 1.4 o mayores.&lt;/p&gt;

&lt;p&gt;Parece un cambio fácil. Solo hay que tocar esta línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next unless (consumption - average).abs &amp;gt; standard_deviation

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y cambiarla por:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next unless (consumption - average).abs &amp;gt; standard_deviation * 1.4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este es el resultado, el cual resulta más manejable. Así que nuestra responsable de proyecto está contenta, aunque comenta que sería interesante poder modificar ese valor a medida que se van tomando medidas de ahorro en las oficinas y se puede empezar a ser más exigente. “Bueno”, pensamos, “no es más que cambiar un valor en el código”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data sample 18000 rows
Found 2310 outliers
Found 7 per office

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo que acabamos de hacer no ha sido un refactor. Hemos hecho un cambio de funcionalidad, para lo cual hemos tenido que cambiar el código del programa. Esto viola el principio de diseño de software conocido como Open/Closed: abierto para extensión y cerrado para modificación.&lt;/p&gt;

&lt;p&gt;Lo que nos dice este principio es que para hacer este cambio de funcionalidad que acabamos de realizar, sería preferible no tener que modificar el programa.&lt;/p&gt;

&lt;h2&gt;
  
  
  El primer refactor
&lt;/h2&gt;

&lt;p&gt;Tener que modificar el código para hacer que el comportamiento del software cambie siempre es un problema.&lt;/p&gt;

&lt;p&gt;Supongamos que este programa de ahorro de energía se va a aplicar por áreas geográficas o países. Cada dirección regional necesita personalizar el programa debido a diferentes razones. Por ejemplo, en países con climas más extremos es posible que haya mayores consumos por calefacción en invierno, así que requerirán un sistema un poco menos sensible con sus datos que otros.&lt;/p&gt;

&lt;p&gt;Así que si tenemos, por ejemplo cinco direcciones regionales necesitaremos cinco versiones diferentes del programa. Y eso únicamente teniendo en cuenta este pequeño aspecto. Y si fuese una aplicación web única para todos, tendrían que pedirnos que la cambiásemos para cada uso. No parece una solución ni útil ni escalable.&lt;/p&gt;

&lt;p&gt;Lo ideal sería que cada dirección regional usase el mismo programa, pero con diferente configuración. La configuración es una de las formas en que un código puede estar abierto a extensión sin tener que modificarlo, ya que puede hacerse independiente del código del programa.&lt;/p&gt;

&lt;p&gt;El problema que tenemos en este caso es que esta parte del algoritmo incluye un parámetro que está fijado en el propio código aunque, por su naturaleza, tiene sentido que su valor cambie. No es incorrecto en sí mismo, pues el código funciona, pero introduce una dificultad si necesitamos ajustarlo a otro valor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;next unless (consumption - average).abs &amp;gt; standard_deviation * 1.4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto es lo que llamamos un &lt;em&gt;code smell&lt;/em&gt;: un aspecto del código que no es incorrecto &lt;em&gt;per se&lt;/em&gt;, pero revela un problema subyacente que puede manifestarse cuando necesitamos cambiar el comportamiento de la unidad de código en que se encuentra.&lt;/p&gt;

&lt;p&gt;El &lt;em&gt;smell&lt;/em&gt; o síntoma es el hecho de que aparezca un número arbitrario en el código. Este en concreto tiene un nombre: &lt;em&gt;número mágico&lt;/em&gt; o &lt;em&gt;magic number&lt;/em&gt;. El problema inmediato es que es fácil perder la pista de su significado. Nosotras tenemos fresco lo que significa, pues acabamos de escribir el programa. Pero si otra persona tiene que ocuparse de ese cambio, puede encontrarse con dificultades para averiguar qué pinta ahí ese número.&lt;/p&gt;

&lt;p&gt;Eliminar este &lt;em&gt;code smell&lt;/em&gt; implica un cambio en el código que debe hacerse sin afectar a la funcionalidad actual. Por ejemplo, si ahora mismo el código detecta consumos inusuales mayores de 1.4 desviaciones estándar, una vez que hagamos el cambio, el programa debería detectar exactamente las mismas.&lt;/p&gt;

&lt;p&gt;Para eliminar el &lt;em&gt;smell&lt;/em&gt; tenemos varias soluciones que esencialmente consisten en darle un nombre a ese valor de forma que siempre podamos saber qué representa. Así que disponemos de tres posibles refactors que podríamos aplicar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Introducir una constante&lt;/strong&gt; : que aplicaremos cuando sepamos que ese valor no va a cambiar, al menos no en un futuro previsible. Tenemos ejemplos de constantes matemáticas y físicas, como PI, pero en nuestro negocio podrían existir otros valores constantes. No es nuestro caso porque los requisitos que nos piden es que se pueda cambiar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introducir una variable&lt;/strong&gt; : que aplicaremos cuando ese valor puede cambiar y pueda proceder de una función usada en el ámbito de nuestra pieza de código. Aunque sabemos que el valor del que estamos hablando es variable, también sabemos que lo queremos cambiar desde fuera del propio código.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introducir un parámetro&lt;/strong&gt; : que será la solución cuando queremos que el valor venga de fuera de nuestra pieza de código, por lo que su lugar lógico es convertirse en un parámetro en la signatura de nuestra función o método.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este último refactor tiene el objetivo de que podamos usar nuestro código de esta manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = ConsumptionAnalyzer.new
a.execute("sample.csv", 1.4)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por lo general, todos los refactorings tienen una mecánica específica. En algunos casos está lo bastante definida como para que se pueda automatizar. Así, muchos IDE nos proporcionan estos automatismos, de modo que simplemente tenemos que indicar el trozo de código que queremos cambiar y el editor hará el resto.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Introducir parámetro&lt;/em&gt; es uno de esos refactoring que está automatizado. Sin embargo, es muy sencillo de hacer. Añadimos el parámetro en la signatura del método &lt;code&gt;execute&lt;/code&gt; y le ponemos un valor por defecto para asegurar que podemos usar el software exactamente igual que antes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# frozen_string_literal: true
require "csv"

Outlier = Struct.new(:office, :consumption, :deviation)

class ConsumptionAnalyzer
  def initialize

  end

  def execute(file_name, deviation_factor = 1.4)

    # Removed for clarity

    data.each do |row|
      # Removed for clarity

      consumptions.each do |consumption|
        next unless (consumption - average).abs &amp;gt; standard_deviation * deviation_factor

        # Removed for clarity
      end

      consumptions = []
    end
    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es decir, así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = ConsumptionAnalyzer.new
a.execute("sample.csv")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez que comprobamos que la nueva versión funciona igual, podemos empezar a explotarla pasándole un parámetro. Para nuestro ejemplo, hemos creado una utilidad de línea de comandos a la que le podemos pasar el parámetro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env ruby
# frozen_string_literal: true

require_relative '../lib/energy/consumption_analyzer'

deviation = ARGV[0].to_f
deviation = 1.4 if deviation.zero?

a = ConsumptionAnalyzer.new
a.execute('../sample.csv', deviation)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Algunas reflexiones sobre este primer refactor
&lt;/h2&gt;

&lt;p&gt;En realidad si nuestro código funcionase bien y no hubiese ninguna necesidad de cambiarlo, no estaríamos hablando de &lt;em&gt;refactoring&lt;/em&gt;. Simplemente, el programa seguiría ahí, prestando su servicio, sin necesidad de tocarlo. Pero aunque eso es algo relativamente frecuente, también lo es que cuando introducimos un software, especialmente al principio, descubrimos limitaciones o posibilidades que nos impulsan a cambiarlo.&lt;/p&gt;

&lt;p&gt;Por supuesto, lo ideal sería que fuese barato cambiar el código, entendiendo como barato que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El tiempo y esfuerzo necesario para realizar el cambio sea el mínimo posible.&lt;/li&gt;
&lt;li&gt;El riesgo de introducir defecto sea el mínimo posible, preferentemente cero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lo que acabamos de ver es que la existencia de &lt;em&gt;code smells&lt;/em&gt; contribuye a incrementar el tiempo y el riesgo requerido para el cambio. Los motivos serían:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El code smell en sí dificulta la comprensión del código y, por tanto, hace que tardemos más en entender donde y cómo tenemos que aplicar el cambio necesario.&lt;/li&gt;
&lt;li&gt;El estado del código dificulta introducir la nueva funcionalidad porque la estructura actual no contempla la posibilidad de una forma diferente de hacer las cosas.&lt;/li&gt;
&lt;li&gt;Manipular el código implica la posibilidad de alterar su comportamiento que puede introducir errores o resultados no deseados.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como hemos podido ver, antes de poder aplicar el cambio deseado, hemos tenido que resolver el &lt;em&gt;code smell&lt;/em&gt;. Una vez reparado el código, introducir el cambio fue fácil.&lt;/p&gt;

&lt;p&gt;Una vez hecho esto, lo primero que podría venirnos a la mente es algo así como: entonces podría ser buena idea identificar los &lt;em&gt;code smells&lt;/em&gt; que haya en el código y arreglarlos. De este modo, en el futuro nos encontraremos con menos problemas para hacer cambios en el sistema.&lt;/p&gt;

&lt;p&gt;A esta propuesta no le falta razón, pero tiene algunos inconvenientes. Para empezar, muchas veces no vamos a tener tiempo de hacer eso. El negocio se mueve y es más importante introducir nuevas funcionalidades o mejoras y arreglar errores. Perfectamente, puede ocurrir que identifiquemos code smells en áreas del código que no son importantes para el negocio, ya que funcionan como es debido y no se han necesitado cambios.&lt;/p&gt;

&lt;p&gt;Por este motivo, es mucho más práctico limitar esto a las áreas del código que tenemos que tocar por necesidades del negocio. De hecho, es lo que ha ocurrido hace un momento. Nos han pedido que se pueda cambiar un cierto parámetro que originalmente estaba fijado por el código. Tuvimos que deshacer eso para ofrecer esa posibilidad. En este caso, la solución del &lt;em&gt;code smell&lt;/em&gt;, el refactoring, coincidió prácticamente con la solución del problema.&lt;/p&gt;

&lt;p&gt;Hay muchas posibles necesidades que pueden surgir en este pequeño proyecto que nos darían oportunidades para refactorizar todo el código. En algunos casos con más coste y en otro con menos. Así, se me ocurren:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Los datos podrían ser proporcionados en otros formatos: XML, Json&lt;/li&gt;
&lt;li&gt;De hecho, ahora vienen en forma de archivos, pero en otros casos podrían obtenerse consultando una API, etc.&lt;/li&gt;
&lt;li&gt;Quizá nos pidan entregar los resultados en un CSV o similar que se pueda abrir en una hoja de cálculo&lt;/li&gt;
&lt;li&gt;O quizá nos pidan incluir más datos en el reporte&lt;/li&gt;
&lt;li&gt;O incluso cambiar completamente el algoritmo, basándose en otra medida&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todos estos cambios podrían llegar a ser solicitados. Pero tampoco podemos obsesionarnos con imaginar todos los futuros posibles y anticiparlos. Como mucho, podemos asumir que llegarán cambios.&lt;/p&gt;

&lt;p&gt;Por esa misma razón, es preferible actuar de una manera reactiva: refactorizar cuando surge la necesidad o cuando tenemos la oportunidad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Donde nacen los &lt;em&gt;code smells&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Como hemos dicho más arriba, los &lt;em&gt;code smells&lt;/em&gt; son ciertos patrones del código que señalan la presencia de defectos de diseño, dificultando el cambio del software a la hora de arreglar errores o modificar las funcionalidades. En sí mismos no son errores ni provocan un mal funcionamiento del código.&lt;/p&gt;

&lt;p&gt;Pero el hecho de que aparezcan en nuestro código sería un indicador de un diseño inadecuado para el programa que tenemos entre manos y este puede venir motivado por varias causas.&lt;/p&gt;

&lt;p&gt;Existen dos fuerzas que mueven el desarrollo de software: nuestro conocimiento del dominio o negocio que nos dice qué es lo que tenemos que programar, y nuestro conocimiento técnico, que nos dice cómo tenemos que implementarlo.&lt;/p&gt;

&lt;p&gt;Nuestras carencias en cada una de ellas son las dos principales fuentes de problemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  La deuda técnica
&lt;/h3&gt;

&lt;p&gt;La deuda técnica sería provocada por nuestra falta de conocimiento del dominio o negocio en un momento dado. Esta falta de conocimiento no sería causada por desidia o desinterés, sino por la incertidumbre. Cuando ponemos un software en producción, puede que no tengamos una idea clara de como va a responder sus usuarias potenciales, puede que no conozcamos lo bastante de sus necesidades o de las soluciones que realmente necesitan.&lt;/p&gt;

&lt;p&gt;Podríamos haber supuesto que ciertos parámetros se mantendrán constantes, o partir de ciertas hipótesis sobre cómo las usuarias interactuarán con el software. Podríamos asumir que ciertos aspectos del negocio cambian lentamente o, al contrario, que lo hacen con mucha frecuencia. Y así, un largo etcétera de aspectos de los que no sabemos mucho.&lt;/p&gt;

&lt;p&gt;Por tanto, podemos empezar a desplegar el software con el objetivo de descubrir precisamente eso que no conocemos. En consecuencia el código reflejará una serie de asunciones por nuestra parte que podrían revelarse incorrectas o incompletas.&lt;/p&gt;

&lt;p&gt;A esa diferencia entre el conocimiento real del negocio y lo que está reflejado en el código es a lo que llamamos &lt;em&gt;deuda técnica&lt;/em&gt;. Cuando asumimos esa diferencia y ponemos código en producción de forma consciente, también asumimos que en algún momento tendremos que &lt;em&gt;pagar&lt;/em&gt; esa deuda, cosa que haremos refactorizando para poder introducir tanto el nuevo conocimiento que hemos desarrollado sobre el negocio como las nuevas funcionalidades.&lt;/p&gt;

&lt;p&gt;Con todo, la deuda técnica no es la principal causa de smells, ya que la deuda técnica bien manejada implica reconocer en el propio código la posibilidad de cambios en el futuro y preparar el código para que no sea muy costoso aplicarlos llegado el momento.&lt;/p&gt;

&lt;h3&gt;
  
  
  El mal diseño
&lt;/h3&gt;

&lt;p&gt;La otra fuerza que mueve el desarrollo de software es nuestra pericia técnica. Es decir, nuestra capacidad para escribir un software que refleje el conocimiento del negocio de la mejor manera posible.&lt;/p&gt;

&lt;p&gt;Si no tenemos mucha experiencia o ideas claras en el diseño de software y los principios que lo guían, lo más probable es que nuestro código presente muchos &lt;em&gt;code smells&lt;/em&gt;. Cuando tenemos experiencia, podemos prevenir algunos de los más groseros. A veces, la experiencia nos sirve para tolerar algunos defectos de diseño basándonos principios de conveniencia, como sería el caso de asumir deuda técnica: aceptar el compromiso de tener que refactorizar esto en el futuro, mientras no tengo conocimiento suficiente para hacerlo mejor.&lt;/p&gt;

&lt;p&gt;Sin embargo, una programadora novel o una con experiencia, pero con bajo interés en el diseño de software, introducirá muchos smells que, a la larga, dificultarán el progreso de ese software. Las prisas por salir a producción, o unas prácticas técnicas descuidadas, también nos llevarán a introducir muchos code smells.&lt;/p&gt;

&lt;p&gt;La consecuencia es que se crearán diseños inflexibles, acoplados a tecnologías específicas, con el código mal organizado, etc, que incrementarán el coste del desarrollo.&lt;/p&gt;

&lt;p&gt;Y esta situación empeora si carecemos de tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  La carencia de tests
&lt;/h2&gt;

&lt;p&gt;La falta de tests perjudica nuestras posibilidades de refactorizar código. Los tests nos proporcionarían la red de seguridad necesaria para hacer cambios sabiendo que en caso de alterar el comportamiento del programa, algún test dejaría de pasar y nos indicaría donde estamos introduciendo problemas.&lt;/p&gt;

&lt;p&gt;Además, la falta de test favorece problemas de diseño. Para poder testear nuestro programa necesitamos una forma fácil de hacer el setup necesario para ejecutarlo. Si el código es fácil de poner bajo test, normalmente es indicativo de que tiene un diseño razonablemente bueno. Lo que no quiere decir que no sea mejorable.&lt;/p&gt;

&lt;p&gt;Pero si no tenemos tests, es muy posible que haya muchos aspectos que habremos pasado por alto.&lt;/p&gt;

&lt;p&gt;En nuestro ejemplo, para poder poner el código bajo test tendríamos que forzar un poco las cosas. Por un lado, generar un archivo de datos de ejemplo que contenga una muestra adecuada de lo que podemos esperar. Pero, además, tal como lo hemos escrito es complicado capturar el output del programa, ya que se lanza directamente a la consola. Aunque es posible hacerlo en Ruby, que es el lenguaje que hemos estado usando, implica un trabajo extra.&lt;/p&gt;

&lt;p&gt;En resumen: el hecho de no haber tenido el testing en mente, y ya no estoy hablando de TDD, ha favorecido un mal diseño. O bien, debido al mal diseño, el código es difícil de poner bajo test. Y como no tenemos tests, introducir cambios conlleva un riesgo.&lt;/p&gt;

&lt;p&gt;¿Qué podemos hacer entonces?&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor seguro y refactor probado
&lt;/h2&gt;

&lt;p&gt;Poner un código bajo tests cuando no ha sido diseñado para ello es costoso y puede tener riesgos. El programa con el que estamos trabajando tiene ese problema. No es fácil ponerlo bajo test tal y como ha sido diseñado. El código no tiene estructura y está completamente incorporado en un solo método de un objeto, por lo que tampoco podemos poner bajo tests partes del mismo.&lt;/p&gt;

&lt;p&gt;En estos casos podemos optar por &lt;em&gt;refactors seguros&lt;/em&gt; o &lt;em&gt;probados&lt;/em&gt;. ¿En qué consisten? Los refactors seguros o probados son aquellos que podemos aplicar con la confianza de que no se alterará el comportamiento de la pieza de software ni se introducirán errores, bien porque son automáticos, bien porque está probado que no introducen riesgo. Tenemos dos formas principales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactor automático proporcionado por una herramienta de nuestro editor o entorno de desarrollo. Ya los hemos comentado anteriormente, la automatización nos proporciona la seguridad de que el refactor se aplicará de forma consistente.&lt;/li&gt;
&lt;li&gt;Seguir una receta bien conocida. Con frecuencia, muchos refactorings de ese tipo están automatizados, pero cuando no es así, seguir los pasos de la receta paso a paso, garantiza que el refactor se realiza correctamente y no introduce errores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por otro lado, a fin de minimizar los riesgos lo mejor es seguir este procedimiento general y asumiendo que tenemos el código bajo control de versiones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hacer un &lt;em&gt;commit&lt;/em&gt; del estado actual del código antes de iniciar el refactor, para poder revertir los cambios fácilmente en caso de problemas.&lt;/li&gt;
&lt;li&gt;Ejecutar el refactor, ya sea automático o siguiendo la receta.&lt;/li&gt;
&lt;li&gt;Comprobar que no se han introducido errores. Si es así, revertir los cambios y volver a empezar.&lt;/li&gt;
&lt;li&gt;Si todo ha ido bien, consolidar el cambio, haciendo un nuevo &lt;em&gt;commit&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si el refactor lo hacemos por pasos, deberíamos hacer un commit por cada paso que hagamos siempre que no introduzca errores. En principio, las recetas de refactor probados no dejan nunca el código en estado inestable. Por esa razón, si introducimos un error, podemos deshacer ese cambio fácilmente y volver a un punto estable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mejorando nuestro código con refactors probados
&lt;/h2&gt;

&lt;p&gt;Como hemos dicho, nuestro código reside en un único método de la clase &lt;code&gt;ConsumptionAnalyzer&lt;/code&gt; de tal modo que es muy difícil de testear y modificar en su caso. ¿Podríamos convertirlo en un código más manejable al cual pudiésemos añadir tests, aunque sean parciales?&lt;/p&gt;

&lt;p&gt;La respuesta es que sí. Disponemos de varios refactors que podríamos aplicar en este código y mejorar la situación. Pero antes de eso, me gustaría que nos fijásemos en algunos aspectos de la forma que tiene el código. A ver si los descubres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# frozen_string_literal: true
require "csv"

Outlier = Struct.new(:office, :consumption, :deviation)

class ConsumptionAnalyzer
  def initialize

  end

  def execute(file_name, deviation_factor = 1.4)

    data = CSV.parse(File.read(file_name), headers: true, converters: :numeric)
    consumptions = []
    outliers = []
    data.each do |row|
      consumptions.append(row["consumption"])

      next if consumptions.size &amp;lt; 12

      average = consumptions.sum(0.0) / consumptions.size
      sum = consumptions.sum(0.0) { |element| (element - average)**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      consumptions.each do |consumption|
        next unless (consumption - average).abs &amp;gt; standard_deviation * deviation_factor

        outlier = Outlier.new
        outlier.office = row["office"]
        outlier.consumption = consumption
        outlier.deviation = (consumption - average) / standard_deviation

        outliers.append(outlier)
      end

      consumptions = []
    end
    puts outliers
    puts "Data sample #{data.size} rows"
    puts "Found #{outliers.size} outliers"
    puts "Found #{outliers.size / 300} per office"
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo primero es que tiene muchas líneas, creo que 29. Puede parecer que no es mucho, pero en mi opinión 29 líneas pueden ser demasiadas para un método. Esto es otro &lt;em&gt;code smell&lt;/em&gt; llamado muy apropiadamente &lt;em&gt;long method&lt;/em&gt;. No existe un límite objetivo de líneas para un método, pero cuando hay muchas, debería llevarnos a preguntarnos cosas como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿Contribuyen todas las líneas al objetivo del método?&lt;/li&gt;
&lt;li&gt;¿Podríamos hacer grupos de líneas que colaboran en realizar una tarea?&lt;/li&gt;
&lt;li&gt;¿Se ocupa el código de hacer varias cosas diferentes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;De hecho, hay otro rasgo que he visto definido a veces como &lt;code&gt;code smell&lt;/code&gt;: las líneas en blanco para separar bloques de código que aparentemente se ocupan de cosas distintas. Nosotras podríamos usarlas como herramienta, como veremos a continuación.&lt;/p&gt;

&lt;p&gt;Así que todo apunta a que el método está encargándose de varios trabajos o responsabilidades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Obtener los datos de un archivo físico en formato CSV&lt;/li&gt;
&lt;li&gt;Coleccionar los datos de una oficina&lt;/li&gt;
&lt;li&gt;Calcular los índices estadísticos de media y desviación típica para…&lt;/li&gt;
&lt;li&gt;Decidir si un consumo es excesivamente alto o bajo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un refactor seguro que podemos hacer es el conocido como &lt;em&gt;extraer método&lt;/em&gt;. Consiste en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agrupar todas las líneas que se ocupan de algún asunto en particular.&lt;/li&gt;
&lt;li&gt;Identificar las variables o parámetros de los que depende ese bloque de líneas.&lt;/li&gt;
&lt;li&gt;Identificar el resultado que esas líneas generan y que es usado por el resto del código a continuación.&lt;/li&gt;
&lt;li&gt;Crear un nuevo método vacío cuyo nombre refleje la tarea que hacen esas líneas.&lt;/li&gt;
&lt;li&gt;Copiar el grupo de líneas en el cuerpo del método recién creado.&lt;/li&gt;
&lt;li&gt;Añadir en la signatura del método los parámetros necesarios.&lt;/li&gt;
&lt;li&gt;Hacer que el método devuelva el resultado procesado por las líneas si procede.&lt;/li&gt;
&lt;li&gt;Finalmente, reemplazamos el bloque de líneas por una llamada al método que acabamos de introducir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dicho así suena un poco complicado, sin embargo, es un refactor que suele estar automátizado.&lt;/p&gt;

&lt;p&gt;Vamos a verlo paso a paso con algunos ejemplos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Obtención de los índices estadísticos
&lt;/h3&gt;

&lt;p&gt;Estas cuatro líneas hacen dos cosas diferentes: la primera calcula la media de los consumos y las otras tres, calculan la desviación típica.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity

    data.each do |row|
      # Code removed for clarity

      average = consumptions.sum(0.0) / consumptions.size
      sum = consumptions.sum(0.0) { |element| (element - average)**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      # Code removed for clarity
      end
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separémoslas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity

    data.each do |row|
      # Code removed for clarity

      average = consumptions.sum(0.0) / consumptions.size

      sum = consumptions.sum(0.0) { |element| (element - average)**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      # Code removed for clarity
      end
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La línea que calcula la media hace uso de dos datos que provienen del array &lt;code&gt;consumptions&lt;/code&gt;. Así que depende completamente de este array. Podríamos extraer esta línea a un método lo que ocultaría los detalles del cálculo en este nivel, pero también nos permitiría hacer un test para verificar que el cálculo se hace correctamente.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El parámetro para ese método sería un array como el de consumptions.&lt;/li&gt;
&lt;li&gt;El método debería devolver el valor calculado de la media aritmética.&lt;/li&gt;
&lt;li&gt;Al método podríamos llamarlo simplemente &lt;code&gt;average&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Creamos el método vacío:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer

  # Code removed for clarity

  def average(consumptions)

  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copiamos y pegamos las líneas implicadas. Nota: en Ruby no hace falta poner el return explícito cuando lo que se va a devolver es el último cálculo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer

  # Code removed for clarity

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente, reemplazamos el bloque con la llamada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity

    data.each do |row|
      # Code removed for clarity

      average = average(consumptions)

      # Code removed for clarity
    end
    # Code removed for clarity
  end

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El otro bloque tiene varias características interesantes. Las variables &lt;code&gt;sum&lt;/code&gt; y &lt;code&gt;variance&lt;/code&gt; no se usan fuera de ese bloque. Son variables temporales. ¿Y sabes una cosa? Eso también puede ser un &lt;em&gt;code smell&lt;/em&gt; y es un buen ejemplo de que son síntomas, no problemas en sí mismos. De hecho, la razón de tener variables temporales es que es más fácil entender lo que está pasando.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sum&lt;/code&gt; no es la suma de los consumos del array, sino la suma de los mínimos cuadrados de las diferencias de cada consumo con la media, lo que nos va a proporcionar un nuevo índice estadístico denominado &lt;em&gt;varianza&lt;/em&gt;. De hecho, acto seguido usamos el valor de &lt;code&gt;sum&lt;/code&gt; para calcular &lt;code&gt;variance&lt;/code&gt;, la cual nos proporciona la desviación estándar. En otras palabras, estas líneas colaboran entre sí para calcular la desviación estándar de los datos, pero con nadie más en el código.&lt;/p&gt;

&lt;p&gt;La presencia de estas variables temporales que solo se usan para almacenar durante un momento cálculos parciales nos indica que tendría sentido aislar esas líneas en un método ocupado de calcular la desviación típica.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity
    data.each do |row|
      # Code removed for clarity

      average = average(consumptions)

      sum = consumptions.sum(0.0) { |element| (element - average)**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      # Code removed for clarity
    end
    # Code removed for clarity
  end

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un elemento que nos llama la atención es que también se usa la media, recién calculada en la línea anterior. Parece lógico pasar la media ya calculada para calcular la desviación típica. Pero igual no es tan buena idea. Me explico: el cálculo solo tiene sentido si se hace sobre los mismos datos. Si pasamos el dato de la media calculada sería posible calcular una media con otros datos y la desviación típica con otros.&lt;/p&gt;

&lt;p&gt;Si este bloque solo tuviese el array de datos sería capaz de calcular la media por sus propios medios, bien haciendo el cálculo directamente, bien invocando el método &lt;code&gt;average&lt;/code&gt; que acabamos de introducir y que espera que le pasemos un array de datos.&lt;/p&gt;

&lt;p&gt;Podríamos reescribir ese fragmento así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity
    data.each do |row|
      # Code removed for clarity

      average = average(consumptions)

      sum = consumptions.sum(0.0) { |element| (element - average(consumptions) )**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      # Code removed for clarity
    end
    # Code removed for clarity
  end

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por cierto, esto es otro refactoring que tiene el nombre de &lt;em&gt;inline variable&lt;/em&gt; y que, como hemos visto, es muy fácil de hacer. Basta con reemplazar el uso de una variable con el contenido de esa variable o, como en este caso, la expresión cuyo resultado se le asigna.&lt;/p&gt;

&lt;p&gt;Es posible que alguien enarque una ceja pensando, ¿por qué hacer el cálculo dos veces si ya lo tenemos hecho? ¿No es perjudicial para el consumo de recursos? Podría ser, aunque también podríamos aplicar otras soluciones. Sin embargo, en este caso preferimos hacer independientes entre sí ambos cálculos. Esta discusión nos llevaría a hablar de acoplamiento, pero es un tema en el que ahora mismo prefiero no entrar.&lt;/p&gt;

&lt;p&gt;Ahora que hemos aislado las líneas de código, vemos que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Necesitarán recibir &lt;code&gt;consumptions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Devolverán la desviación estándar.&lt;/li&gt;
&lt;li&gt;El método se llamará &lt;code&gt;standard_deviation&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity
    data.each do |row|
      # Code removed for clarity

      average = average(consumptions)

      sum = consumptions.sum(0.0) { |element| (element - average(consumptions) )**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      # Code removed for clarity
    end
    # Code removed for clarity
  end

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end

  def standard_deviation(consumptions)

  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copiamos y pegamos el cuerpo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity
    data.each do |row|
      # Code removed for clarity

      average = average(consumptions)

      sum = consumptions.sum(0.0) { |element| (element - average(consumptions) )**2 }
      variance = sum / (consumptions.size - 1)
      standard_deviation = Math.sqrt(variance)

      # Code removed for clarity
    end
    # Code removed for clarity
  end

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end

  def standard_deviation(consumptions)
    sum = consumptions.sum(0.0) { |element| (element - average(consumptions) )**2 }
    variance = sum / (consumptions.size - 1)
    standard_deviation = Math.sqrt(variance)   
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reemplazamos el bloque copiado con la llamada. Ahora las variables temporales están limitadas a un contexto por lo que dejan de preocuparnos como &lt;em&gt;smell&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ConsumptionAnalyzer
  def execute(file_name, deviation_factor = 1.4)

    # Code removed for clarity
    data.each do |row|
      # Code removed for clarity

      average = average(consumptions)
      standard_deviation = standard_deviation(consumptions)

      # Code removed for clarity
    end
    # Code removed for clarity
  end

  def average(consumptions)
    consumptions.sum(0.0) / consumptions.size
  end

  def standard_deviation(consumptions)
    sum = consumptions.sum(0.0) { |element| (element - average(consumptions) )**2 }
    variance = sum / (consumptions.size - 1)
    Math.sqrt(variance)   
  end
end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con todo, podríamos seguir aplicando el refactor &lt;em&gt;inline variable&lt;/em&gt; si nos parece que tiene sentido.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def standard_deviation(consumptions)
    Math.sqrt(variance(consumptions))
  end

  def variance(consumptions)
    sum = consumptions.sum(0.0) { |element| (element - average(consumptions))**2 }
    sum / (consumptions.size - 1)
  end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;He dejado todos los métodos públicos, pues de este modo los podría poner bajo test de forma separada.&lt;/p&gt;

&lt;p&gt;Por otra parte, el resultado de estos cambios con respecto al diseño es el aumento de la cohesión del código. Los métodos que hemos introducido agrupan líneas que colaboran en una tarea.&lt;/p&gt;

&lt;p&gt;Ahora bien, si seguimos por aquí tenemos el peligro de caer en una espiral de refactoring que no nos lleve a ninguna parte. El diseño del software debe reflejar la comprensión del problema que resuelve. Si hacemos refactoring sin tener esto en cuenta, podemos perjudicar ese aspecto, moviendo el código hacia un diseño que no coincide con nuestro modelo mental.&lt;/p&gt;

&lt;p&gt;Por eso, es preferible usar estar técnicas solo cuando necesitamos intervenir en el código para añadir o modificar funcionalidades del producto, o cuando necesitamos corregir errores.&lt;/p&gt;

&lt;p&gt;En fin, pienso que como artículo introductorio ya está quedando un poco largo, así que voy a parar aquí y continuaremos hablando sobre la oportunidad del refactor en un nuevo artículo.&lt;/p&gt;

</description>
      <category>articles</category>
      <category>goodpractices</category>
      <category>refactoring</category>
      <category>ruby</category>
    </item>
    <item>
      <title>null is something… to care about</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Sat, 13 Nov 2021 16:52:06 +0000</pubDate>
      <link>https://dev.to/franiglesias/null-is-something-to-care-about-4cf0</link>
      <guid>https://dev.to/franiglesias/null-is-something-to-care-about-4cf0</guid>
      <description>&lt;h2&gt;
  
  
  null is something… to care about
&lt;/h2&gt;

&lt;p&gt;In case you feel familiar with the title… You’re right. I’ve shamelessly stolen it from a talk by Sandi Metz:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/9lv2lBq6x4A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I’ve found myself recently in a situation in which using &lt;em&gt;null&lt;/em&gt; was a bad decision, so I decide to research ideas about how to avoid it and model with a better approach. &lt;a href="https://www.arhohuttunen.com/avoiding-unnecessary-null-checks/" rel="noopener noreferrer"&gt;This article by Arho Huttunen&lt;/a&gt; about avoiding *null *checks helped a lot and also provided me with some useful links, as well as this &lt;a href="https://www.yegor256.com/2014/05/13/why-null-is-bad.html" rel="noopener noreferrer"&gt;other article by Yegor Bugayenko&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mvki6gip4mi4cmdkxk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mvki6gip4mi4cmdkxk7.png" alt="Even the empty cup is something" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;null reference&lt;/em&gt; was introduced by Tony Hoare in 1965 when he was developing the type system for ALGOL W. &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/" rel="noopener noreferrer"&gt;And he regretted it&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. (Tony Hoare (2009): &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/" rel="noopener noreferrer"&gt;Null References: The Billion Dollar Mistake&lt;/a&gt;, QCon, 2009)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, in this post we will try to discuss why &lt;em&gt;null&lt;/em&gt; can be a really big problem, and how to avoid it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;em&gt;null&lt;/em&gt; anyway?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It is a strange fate that we should suffer so much fear and doubt over so small a thing. (Boromir in The fellowship of the Ring, J.R.R.Tolkien)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What is &lt;em&gt;null&lt;/em&gt;? &lt;em&gt;null&lt;/em&gt; is a pointer that doesn’t refer to a valid object. This is similar to saying that &lt;em&gt;null&lt;/em&gt; points to nothing. Or, in simpler words: &lt;em&gt;null&lt;/em&gt; is nothing.&lt;/p&gt;

&lt;p&gt;For example, if we consider a &lt;em&gt;linked list&lt;/em&gt;, in which every item in the list keeps a pointer to the next one, &lt;em&gt;null&lt;/em&gt; is used to mark the end of the list. &lt;em&gt;If this item points to nothing, then the list ends.&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Also, one typical use of &lt;em&gt;null&lt;/em&gt; is to signal that a certain object has not been initialized and is waiting to have a value. We have to put something there, at some moment. The sooner, the better. For example, when we define the properties of a class, we usually initialize them at construction time. Type systems can prevent us from not initializing them by failing when we try to use an uninitialized property. The code above shows some examples.&lt;/p&gt;

&lt;p&gt;Those were situations in which &lt;em&gt;null&lt;/em&gt; is &lt;em&gt;nothing&lt;/em&gt;. Also, they have some traits in common:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usually, we don’t explicitly set them as &lt;em&gt;null&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Those &lt;em&gt;nulls&lt;/em&gt; are internal to the object or data structure. The outer world doesn’t need to know about them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, sometimes &lt;em&gt;null&lt;/em&gt; is &lt;em&gt;something&lt;/em&gt;. And that’s very important when communicating with other objects in the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Null&lt;/em&gt; as “&lt;em&gt;no result”&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;One frequent use of &lt;em&gt;null&lt;/em&gt; is to represent that something is not found. This can happen in several situations.&lt;/p&gt;

&lt;p&gt;Let’s consider a repository of entities. There are two main ways to get an entity from such a repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;by its identifier&lt;/strong&gt;: we get one entity providing its identifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;by specification&lt;/strong&gt;: we can get a set of entities that match certain criteria.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  To be or not to be
&lt;/h3&gt;

&lt;p&gt;In a first way, what we can expect is that one entity with that identifier exists or not. If it exists is not a problem at all. We retrieve it and do whatever we need.&lt;/p&gt;

&lt;p&gt;But if the entity doesn’t exist we get &lt;em&gt;nothing&lt;/em&gt;. It makes sense. If the entity with that identifier was never stored, then you will never find it. There are two big possible reasons: something happened that prevented the entity from being stored in the first place or someone is trying to fool the system by using a non-existent identifier.&lt;/p&gt;

&lt;p&gt;So, we get nothing and the temptation to represent it as &lt;em&gt;null&lt;/em&gt; is strong. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;But it is also wrong because it forces us to check if there is something in the response. It seems kind of ridiculous to verify if my partner has given me something I asked her for. If she cannot give it to me, she simply will say “I don't have it”. I don’t need to check my hand to be sure that I don’t have the damn thing.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The repository should fail with an exception because an entity that we hope does exist does not exist is something that shouldn't happen. The consumer only has to deal with that problem or pass it to a higher level until the exception finds a proper handler.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Sorry, we don’t find any of those
&lt;/h3&gt;

&lt;p&gt;In the specification way, the problem is slightly different. In this case, we can expect that there are no entities that fulfill the specification, thus the result can be &lt;em&gt;nothing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If there are entities that meet the conditions of the specification, then you will get a set of entities. Maybe in the form of an array, a set, a collection, or a similar container data structure. Possibly you will get the set and iterate over it to provide some functionality.&lt;/p&gt;

&lt;p&gt;Here, &lt;em&gt;nothing&lt;/em&gt; means &lt;em&gt;empty&lt;/em&gt;. And that’s because this time &lt;em&gt;nothing&lt;/em&gt; is &lt;em&gt;something&lt;/em&gt;: the result is a set or collection that happens to be &lt;em&gt;empty&lt;/em&gt;. If you iterate an empty collection, nothing will happen, but also, nothing would break. You simply inform the consumer that no results were found.&lt;/p&gt;

&lt;p&gt;So, you don’t usually need to check if the result set is empty.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Null as “maybe…”
&lt;/h2&gt;

&lt;p&gt;Sometimes we use &lt;em&gt;null&lt;/em&gt; to express that something is optional and it can be there or not. Consider this situation:&lt;/p&gt;

&lt;p&gt;We have a student assessment system that allows us to assess their tasks. A grade is mandatory, but you can also add feedback with a customizable message and one standard label.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Ok. Feedback is optional, so we can have an Assessment class with a mandatory Grade and nullable Feedback property. It sounds reasonable, but it leads to several problems.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Most of the time you have to check if Feedback is present. If you forget that, you will find a situation in which you will be sending a message to &lt;em&gt;nothing&lt;/em&gt;, and the system will complain, throwing a &lt;em&gt;Null pointer exception&lt;/em&gt; or its equivalent.&lt;/p&gt;

&lt;p&gt;For example, if you want to show a list view of the assessments for a student, you will have to add a conditional to check if the Feedback exists and render something in the affirmative case and render something different in the opposite one.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The problem here is that &lt;em&gt;No Feedback&lt;/em&gt; is also a &lt;em&gt;Feedback&lt;/em&gt; and &lt;em&gt;null&lt;/em&gt; doesn’t represent that properly, because &lt;em&gt;null&lt;/em&gt; is &lt;em&gt;nothing,&lt;/em&gt; and not having feedback is something.&lt;/p&gt;

&lt;p&gt;You can avoid that by introducing the &lt;strong&gt;Null Object&lt;/strong&gt; pattern instead. A Null Object is an object with the same interface that does nothing or provides some kind of default behavior. The Null Object can even fail with exceptions if it makes sense for your domain at some point.&lt;/p&gt;

&lt;p&gt;Let’s see an example. We define an interface for Feedback:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This is the standard feedback object:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And this is the Null Object implementation:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This way, you can communicate with the Null Object the same way you do with any standard objects of the same type, sending the same messages and expecting a proper response from it:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And not having to worry about checking for &lt;em&gt;null&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Null as “I can’t do this in that way”
&lt;/h2&gt;

&lt;p&gt;Related to the previous topic, sometimes you query an object for some data to perform an operation with the object itself.&lt;/p&gt;

&lt;p&gt;The point here is that if the data we asked for is &lt;em&gt;null&lt;/em&gt;, the consumer has to decide how to manage the situation. For example, gathering another data from the object and trying to call again the same method or another one.&lt;/p&gt;

&lt;p&gt;This problem comes from a code smell known as an &lt;strong&gt;anemic object&lt;/strong&gt;. An anemic object is mostly a container of data with little or no behavior at all. In OOP we expect that objects combine data and processes, in such a way that you don’t need to ask them for data. You tell them to do things, instead.&lt;/p&gt;

&lt;p&gt;A typical example is sending notifications to users in our systems. They usually have several ways to be notified (email, SMS, etc), but it is possible that they don’t have defined a destination for one or several of them. So, you need to ask the User or Customer object about every possible communication channel and decide if you can use one or another.&lt;/p&gt;

&lt;p&gt;This is our anemic Customer model:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And this is a service suffering the consequences:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;One of the possible solutions is to apply the &lt;em&gt;Tell, don’t ask&lt;/em&gt; principle. This way, you can tell the User object to perform the notification and it will do it using the preferred or configured channel.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This way, the problems of managing the &lt;em&gt;nulls&lt;/em&gt; are hidden to other components. So, NotificationService could do something like this, using a Double Dispatch:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Another solution is to re-think the problem and the solution design. If the User object cannot manage the communication, because it is the responsibility of another object, then the User object can provide the preferred communication channel or a Null Object if none is available. This way, the notification service won’t need to worry about checking if they are valid or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust-driven programming
&lt;/h2&gt;

&lt;p&gt;While I was writing this post I started to think about &lt;em&gt;defensive programming&lt;/em&gt; techniques. In general, defensive programming considers all inputs as dangerous, so you shouldn’t trust them. This way, you need to validate all inputs to your system from the outside, but also to your functions, applying sanitization and validation rules to accept them.&lt;/p&gt;

&lt;p&gt;Instead of that, object-oriented programming proposes something that we could call &lt;em&gt;trust-driven programming&lt;/em&gt;. Objects in the system are created valid and complete, and we should expect them to provide valid data, in the form of valid objects and that they will properly manage their concerns. Objects &lt;em&gt;cooperate&lt;/em&gt; for the benefit of others.&lt;/p&gt;

&lt;p&gt;When an object answers with a &lt;em&gt;null&lt;/em&gt;, it introduces mistrust into the system. Mistrust leads to fear, and fear leads to the dark side…  And bad programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t force checking for null
&lt;/h2&gt;

&lt;p&gt;Most of the time, the problem with &lt;em&gt;null&lt;/em&gt; is the need to check if some object response is &lt;em&gt;null&lt;/em&gt;. That means that you cannot trust in that response, and the lack of trust is always a problem.&lt;/p&gt;

&lt;p&gt;Having to check for &lt;em&gt;null&lt;/em&gt; requires the insertion of a conditional clause that increases cyclomatic complexity and introduces risks. What will happen if you forget the check for &lt;em&gt;null&lt;/em&gt;? Or worst: What can happen if you pass along the &lt;em&gt;null&lt;/em&gt; to an indirection chain?&lt;/p&gt;

&lt;p&gt;So, as a general rule, you should not return &lt;em&gt;null&lt;/em&gt;. Instead, try to manage it inside the objects using one or several of these techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fail with an exception&lt;/strong&gt;. This is the way to go when it is possible that the response doesn’t exist at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return Null Objects&lt;/strong&gt;. This allows the consumer to interact with an object that can have some kind of neutral or default behavior. Empty containers, such as collections, can be considered &lt;em&gt;null objects&lt;/em&gt; in the sense that they represent empty sets of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply the “*Tell, don’t ask” *principle.&lt;/strong&gt; Sometimes, having to deal with &lt;em&gt;null&lt;/em&gt; is a design flaw. If you have to &lt;em&gt;ask&lt;/em&gt; an object for some information to do something with the same object, there are a lot of chances that you can &lt;em&gt;tell&lt;/em&gt; the object to do all the process itself, providing adequate behavior in the case that something is not there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-think your design&lt;/strong&gt;. Maybe “Tell, don’t ask” doesn’t apply, but you can consider other options or patterns to redesign the code. &lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>'Learn TDD' book English version mostly finished</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Mon, 26 Jul 2021 13:56:51 +0000</pubDate>
      <link>https://dev.to/franiglesias/learn-tdd-english-version-mostly-finished-2812</link>
      <guid>https://dev.to/franiglesias/learn-tdd-english-version-mostly-finished-2812</guid>
      <description>&lt;p&gt;For all of you interested in learning Test Driven Development, we announce that the book &lt;a href="https://leanpub.com/tddbook-en" rel="noopener noreferrer"&gt;Learn TDD&lt;/a&gt; is mostly finished in its English version.&lt;/p&gt;

&lt;p&gt;You can preview the full content for free in the &lt;a href="https://leanpub.com/tddbook-en/#read" rel="noopener noreferrer"&gt;LeanPub page for the book&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>tdd</category>
      <category>books</category>
    </item>
    <item>
      <title>Entry-level TDD with the Leap Year Kata</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Mon, 21 Jun 2021 11:20:15 +0000</pubDate>
      <link>https://dev.to/franiglesias/entry-level-tdd-with-the-leap-year-kata-2dh2</link>
      <guid>https://dev.to/franiglesias/entry-level-tdd-with-the-leap-year-kata-2dh2</guid>
      <description>&lt;p&gt;Maybe you have heard about the wonders of practicing TDD to develop your software. Or maybe you have read one of those rants about TDD being death. Never mind, if you want to learn how to inaugurate your TDD journey, you are at the right place. Let’s start from the very beginning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fci19xgl2gxsl0y2hkhxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fci19xgl2gxsl0y2hkhxy.png" alt="Mastering TDD with the Leap Year kata" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is TDD
&lt;/h2&gt;

&lt;p&gt;TDD is a development technique based on the idea of writing tests before the production code. It was invented by KentBeck in the nineties as part of Extreme Programming. It basically works this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You decide the new piece of functionality you want to add and write a test that describes it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You write production code until the test pass, indicating that the functionality is working&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You refactor the code for a better design&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat the steps until you have developed the full functionality&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key points to take into account are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You only write one test on each iteration. That is: you don’t define the complete functionality up-front.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each test asks you to write a part of the functionality that the production code currently lacks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every test should be as small as possible, and the changes in production code should be also as small as possible. This is what we call “baby steps”.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other authors contributed to refining the technique. Namely, Robert C. Martin developed the &lt;a href="http://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html" rel="noopener noreferrer"&gt;Three Laws of TDD&lt;/a&gt;. They help us to decide how much test and code we should write. Here they are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You must write a failing test before you write any production code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You must not write more of a test than is sufficient to fail or fail to compile.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You must not write more production code than is sufficient to make the currently failing test pass.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will return to these laws in the examples of this post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi25px02c7m0li3hvz6fa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi25px02c7m0li3hvz6fa.png" alt="Let’s make these tests pass, young padawan" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The TDD cycle
&lt;/h3&gt;

&lt;p&gt;One of the characteristics of TDD is the red-green-refactor cycle.&lt;/p&gt;

&lt;p&gt;The cycle starts with a test that fails because no code makes it pass yet. Usually, testing frameworks represent failing tests in red color, so we say that we are in the red phase of the cycle.&lt;/p&gt;

&lt;p&gt;Our goal is to make the test pass, something that is represented with the green color. So, we can say that our goal is to put the tests in green adding enough production code, but no more.&lt;/p&gt;

&lt;p&gt;At this point, we should start with the most simple or obvious implementation that can work. Even if it seems too obvious or rough, we only want to make the test pass as soon as possible, establishing the desired behavior.&lt;/p&gt;

&lt;p&gt;When we have all the tests passing in green, it is time to refactor the current implementation to a better design. This includes, among other actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Remove code duplication if possible&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improve naming of variables, functions, classes, constants&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improve code organization by extracting parts of it to private methods or new classes as responsibilities emerge&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improve code design as applicable conditions appear&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What TDD is not
&lt;/h3&gt;

&lt;p&gt;TDD uses tests as a development tool, but tests in TDD are not the same as Quality Assurance tests, although they overlap a lot.&lt;/p&gt;

&lt;p&gt;The main differences between the TDD style of testing and QA are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Some TDD tests are redundant for a QA suite. Usually, you start writing a lot of tests that are not needed for QA and should be removed after they fulfilled their purpose of driving development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In general, the goal of TDD tests is to challenge the current production code to add new behavior. QA tests are meant to verify that behavior is the expected one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nevertheless, one of the outcomes of working using TDD is that we get a nice regression suite of unitary tests after clean it a bit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code quality and TDD
&lt;/h3&gt;

&lt;p&gt;TDD is not a guarantee of code quality or good code design. It is a tool that helps to build software with better quality and design, preventing lots of defects and providing us with a methodology to add new functionality or improve design, without breaking anything.&lt;/p&gt;

&lt;p&gt;This is possible because once we make a test pass, these very same tests become a regression test that ensures that the developed behavior stays untouched.&lt;/p&gt;

&lt;p&gt;The refactor phase in the TDD cycle is the moment that we use to increase code quality, migrating from simple, naive, implementation to better-structured ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of TDD
&lt;/h3&gt;

&lt;p&gt;TDD helps us to develop a discipline when writing software. Every test defines a short-test goal and helps us to keep focused on a single task. &lt;a href="https://dev.to/franiglesias/improve-your-life-with-tdd-a5d"&gt;Using TDD you work relaxed, one step at a time&lt;/a&gt;. In fact, some studies show&lt;a href="https://api.semanticscholar.org/CorpusID:16559803" rel="noopener noreferrer"&gt; evidence that teams&lt;/a&gt; doing TDD spent less time debugging, have fewer bugs after deployment, and write a lot more tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-on
&lt;/h2&gt;

&lt;p&gt;Let’s start learning how to develop using Test Driven Development with a pretty simple exercise known as The Leap Year Kata. A Kata is a coding exercise that we should repeat frequently to gain automation of certain thought processes and steps. Kata is a term borrowed from a kind of exercise in martial arts.&lt;/p&gt;

&lt;p&gt;The Leap Year Kata consists of developing a simple class or function to calculate if a given year is a leap year or not. The point of the exercise is to learn how the TDD cycle works, thus the simplicity of the problem.&lt;/p&gt;

&lt;p&gt;For this post, I will develop a Year class, with a method isLeap that returns a bool value indicating if the year is a leap year or not. Let’s suppose a pairing session between an expert TDD practitioner and an entry-level developer.&lt;/p&gt;

&lt;p&gt;– Ok, let’s do this. — said the expert. We are going to write this Year class guided by tests. First of all, we should take a look at the rules for calculating if a year is a leap year.&lt;/p&gt;

&lt;p&gt;– Yes. All years that can be divided by 4, but not by 100, are Leap years.&lt;/p&gt;

&lt;p&gt;– Good. So, years that can be divided by 100 are not leap ones. Except for years divisible by 400 that are leap years.&lt;/p&gt;

&lt;p&gt;– Then, maybe we can start by writing a test to verify that a year is not a leap year. – Said the newbie developer.&lt;/p&gt;

&lt;p&gt;– No, my friend. Those are a lot of steps at once. – Replied the expert.&lt;/p&gt;

&lt;p&gt;– A lot? But, it’s only a single thing.&lt;/p&gt;

&lt;p&gt;– Let’s see. What do you need to check that a year is or not a leap year?&lt;/p&gt;

&lt;p&gt;– Well, I only need a class called Year, with a method isLeap. I can instantiate it with an example of a known common year and verify that the isLeap method returns false. Something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– I can see that we need no less than three things: a class named year, a method in thas class named isLeap, and that the method can return false if the year is not a leap year.

&lt;p&gt;– And are you suggesting writing a test for every single one of them?&lt;/p&gt;

&lt;p&gt;– Exactly, my little padawan.&lt;/p&gt;

&lt;p&gt;– How is that possible?&lt;/p&gt;

&lt;p&gt;– By applying the TDD rules. What is the first one of them?&lt;/p&gt;

&lt;p&gt;– Hum… We cannot write production code unless we have a failing test.&lt;/p&gt;

&lt;p&gt;– So…&lt;/p&gt;

&lt;p&gt;– So, we need to write a test.&lt;/p&gt;

&lt;p&gt;– And… what does the second law say?&lt;/p&gt;

&lt;p&gt;– It says that you cannot write more than one test enough to fail or not compile.&lt;/p&gt;

&lt;p&gt;– That’s correct. And if we write that test you show me before, we will find that it can fail for a lot of reasons. The first thing we need is to be able to instantiate the class, so our first test should force us to define the class to the point that we can instantiate one object.&lt;/p&gt;

&lt;p&gt;– So, that’s a baby step!&lt;/p&gt;

&lt;p&gt;– Sure! Like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– But, but, but… you are not even passing an argument to instantiate the class.

&lt;p&gt;– I know. But this test is enough to fail and to tell us exactly what we need to do next. See what happens if we execute it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Error : Class ‘Year’ not found&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– We should write the class, I guess.&lt;/p&gt;

&lt;p&gt;– Yes, but how much code should we write.&lt;/p&gt;

&lt;p&gt;– Well, we can create a new file named year.php, and define a class named Year, containing a meth…&lt;/p&gt;

&lt;p&gt;– Hold! We only need to write enough code to make this test pass. We don’t even need to create a new file for that.&lt;/p&gt;

&lt;p&gt;– But, that’s not a correct practice. You should separate classes in files.&lt;/p&gt;

&lt;p&gt;– I know that, but we will have the refactoring phase for that. In the meantime, we need to make the test pass, and we can achieve that by doing the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– See? The test passes.

&lt;p&gt;– All right, all right, teacher. But now, we should move the class to its own file.&lt;/p&gt;

&lt;p&gt;– Of course, but now we are protected by a test. If we move the class to the wrong file, the test will not pass, indicating our mistake.&lt;/p&gt;

&lt;p&gt;– Really?&lt;/p&gt;

&lt;p&gt;– For sure! Let's move it and finish our first iteration.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– So, we have moved the class and the test is still passing. That’s great. We have completed a cycle. What should we do the next, little padawan developer?

&lt;p&gt;– We could write a test that invokes the “isLeap” method, but we should instantiate the class before.&lt;/p&gt;

&lt;p&gt;– You’re right. However, we can refactor our current test to have an instance of the class before writing the test. What do you think?&lt;/p&gt;

&lt;p&gt;– I can’t see how useful that would be, but I trust you.&lt;/p&gt;

&lt;p&gt;We prepare ourselves for the next iteration by doing this, leaving the code easy to change as needed, while the test is green. See the code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– That’s not a huge change.

&lt;p&gt;– No, it isn’t. But it is an enabler for our next step.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Now, if we run the test, it fails because of a new reason:

&lt;blockquote&gt;
&lt;p&gt;Error : Call to undefined method App\Katas\LeapYear\Year::isLeap()&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– Yes, little padawan. And the reason is that we don’t have the required method.&lt;/p&gt;

&lt;p&gt;– I see. But… you are writing all in the same test. Is that right?&lt;/p&gt;

&lt;p&gt;– At this point, it is fine. We could write each iteration in a different test, but we will remove these simple tests at the end of the process, so…&lt;/p&gt;

&lt;p&gt;– Wait! Are we going to remove this test at the end? Are you kidding me?&lt;/p&gt;

&lt;p&gt;– No, I’m not kidding. The point is that some of the tests that we write in TDD are useless or redundant out of this context, so we’ll remove them, leaving those that can act as regression tests.&lt;/p&gt;

&lt;p&gt;– Oh!&lt;/p&gt;

&lt;p&gt;– Let’s continue. We need to write our “isLeap” method.&lt;/p&gt;

&lt;p&gt;– Yes, we can then pass the year as a parameter and check if we can divide it by four or not.&lt;/p&gt;

&lt;p&gt;– My dear padawan: what is the minimum piece of code that will make the test pass?&lt;/p&gt;

&lt;p&gt;– Hum… Let me think… Oh! I know: it will be enough with the method definition.&lt;/p&gt;

&lt;p&gt;– That’s correct.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– So, we have the test passing again. Could we improve the code in some way?

&lt;p&gt;– Is there code to improve?&lt;/p&gt;

&lt;p&gt;– Maybe.&lt;/p&gt;

&lt;p&gt;– Ufff. Let me think. Possibly we can declare the return type but it will force us to return a value, and we don’t know what to return at this moment.&lt;/p&gt;

&lt;p&gt;– Any other improvement that can help us with the next steps?&lt;/p&gt;

&lt;p&gt;– Errr… We could introduce the parameter needed in the constructor even if we don’t use it. This will break the test but… we can prevent that by changing the test first, and then modifying the production code to use it.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Fantastic, my padawan! You learn fast. Now the production code, please.

&lt;p&gt;– Yes. Here it is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Great! Now we are ready to start testing behavior. Maybe your first test makes sense now.

&lt;p&gt;– Do you mean that we have been doing this workaround only to return to my first test? Seriously?&lt;/p&gt;

&lt;p&gt;– Don’t get angry. Anger leads to the dark side. You should practice those baby steps until you automate and perform them in a few seconds. That will help you avoid many silly errors, like typos, putting the wrong file in the wrong folder, and using wrong names... Let’s see your test now and run it. What should happen?&lt;/p&gt;

&lt;p&gt;– Easy. It will fail.&lt;/p&gt;

&lt;p&gt;– Because…&lt;/p&gt;

&lt;p&gt;– Because… we don’t have any code that checks that the value of the year can be divided by four, so we will need to add code that can do exactly that.&lt;/p&gt;

&lt;p&gt;– Can you see my point? If we had executed your first test, we would have found that it failed because we have neither the class nor the method. Now, we are sure that the unique valid reason for the test to fail is not having code that performs the behavior that we want.&lt;/p&gt;

&lt;p&gt;– So, let’s consider the test again:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Fine. If we execute it, it fails this way:

&lt;blockquote&gt;
&lt;p&gt;Failed asserting that null is false.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– Yes. And it fails because of a good reason: the behavior is not implemented.&lt;/p&gt;

&lt;p&gt;– Then we now can check if the year is divisible by…&lt;/p&gt;

&lt;p&gt;– I have a better idea, for now, we can simply do the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Now, I know you’re fooling me.

&lt;p&gt;– Not at all. Run the test, please.&lt;/p&gt;

&lt;p&gt;– It passes. But, this will not detect leap years.&lt;/p&gt;

&lt;p&gt;– Yes. It’s pretty obvious. But that’s exactly the behavior that we want at this moment as defined by our tests. Now, our goal is to challenge the current implementation with a new test.&lt;/p&gt;

&lt;p&gt;– Let me guess. We should write a test that verifies that if we instantiate the class with a genuine leap year it will be detected.&lt;/p&gt;

&lt;p&gt;– You are right. And here is such a test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– And it fails because of the right reason:

&lt;blockquote&gt;
&lt;p&gt;Failed asserting that false is true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– True. We need to implement something that makes it pass.&lt;/p&gt;

&lt;p&gt;– Yesssss. Finally!&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Great job, young padawan. Now the test passes and the code identifies most of the leap years. But maybe we can refactor it a bit, don’t you think?

&lt;p&gt;– Yes, let’s do it. I see that we can eliminate the &lt;strong&gt;if&lt;/strong&gt; and return the result of the boolean expression. Also, we can declare the return type as boolean.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Nice. But we need to detect special no leap years, like 1900 or 1800.

&lt;p&gt;– We need a test for that, teacher.&lt;/p&gt;

&lt;p&gt;– And here it is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– If we run it, it fails.

&lt;blockquote&gt;
&lt;p&gt;Failed asserting that true is false.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– I‘ve noted that you are running all of the tests. Why?&lt;/p&gt;

&lt;p&gt;– Because I want to be sure that we don’t break a test that was passing before. This would mean that we have altered the behavior in some way. If this happens, we should stop and fix the code to make that test pass again.&lt;/p&gt;

&lt;p&gt;– Ok, so every test that we make pass becomes a regression test.&lt;/p&gt;

&lt;p&gt;– Exactly. That’s mandatory to be able to refactor. While refactoring, tests must be passing. All of them. That guarantees that we preserve the behavior.&lt;/p&gt;

&lt;p&gt;– But right now, we have a test in red, so it’s time to implement something new, isn’t it?&lt;/p&gt;

&lt;p&gt;– Yes. We should manage the situation in which a year is divisible by 100.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– That was pretty simple, but it does the job.

&lt;p&gt;– It is fine enough. Maybe we can refactor something.&lt;/p&gt;

&lt;p&gt;– Let me see... We have this idea of &lt;em&gt;divisible by&lt;/em&gt; in the code, perhaps we can make it explicit in code extracting the calculation to a private method.&lt;/p&gt;

&lt;p&gt;– Interesting. I like it. Let’s do it. But don’t forget to check that tests keep passing.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– Wow! We are near to complete the development. We need a new test so we can implement the management of the special leap years every 400 years.

&lt;p&gt;– For example, the year 2000.&lt;/p&gt;

&lt;p&gt;– That’s right. Let’s write the test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– As expected, this test doesn’t pass.

&lt;p&gt;– We are on the right track to implement production code that makes it green:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– So, my dear padawan, we are mostly done. Do you think we could refactor something here?

&lt;p&gt;– I can’t see a good opportunity for that. The code is pretty simple and clean. Perhaps we could combine the two last conditions, because if a year number is divisible by four, but not by 100, then the year is a leap year and common if not.&lt;/p&gt;

&lt;p&gt;– Yes. I agree. So, we can refactor the code to reflect that and finish here our first lesson.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
– It’s nice, but I’m not sure if it is better. Anyway, having tests has allowed us to try this new approach being sure that the behavior is not broken. TDD allows for that and to decide if you can refactor to a better design, or experiment with different options.

&lt;p&gt;– This approach has surprised me. It seems slow, but I liked the process a lot.&lt;/p&gt;

&lt;p&gt;– You should practice TDD exercises, every day if possible. By doing so, you will gain experience, speed, and trust. Now, you should practice this kata several times, until you perform it fluidly and fast.&lt;/p&gt;

&lt;p&gt;– I will do.&lt;/p&gt;

&lt;p&gt;– Next time, I will propose you a slightly more complex exercise, and you’ll see that the process is exactly the same.&lt;/p&gt;

&lt;p&gt;– Thank you. I’m looking forward to a new session.&lt;/p&gt;

</description>
      <category>tdd</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Refactoring conditionals</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Mon, 31 May 2021 11:59:44 +0000</pubDate>
      <link>https://dev.to/franiglesias/refactoring-conditionals-5133</link>
      <guid>https://dev.to/franiglesias/refactoring-conditionals-5133</guid>
      <description>&lt;h2&gt;
  
  
  Refactoring conditionals
&lt;/h2&gt;

&lt;p&gt;Conditionals are supposed to add intelligence to code, but they can be a nightmare to understand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvksrmzpcsy04t3zf3s95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvksrmzpcsy04t3zf3s95.png" alt="You need to decide which one" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why refactor conditionals
&lt;/h2&gt;

&lt;p&gt;Our targets when refactoring conditionals are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making it easy to understand the conditions that we are checking&lt;/strong&gt;: conditions can be hard to understand if we check for specific values that are meaningfully related to the concept they try to evaluate. See this example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The condition is fairly easy to describe in human terms: select documents that have too long names. But its code expression is really hard to understand. Let’s simplify this a bit, extracting the conditional expression to a method with a meaningful name.&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;strong&gt;Reduce the cognitive complexity when following the execution flow through their branches&lt;/strong&gt;. When you read a conditional structure you need to keep track of the main flow. If you introduce conditionals inside the branch of another conditional you are creating new breaking points that need to be tracked. At a certain level, you will overflow the ability of your working memory to handle all that tracking.

&lt;p&gt;You can easily spot this by looking at the indentation levels of the code. The more indented it is, the more difficult it to understand and the easier it for bugs to appear.&lt;/p&gt;

&lt;p&gt;You can manage this with several refactors. The most simple and easy to apply is to extract code in branches to methods. That opens new refactoring opportunities inside the private methods.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conditions with meaning
&lt;/h2&gt;
&lt;h3&gt;
  
  
  You don’t always need conditions
&lt;/h3&gt;

&lt;p&gt;We use conditionals to control the execution flow of a program: go this way if a condition is met. If not, go this other. But, sometimes, we can avoid that. Let’s look at this example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
We don’t need the flow control part here, we are served with the response from the object. Also, it’s pretty confusing that you return false in the true leg of the conditional.

&lt;p&gt;To be precise, we can simply return the negation of that response:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This kind of refactor helps to avoid some potential points of failure. With the original code, you have to ensure no less than three things:

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The method invoked provides the correct value&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The conditional expression evaluates to the correct value&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each leg of the conditional returns the correct value&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you only have to check one thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The method invoked provides the correct value&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Ternary operator instead of if/then
&lt;/h2&gt;

&lt;p&gt;The ternary operator is pretty convenient under certain conditions. You should use it carefully because it can bring more problems than solutions. But it is perfect for this situation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
When we have two possible ways of calculating a value depending on a simple condition, the ternary operator is perfectly fine:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The conditions to use the ternary operator are:

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We need to choose between two ways of calculating something&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We don’t nest ternary operators.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benefit here is that we state clearly that we are calculating something (the &lt;strong&gt;$event&lt;/strong&gt; that we want to send in some next step).&lt;/p&gt;
&lt;h3&gt;
  
  
  If it’s complicated, extract a method
&lt;/h3&gt;

&lt;p&gt;The ternary operator works pretty fine in the simple situations exposed before. But if the calculation is more complex or can’t be reduced to clean construction, you’d better extract a method and hide the conditional inside of it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Replace type checking with type safety
&lt;/h3&gt;

&lt;p&gt;Take a look at this code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This code reveals a big &lt;em&gt;bad smell&lt;/em&gt;. If you need to check for type to perform a task, then you have a design problem. Probably, you are trying to ensure that the &lt;strong&gt;Visit&lt;/strong&gt; has a related &lt;strong&gt;Patient&lt;/strong&gt;, so you can do something with it.

&lt;p&gt;&lt;em&gt;Does it make sense that a visit has no patient-related?&lt;/em&gt; If not, you probably should have to fail with an exception. In fact, you should ensure that when instantiating the &lt;strong&gt;Visit&lt;/strong&gt; object. Anyway, the &lt;strong&gt;Visit::getVisitPatient&lt;/strong&gt; method should be typed to state that the expected outcome is a &lt;strong&gt;VisitPatient&lt;/strong&gt;. By doing that, you can enclose the code block in a &lt;strong&gt;try/catch&lt;/strong&gt; to manage the situation.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;strong&gt;Visit::getVisitPatient()&lt;/strong&gt; guarantees that it only will return a &lt;strong&gt;VisitPatient&lt;/strong&gt;. Otherwise, something is wrong.

&lt;p&gt;If not having a &lt;strong&gt;VisitPatient&lt;/strong&gt; is fine for &lt;strong&gt;Visit&lt;/strong&gt;, then you should use &lt;strong&gt;nullable&lt;/strong&gt; return type. This way, instead of checking for type, you will check for object existence, making code cleaner:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
You can do this because &lt;strong&gt;getVisitPatient&lt;/strong&gt; guarantees that it will return a &lt;strong&gt;VisitPatient&lt;/strong&gt; or a &lt;strong&gt;null&lt;/strong&gt;, keeping consumers safe about the type, but having to manage the situation of empty value. So you simply need to check that &lt;strong&gt;$visitPatient&lt;/strong&gt; has a value.

&lt;p&gt;Also, remember that this example could reveal a violation of the &lt;strong&gt;Tell, don’t ask&lt;/strong&gt; principle or &lt;strong&gt;Demeter’s law&lt;/strong&gt;. It is possible that code related to &lt;strong&gt;VisitPatient&lt;/strong&gt; can be happily encapsulated in &lt;strong&gt;Visit&lt;/strong&gt;. You can take a look at &lt;a href="https://medium.com/docplanner-tech/refactor-for-better-knowledge-allocation-ef83be9d44fe" rel="noopener noreferrer"&gt;this previous article on the topic&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Give combined conditions a meaning
&lt;/h3&gt;

&lt;p&gt;Sometimes combined conditions represent a concept that is not explicitly put in code. Let’s see a pretty simple example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This line of code makes you think about what it is actually checking for. This line is in the context of an SMS notification service, so it makes sense to think about the idea of &lt;em&gt;a patient can be notified via SMS or not&lt;/em&gt;. This can be achieved if both two conditions are met:

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The patient has a phone number (that can receive SMS)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The patient chose to allow being notified with SMS&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, instead of asking about these two things to the same object, we can encapsulate both questions into only one, inside &lt;strong&gt;VisitPatient,&lt;/strong&gt; because both conditions belong to the object:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
If we don’t have a better place to move the combined conditions, we can simply create a private method. This is the proper way when we are using values from different origins.
&lt;h2&gt;
  
  
  Managing the flow
&lt;/h2&gt;

&lt;p&gt;Conditionals are control flow structures and we use them to take different actions when certain requirements are met. We’ve been talking about the conditions, but now we will focus on their &lt;em&gt;branches&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can have different problems with the branches of conditional expressions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Too large branches&lt;/strong&gt;: too much code in the body of a branch makes it easy to lose track of the context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unbalanced branches&lt;/strong&gt;: one of the branches is comparatively larger than the other, making the latter &lt;em&gt;invisible&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nested conditionals&lt;/strong&gt;: conditions inside conditions are especially difficult to understand and are a perfect place for intricated bugs to hide.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Too large branches
&lt;/h3&gt;

&lt;p&gt;The best approach is to extract branches to their own method with a descriptive name for the abstraction level. This will make it easier to understand the overall flow, and you always be able to dig into details if needed.&lt;/p&gt;

&lt;p&gt;Look at this code. The branch is remarkably long:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
We move all the code in the branch to a new method, taking care of managing how we return the data:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now, the main flow is far easier to read. We can dive into details by jumping into the extracted method. We even have a chance to extract this code to a collaborator in the future if we need to reuse this particular piece.
&lt;h3&gt;
  
  
  Unbalanced branches
&lt;/h3&gt;

&lt;p&gt;When one of the legs of a conditional structure is a oneliner and the other is huge, it is good practice to invert the conditional, so the shortest branch appears in the first place. This avoids missing it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
It’s much better to extract both branches to methods with a significant name. This way you stress the fact that two flows are depending on the &lt;strong&gt;province&lt;/strong&gt; variable having value or not.&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
You should consider eliminating the &lt;strong&gt;else&lt;/strong&gt; branch. Sometimes you won’t need it. For example, because you can perform an &lt;em&gt;early return&lt;/em&gt;.&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Nested conditionals
&lt;/h3&gt;

&lt;p&gt;Nested conditionals are a source of frustration and pain. They are difficult to follow and unsafe to modify when needed. How can we avoid them?&lt;/p&gt;

&lt;p&gt;First of all, try to ensure that nesting is needed. In the following example, we can see that the inner conditional should be moved to another place. We can guess that by putting next, things that are related to each other. For example, the calculation of &lt;strong&gt;validSince&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The value of &lt;strong&gt;validSince&lt;/strong&gt; can be calculated before, instead of waiting until being in the conditional. And we can introduce the &lt;strong&gt;null coalesce operator&lt;/strong&gt; to simplify the assignment using the first value among all possible that it’s not null.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Our first approach will be to extract the main branches into private methods. Then, you repeat this extraction iteratively so all methods have a maximum of one indentation level in each one. This opens opportunities for further refactors that are pretty difficult, or impossible, in the nested structure. In fact, part of nested structure complexities relies on the fact that you have to carefully manage the execution flow to make sure you don’t overwrite variables or introduce some other bugs.

&lt;p&gt;Let’s take a look at this code extracted from the famous &lt;a href="https://github.com/emilybache/GildedRose-Refactoring-Kata" rel="noopener noreferrer"&gt;GildedRose kata&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This is only half of the code and has a lot of nesting.

&lt;p&gt;For this example, we extract the legs to their methods. Note that this kata deserves an object-oriented approach, but I want to illustrate a different point this time:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
We can see that in the extracted methods the body is inside a conditional. We can apply a &lt;em&gt;return early&lt;/em&gt; refactor that will reduce the nesting. We need to invert the conditional expressions first. As you can see, the readability has increased a lot.&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
We can apply &lt;em&gt;return early&lt;/em&gt; again in the &lt;strong&gt;increaseQuality&lt;/strong&gt; method:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
At this point, we can spot some remaining code duplication that we could refactor. It can be argued that the nesting is there yet. Nevertheless, the takeaway here is that with pretty simple steps we have structured the code so it is much more readable and easier to understand.

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Conditionals can be tricky to manage, but we can improve their readability a lot by using simple refactoring techniques and thinking twice about why we need them.&lt;/p&gt;

&lt;p&gt;Of course, we could go further by applying a good object-oriented design that can even avoid the need for conditional structures. But that should be the topic for another post. In the meantime, you can watch &lt;a href="https://www.youtube.com/watch?v=8bZh5LMaSmE" rel="noopener noreferrer"&gt;this talk by Sandi Metz about how and why to achieve this using the Gilded Rose example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keep an eye on our blog for more tips about refactoring and testing to keep your code healthy.&lt;/p&gt;

&lt;p&gt;Do you enjoy the article? Follow our profile and visit our other channels: &lt;a href="https://docplanner.tech/" rel="noopener noreferrer"&gt;Docplanner.tech website&lt;/a&gt;, &lt;a href="https://www.facebook.com/docplannerTech/?__tn__=%2Cd%2CP-R&amp;amp;eid=ARCMkTULxdIBTOyout0irGzXnbLcnwj_n_n2_RY5mQGdpLjCbKM_VpAqBhN0B_FQD3ygzsJnYWK_Uf_1" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://twitter.com/DocplannerTech" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and&lt;a href="https://www.linkedin.com/showcase/26236889/admin/" rel="noopener noreferrer"&gt; LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Test doubles</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Mon, 31 May 2021 11:59:15 +0000</pubDate>
      <link>https://dev.to/franiglesias/test-doubles-5gdb</link>
      <guid>https://dev.to/franiglesias/test-doubles-5gdb</guid>
      <description>&lt;h2&gt;
  
  
  Test doubles
&lt;/h2&gt;

&lt;p&gt;When you need to test an object using collaborators and want to be sure that the tested behavior is provided only by the code in the object, you probably will need test doubles.&lt;/p&gt;

&lt;p&gt;Using test doubles, you can suppress or keep under control the behavior of the collaborators. This way you isolate the object from any other influence. Also, by using test doubles, you can avoid costly dependencies that can be out of your control or that introduce indetermination or performance penalties.&lt;/p&gt;

&lt;p&gt;But, let’s start from the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we test a piece of software
&lt;/h2&gt;

&lt;p&gt;We test a piece of software by comparing the result of executing it against some defined criteria. We usually name this piece of software the &lt;em&gt;subject under test&lt;/em&gt; or &lt;em&gt;SUT.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These pieces of software can be one of these two types, but not both:&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;query&lt;/strong&gt; retrieves some information about the system's state but doesn’t produce any side effects.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;command&lt;/strong&gt; produces a change in the state of the system but doesn’t return a response.&lt;/p&gt;

&lt;p&gt;Yes: This is the Command Query Separation principle by Bertrand Meyer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing queries
&lt;/h3&gt;

&lt;p&gt;Queries are pretty easy to test because we only need to get the response and compare it with the expected outcome.&lt;/p&gt;

&lt;p&gt;When testing queries we could use test doubles to replace expensive dependencies and to define their behaviors for the different scenarios. Usually, we will be using stubs or fakes for that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing commands
&lt;/h3&gt;

&lt;p&gt;Commands, instead, are a bit harder for testing. We need to verify that we get the expected outcome by looking for their effect on the system. Nevertheless, sometimes we don’t have the possibility to do that because we can’t use the real dependency and we use a test double.&lt;/p&gt;

&lt;p&gt;We will need doubles to define the collaborators' behavior and verify that we produce the desired effect, expecting that we send the proper messages to them. These kinds of doubles are called spies or mocks.&lt;/p&gt;

&lt;p&gt;An example is when we need to test a service that sends an email. There is no way to check that we send a real email. Even if we could do it, looking in some specific mailbox, the performance and reliability of the test would be a complete disaster. So, instead of that, we ensure that we call the appropriate methods of the mailer library with the correct message. We can see an example here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The side-effect of the command is tested in the last two lines, where we interrogate the MailerSpy about the calls performed and the receiver of the message

&lt;h2&gt;
  
  
  Introducing test doubles
&lt;/h2&gt;

&lt;p&gt;We will be using a test example to introduce the different types of test doubles. Imagine that we are building a feature to greet our customers for their birthday, sending them an email, maybe with a promo code or another goodie. Here is the &lt;em&gt;setUp&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
To unit test the use case &lt;em&gt;GreetCustomerForBirthday&lt;/em&gt;, we will need to double all of its collaborators.

&lt;h3&gt;
  
  
  Dummies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dummies&lt;/strong&gt; are test doubles that have no behavior, all their methods return null or nothing. We use them because we need to comply with some interface and we are not particularly interested in what they do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao4dxahjrssfvlo8lzuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao4dxahjrssfvlo8lzuv.png" alt="Dummy (top) model" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s start with the Logger. We may want a logger in the use case but we are not worried about how it is used, but we need it to instantiate the &lt;em&gt;GreetCustomerForBirthdayHandler&lt;/em&gt;. This is the case for a dummy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stubs
&lt;/h3&gt;

&lt;p&gt;Stubs are test doubles that should have a predefined behavior that we want to control. Imagine that we need to test a service that gets some information talking to an external API. We have an adapter to talk with this API so we will need to double it instead of calling the real API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxupemamvqfxyepznjas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxupemamvqfxyepznjas.png" alt="We nailed it with this unicorn stub" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In one of the possible scenarios, we can simulate that the API returns a correct value. In another, we can simulate that the API is down, so we test that our service can manage that situation gracefully. In every possible scenario, we define a stubbed behavior so we can verify our piece of software.&lt;/p&gt;

&lt;p&gt;The use case in our example needs to get the current date to find the customers celebrating a birthday. Working with dates is always a tricky question, so, instead of access to the real system clock, we abstract it in the form of a &lt;em&gt;ClockService&lt;/em&gt;. This way. we simply need to stub a fixed date and store a crafted &lt;em&gt;Customer&lt;/em&gt; in the test repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fakes
&lt;/h3&gt;

&lt;p&gt;Sometimes we need a collaborator that has the same behavior as the real dependency but in a cheaper way. For example, instead of having a database-backed repository, we could use memory storage to provide the same functionality without the performance penalty. This kind of double is called a Fake.&lt;/p&gt;

&lt;p&gt;Fakes should pass the same tests that the original collaborator, they are alternative implementations.&lt;/p&gt;

&lt;p&gt;We decided to use a FakeCustomerRepository for this test using a memory collection implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spies
&lt;/h3&gt;

&lt;p&gt;When we are testing commands we need to verify that they produce the expected outcome. If we need to double the dependency we won’t be able to check that outcome. Alternatively, we will need to verify that we send the right message to the collaborator in the object-oriented programming sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmjamgd4b12xipjqrkes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmmjamgd4b12xipjqrkes.png" alt="She wears sunglasses, she must be a spy" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Spies are test doubles that can register how they are used so we can interrogate them after executing the subject under test and make assertions about that.&lt;/p&gt;

&lt;p&gt;The side effect of this use case is to send an email. We need to create a Spy that can verify that we call the send method of the MailerService with the appropriate data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mocks
&lt;/h3&gt;

&lt;p&gt;Mocks resolve the same problem as spies: we use them to verify that the correct message is sent to the collaborator. The difference is that a mock expects to be used in a certain way so you could say that it carries the assertion with it.&lt;/p&gt;

&lt;p&gt;The problem with spies and mocks is that they introduce a certain degree of fragility given that they couple the test with the implementation of the code under test.&lt;/p&gt;

&lt;p&gt;Also, tests using mocks are more difficult to understand because they hide the assertion into the expectation. It is preferable to use spies, instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  But, do we really need to use doubles?
&lt;/h2&gt;

&lt;p&gt;There is some controversy about using or not doubles in tests. Most of the concerns are with spies and mocks because of the coupling they introduce. Other objections have to do with the fact that we could consider the behavior of an object as a composition of the behaviors of its collaborators, and test that through the public API of the subject under test. Those are valid points, but using doubles is a trade-off we need to accept in several circumstances.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doubles as boundaries
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Functional tests&lt;/em&gt; are those that verify the behavior of the subject under test. We can have different types of functional tests according to the scope of the test:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unitary test&lt;/strong&gt;: check a unit of software in isolation. We should double any collaborator of the unit to ensure that the outcome is produced only by the code of the unit. Nevertheless, you can use real collaborators instead of doubling them if they have no external dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration tests&lt;/strong&gt;: check subsystems to verify that their components communicate properly. We need to double the dependencies used by the subsystems that don’t belong to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;End-to-end tests&lt;/strong&gt;: check the behavior of a system from the outside, using its entry and output points. We should double things that we don’t own, that are expensive in terms of performance, or that have undefined behavior, such as external services, mostly via fakes or crafted versions.&lt;/p&gt;

&lt;p&gt;As you can see, doubles help to define the boundaries of the test scope. You use a double whenever you need to set a boundary that you don’t want to cross in testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolation
&lt;/h3&gt;

&lt;p&gt;Those boundaries seldom match architectural boundaries that put your tests at risk if you cross them.&lt;/p&gt;

&lt;p&gt;You don’t want to include in your test the performance penalty of talking to a real database at the unitary level, so you will double the database adapter to achieve this.&lt;/p&gt;

&lt;p&gt;You don’t want the risk of having pre-existing data that will make unpredictable the behavior of the subject under test and the overload of having to manage to keep the database state clean.&lt;/p&gt;

&lt;p&gt;You don’t want to cross your fingers trusting some external API to be up and running or returning the expected responses for your tests. In consequence, you will need to fake it in some way.&lt;/p&gt;

&lt;p&gt;You don’t want to be dependent on the machine or the concrete set-up on which the test runs, so you use doubles to isolate from those. A typical situation is when a test has to manage dates or times, you should stub the behavior of the system clock to avoid tests that fail or pass depending on the machine, the timezone, or even the date or time when you run them.&lt;/p&gt;

&lt;p&gt;So, doubles are one of the tools that we count on to isolate tests from all the details that we don’t own or cannot control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outside-in test-driven development
&lt;/h3&gt;

&lt;p&gt;The so-called London school of TDD uses doubles as first-class citizens in the outside-in approach to test-driven development.&lt;/p&gt;

&lt;p&gt;In the inner unitary test loops, mocks are used extensively as design tools, doubling the collaborators used by the component under development to define and refine their interfaces and the communication between units. The drawbacks of this approach, such as the test-implementation coupling, are compensated by the fact that we are designing the implementation and it probably won’t change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices with doubles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  You don’t share doubles behavior between tests, except fakes.
&lt;/h3&gt;

&lt;p&gt;Shared doubles can introduce dependencies between tests, something that leads to low reliability and low trustfulness. The needs of one test are not the same as another, and they should be able to evolve separately.&lt;/p&gt;

&lt;p&gt;So, you can share doubles when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;They are fakes in the sense of alternative, low-cost fully-owned implementations of a given dependency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They are dummies, so they have no behavior, and, therefore, they don’t introduce cross dependency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They are configurable stubs, so you can control the behavior from the test.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  You only stub the behavior that is interesting for the current test.
&lt;/h3&gt;

&lt;p&gt;In addition, you don’t want to have one-size-fits-all doubles. You only stub the behavior you need for the test at hand, forgetting about other possible scenarios. Remember that you want to have isolated tests, even inside the same test case.&lt;/p&gt;

&lt;h3&gt;
  
  
  You set the minimum expectations needed to verify a side effect in mocks.
&lt;/h3&gt;

&lt;p&gt;Expectations are useful to assert side effects in mocks, but you don’t use them to verify calls to stubs.&lt;/p&gt;

&lt;p&gt;I mean: in a query, you could need to stub some behavior in a collaborator, the test will execute the call to that collaborator and get the stubbed response. The test will pass if the code under test manages the collaborator response the right way returning the correct response. In this situation, you don’t need to assert that the message was issued to the collaborator: it is implied in the successful test execution.&lt;/p&gt;

&lt;p&gt;When testing a command, you will need to verify the side-effect of calling a specific collaborator, so an expectation should be set to check that this really happens, and only for that.&lt;/p&gt;

&lt;p&gt;Ideally, you will have only one expectation by test except when you need some triangulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create doubles
&lt;/h2&gt;

&lt;p&gt;There are several techniques to create doubles:&lt;/p&gt;

&lt;h3&gt;
  
  
  Using real objects
&lt;/h3&gt;

&lt;p&gt;This technique is specially used when creating fakes, but you can use it for other types of doubles you would like to share between tests, like dummies.&lt;/p&gt;

&lt;p&gt;It’s pretty straightforward: create and use a new implementation for the interface you need to double. The following example is a pretty simple example of a CustomerRepository implementation only for tests:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
To use it, we only need to do this:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Let’s see an example of a spy:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The main problem with this type of implementation is that it is not Liskov Substitution Principle compliant given we add some query methods to be able to verify the expected outcomes. This is a trade-off that we can accept because their use is limited to the test environment.

&lt;h3&gt;
  
  
  Using anonymous classes
&lt;/h3&gt;

&lt;p&gt;Anonymous classes are a great way to create disposable doubles for a specific test without using a mocking framework and keeping the advantages of real objects. Here, you have an example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The previous example violates Liskov Substitution Principle, so you can be stricter:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
As you can see, we don’t even need to extract the class to another file, but you could use a factory if makes sense for your use case.

&lt;h3&gt;
  
  
  Using mocking frameworks
&lt;/h3&gt;

&lt;p&gt;Mocking frameworks can be an easy way to build doubles that are expensive to double using the other techniques. For example, in PHP, the PSR Logger Interface is huge, with no less than nine methods to implement. It is way simpler to use the mocking framework like this and get a dummy double:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Every mocking framework has its own syntax. I will be using the native PHPUnit one in these examples, but you will get the point.

&lt;p&gt;To build a stub, you only have to define a response to a method call, like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This is an example of a &lt;em&gt;MailerService&lt;/em&gt; mock that expects the method &lt;em&gt;send&lt;/em&gt; to be called with a message.&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
In this snippet we have defined a mock of the &lt;em&gt;MailerService&lt;/em&gt; interface, setting the expectation that we will be calling its send method once with a &lt;em&gt;Message&lt;/em&gt; object.

&lt;p&gt;We can create a spy, instead of a pure mock:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
In this example, you inject the &lt;em&gt;$mailerMock&lt;/em&gt; as a dependency, but you check the outcomes asking to the &lt;em&gt;$mailerSpy&lt;/em&gt;. It is pretty weird, but other mocking frameworks offer better interfaces for this feature.

&lt;p&gt;In fact, you probably will be better served by crafting your own doubles instead of using a mocking library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up…
&lt;/h2&gt;

&lt;p&gt;Test doubles are a tool that we will need to use sooner or later. Their purpose is to help us isolate the code under test replacing their dependencies.&lt;/p&gt;

&lt;p&gt;We can use several techniques to create them ranging from implementing our own doubles to use a mocking library.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Refactor for better knowledge allocation</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Mon, 31 May 2021 11:56:02 +0000</pubDate>
      <link>https://dev.to/franiglesias/refactor-for-better-knowledge-allocation-46o4</link>
      <guid>https://dev.to/franiglesias/refactor-for-better-knowledge-allocation-46o4</guid>
      <description>&lt;h2&gt;
  
  
  Refactor for better knowledge allocation
&lt;/h2&gt;

&lt;p&gt;One of the ongoing problems inside all organizations is documentation. From an agile perspective, code can document a good part of the knowledge about the business when written in an expressive, well organized, fashion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nd8v10itgq5c669hz6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nd8v10itgq5c669hz6v.png" alt="Where does this knowledge belong to?" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But this frequently does not happen due to several reasons. Poor communication, too much emphasis in implementation details, &lt;em&gt;framework driven development&lt;/em&gt;, among others. So, many code bases present problems when you try to understand how they express the domain of the business.&lt;/p&gt;

&lt;p&gt;In our previous post we talked about some tips to refactor code to be a better storyteller. This time we will continue digging into the same idea, but from a slightly different point of view: how to better structure the knowledge.&lt;/p&gt;

&lt;p&gt;Knowledge means a lot of things. In every software project there are both business knowledge (how to book a visit with a doctor, for example), but also technical knowledge (how to connect to some database server). The problems arise when different domains of knowledge appear mixed in code. It can be technical concerns entangled with business concerns, different areas of the business sharing pieces of code or huge levels of coupling between different parts of the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Object-Oriented Software… or not so much
&lt;/h2&gt;

&lt;p&gt;There are lots of code bases in the wild written in object oriented languages, using classes and objects, that doesn’t really apply well the OO paradigm. They consist of procedural code with an object-oriented costume. In this code bases, knowledge is something global and objects are usually only containers for data, behavior or both, but they are not really self-consistent, well encapsulated objects.&lt;/p&gt;

&lt;p&gt;In the OO paradigm, objects are experts that encapsulate knowledge and behavior, communicating and cooperating to fulfill the tasks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most difficult problem in teaching object- oriented programming is getting the learner to give up the global knowledge of control that is possible with procedural programs, and rely on the local knowledge of objects to accomplish their tasks. Novice designs are littered with regressions to global thinking: gratuitous global variables, unnecessary pointers, and inappropriate reliance on the implementation of other objects. (&lt;a href="http://c2.com/doc/oopsla89/paper.html" rel="noopener noreferrer"&gt;Beck &amp;amp; Cunningham: A Laboratory For Teaching Object-oriented Thinking&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Object-oriented design is guided by a plethora of principles that serve as guidelines to make decisions about how to allocate knowledge and how objects should interact. We can benefit from applying this principles to improve the quality of our code, moving from a procedural style to a more object-oriented one.&lt;/p&gt;

&lt;p&gt;So, in this post we will review some of these principles and we will try to show them in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separation of concerns principle
&lt;/h2&gt;

&lt;p&gt;This is pretty simple, and the basement for most architectural and code organization decisions: different parts of the code addresses different concerns.&lt;/p&gt;

&lt;p&gt;We can understand this principle from another perspective: a unit of code should not address more than one concern. If so, it needs to be broken into parts. We can apply this to different levels: the methods in a class, the different class in a software module, the different modules in a software application, or the different layers.&lt;/p&gt;

&lt;p&gt;This basic principle, enunciated by Dijsktra, is in the roots of the well know &lt;em&gt;Single Responsibility Principle&lt;/em&gt;, the &lt;em&gt;S&lt;/em&gt; in SOLID, and most of the patterns and principles that help us to put knowledge where it belongs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single Responsibility Principle&lt;/strong&gt; states that software units should have one unique responsibility, defined as a unique reason to change. It doesn’t mean to do only one thing, that could lead us to a convoluted design. Instead of that, this principle is better applied if we consider the reasons that could force us to change that unit. Let’s see an example.&lt;/p&gt;

&lt;p&gt;Price is a common concept for most business. It could be modelled initially with this class:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Pretty anemic, huh? But it is a starting point. Now, the Finance team asks for a way to add VAT to the price. We honor immutability and add a factory method that returns the price with added VAT. Well done!

&lt;p&gt;Also, the Front-End crew asks for a price representation that includes the currency information. We add a simple format method to the class.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Hey! Different currencies requires different representation formats. Also, our beloved Marketing team asks for a feature to add discounts and promotions to our Price objects. Wait a moment! This poor little class has a lot of reasons to change. We are asking it to hold a lot of responsibilities.

&lt;p&gt;We need a change to address that.&lt;/p&gt;

&lt;p&gt;First, Price should be only responsible for holding information about… price. We can model Taxes and Discounts as Price decorators, so we always will be able to recover the base price. Here is an example. It’s not perfect, but now we have separated some of the responsibilities.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The method addVat in Price will become unnecessary but if it is being used we can remove it iteratively.

&lt;p&gt;So, do you need a discount? No problem:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now, you can use it like in the following test. Price is now a compounded object:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now, if the Finance team asks us to apply another Tax to our price, or one different because we need prices for several countries with different laws, we only need to add classes accordingly without touching Price itself.

&lt;p&gt;Also, if Marketing asks us to apply new discounts and promotions, we only need to add classes for each one.&lt;/p&gt;

&lt;p&gt;All of those new classes may change for one unique reason, so we are honoring the Single Responsibility Principle… at least in this area. Remember that we need to address the format concern. But we can apply a similar solution.&lt;/p&gt;

&lt;p&gt;Oh, and another benefit is that by doing this approach, we also are honoring the &lt;em&gt;Open for extension, closed for modification principle&lt;/em&gt;. This principle states that we should avoid to modify existing code in order to add or modify behavior. Instead, we should provide means to extend object behavior without touching that existing code.&lt;/p&gt;

&lt;p&gt;Let’s return to the format problem. It exposes an interesting problem that requires us to talk about segregating interfaces.&lt;/p&gt;
&lt;h2&gt;
  
  
  Interface Segregation Principle
&lt;/h2&gt;

&lt;p&gt;If you look at our Decorators you can see that they have to carry the &lt;strong&gt;format&lt;/strong&gt; method, and they really don’t need it. Price and their Decorators are having two reasons for change: one related to their business meaning, and one related to their presentation. Also, you have to duplicate it in every type you need to create, multiplying the problems with the format variations.&lt;/p&gt;

&lt;p&gt;When working with legacy objects, it is very easy to find classes that hold too many responsibilities and had huge public interfaces with lots of methods serving different kinds of consumers.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Interface Segregation Principle&lt;/em&gt;, states that an object should not depend on methods that it will not use. We should design narrower interfaces based on the needs of its consumers.&lt;/p&gt;

&lt;p&gt;In our example, PriceInterface has methods that obey to different kinds of consumers: one is interested in the amount concerns, and one is interested in the presentation concerns. So, we should separate those concerns in different interfaces.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now, the old Price class implements two interfaces, but our decorators only implement one of them: the one that relates with amount modifications. They are free about presentation concerns.

&lt;p&gt;We can solve these concerns with another family of decorators using the PriceFormatterInterface:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now, you can compose them:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
It looks nice. And it is because we now have separated responsibilities in different classes, with pretty narrow interfaces.

&lt;p&gt;An extra benefit is that we removed some duplication. Let’s talk about being DRY.&lt;/p&gt;
&lt;h2&gt;
  
  
  The DRY principle
&lt;/h2&gt;

&lt;p&gt;The Don’t Repeat Yourself principle, by Hunt and Thomas, states that “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system”.&lt;/p&gt;

&lt;p&gt;The principle doesn’t refer to code duplication, it talks about knowledge duplication. This is an important distinction, because trying to apply DRY to any code duplication can lead us to tough problems. Sandi Metz has a post that worth the reading: &lt;a href="https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction" rel="noopener noreferrer"&gt;The wrong abstraction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppc63b6q6ycos8906bk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppc63b6q6ycos8906bk3.png" alt="Can you spot the abstraction here?" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyway, code duplication can be a symptom of knowledge duplication, it could mean that there is an interesting concept emerging. But you should consider this carefully. Code duplication can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Be unavoidable&lt;/em&gt;: three or more fragments of code look similar, but they don’t represent the same knowledge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Be unnecessary&lt;/em&gt;: three or more fragments of code can be reconciled extracting the parts that are different for economy. For example, blocks of code that can be extracted to one method or function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Represent knowledge duplication&lt;/em&gt;: three or more fragments of code are examples of a more general concept or abstraction.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you notice the &lt;em&gt;three&lt;/em&gt;? There is an heuristic called &lt;em&gt;The Rule of Three&lt;/em&gt; that advises to not refactor duplication unless you have three or more occurrences of the same code. It is a simple rule to prevent &lt;em&gt;premature abstraction&lt;/em&gt; if we don’t have enough information to decide what kind of duplication we are spotting.&lt;/p&gt;

&lt;p&gt;But, is there a similar rule to find knowledge duplication that we should refactor? Yes. If you need to make the very same change in several parts of the code at once, in order to introduce or modify a behavior, then you have a candidate.&lt;/p&gt;

&lt;p&gt;I will try to show you some examples of when to apply DRY and when don’t with our Price Decorators family.&lt;/p&gt;

&lt;p&gt;Does it make sense that both decorators share a common ancestor in order to avoid duplications? No, it doesn’t. Taxes and Discounts are two different concepts, two different pieces of knowledge with their own business rules and they are also managed by different teams in the company. Instead, we could model Taxes and Discounts as different abstractions.&lt;/p&gt;
&lt;h2&gt;
  
  
  The tell, don’t ask principle
&lt;/h2&gt;

&lt;p&gt;The basic idea is that if you are getting information from an object to operate with it, you should encapsulate the whole operation in that object because it is where it belongs. Instead of asking the object about the data needed for the calculation, it is better to tell the object to calculate that itself.&lt;/p&gt;

&lt;p&gt;In a more formal definition: if you need to ask an object about its state in order to change the state itself, you are violating both the information hiding and encapsulation principles. Then, you should encapsulate the operation in a method.&lt;/p&gt;

&lt;p&gt;This principle is very useful to start moving knowledge to the objects to which belongs. Objects should be information experts about themselves. Also, objects are the solely responsibles about its state and consistency. You should be able to trust your objects in that concern. This will make your life easier.&lt;/p&gt;

&lt;p&gt;Let’s see an example. Imagine you have a concept TimeSlot to allocate, ahem…, time slots in a Calendar application. If you want to know if two slots overlap you could do this &lt;em&gt;beauty&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
You are asking time slots about data to perform a calculation in order to guess something about the state of both slots, among other code quality violations. But this is a knowledge that belongs to the TimeSlot object itself: to know if it overlaps with another TimeSlot:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
So you can tell a TimeSlot to calculate if another one is overlapping or not, because it has all the knowledge about itself.&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Anemic object smell
&lt;/h3&gt;

&lt;p&gt;This principle is related with the &lt;em&gt;anemic object smell&lt;/em&gt;. An anemic object, or more specifically an &lt;em&gt;anemic model&lt;/em&gt;, is an object that has only state, but no behavior. There are several objects that are designed to only contain data, for example Data Transfer Objects, but we are referring here to objects that model entities or values that should also have domain behavior.&lt;/p&gt;

&lt;p&gt;In our example, the first implementation of TimeSlot was an anemic object. It only had properties and accessors to those properties.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
As we have shown above, TimeSlot can, and must, encapsulate its own knowledge in the form of properties and behavior. Rich objects also can attract more behavior needed by the application. Imagine that you need a way to get a new TimeSlot just exactly after a given one. Here is the dirty way:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
But, TimeSlot could be able to create them for us:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Because it knows how:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This way, you know that if you need to learn about time slots, your first stop should be the TimeSlot class.

&lt;h2&gt;
  
  
  The minimum knowledge principle or Demeter’s law
&lt;/h2&gt;

&lt;p&gt;This principle states that a unit of software, usually a function or method, should not talk with objects that they don’t know directly. What objects are those?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Objects instantiated inside the unit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Object passed to the unit as parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The object that owns the unit (its methods and attributes)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application of this principle helps a lot to avoid coupling. Coupling is the degree of dependency between software units. Some level of coupling is unavoidable but you can keep it under control. The secret for a healthy coupling is that objects know the minimum about other objects.&lt;/p&gt;

&lt;p&gt;An object should not rely in having intimate knowledge about the internal structure of another. For example, an object should not perform calls on an object inside another one. This is a smell called &lt;em&gt;inappropriate intimacy&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You should talk with an object using only its public interface. If you really need to access to some property or internal, you should consider to add a method that exposes it. But also, you should ask yourself if that knowledge should be available in another object.&lt;/p&gt;

&lt;p&gt;Consider this piece of code. We have a PricingCalculator that allows us to calculate product pricing applying different rules. This is a new way to model some of the behaviors of our previous examples.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
As you can see, base price of Product is defined in its product Family. Let’s see what happen inside the calculator:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
This works, but PricingCalculator has to know that, in order to get the product price, it has to ask first for the Family and then for the price. Now, that’s tight coupling: PricingCalculator must know intimate details of the internal structure of Product. Nevertheless, It shouldn’t need to know how the price is built or where it come from. It only should know that Product has a price.

&lt;p&gt;In the future, if you change Product to have its price defined in a different way, the program could break. For example, imagine that Family is no longer responsible of the Product price:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
If we don’t change PricingCalculator, the program will fail, because it expects to find the price in the Family object. It depends on talking with an object of which it doesn’t know about.

&lt;p&gt;In this case, Product should be the source of truth about its price:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now, PricingCalculator only depends on Price, a known object because it is passed as a parameter in the method forProduct.

&lt;p&gt;In this case, applying Demeter’s Law helps us to decouple and to make our code more resilient to changes, allowing objects to change its internals without affecting others unnecessarily. So, every time you find those chained calls for access objects contained in another object, take a time to encapsulate that logic or event consider if that knowledge belongs to another place.&lt;/p&gt;

&lt;h2&gt;
  
  
  More refactoring tips coming
&lt;/h2&gt;

&lt;p&gt;We expect that with this post you have some food for thought about how to allocate knowledge and behavior in your code.&lt;/p&gt;

&lt;p&gt;Object-oriented design principles are a very good guide to help us to move and organize concepts in our codebase, allowing for a better modelling of the domain knowledge. We didn’t address all of them, of course. The lesson here is to take them into consideration when refactoring.&lt;/p&gt;

&lt;p&gt;There are a lot of things about refactoring, so stay tuned to learn about managing complex and nested conditionals and more ways to improve the health of your code.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>No hagas estos tests</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Thu, 08 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/franiglesias/no-hagas-estos-tests-2b8p</link>
      <guid>https://dev.to/franiglesias/no-hagas-estos-tests-2b8p</guid>
      <description>&lt;p&gt;O al menos, no de esta manera.&lt;/p&gt;

&lt;h2&gt;
  
  
  Objetos mensaje
&lt;/h2&gt;

&lt;p&gt;Eventos, Comandos y Queries, son objetos que nos interesan por su significado y que llevan datos para sus respectivos &lt;em&gt;handlers&lt;/em&gt;. En ocasiones me han planteado: ¿no hay que testear esto?&lt;/p&gt;

&lt;p&gt;Quiero decir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class PostWasCreated
{
    private string $title;
    private string $body;

    public function __construct(string $title, string $body)
    {
        $this-&amp;gt;title = $title;
        $this-&amp;gt;body = $body;
    }

    public function title(): string
    {
        return $this-&amp;gt;title;
    }

    public function body(): string
    {
        return $this-&amp;gt;body;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;– ¿No hay que testear que se construye bien?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class PostWasCreatedTest extends TestCase
{
    public function testShouldCreateEvent(): void
    {
        $event = new PostWasCreated('Title', 'Body');

        self::assertTrue('Title', $event-&amp;gt;title());
        self::assertTrue('Body', $event-&amp;gt;body());
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;– Pues no. Tanto si es en una situación de TDD, como si en QA, este test es una pérdida de tiempo.&lt;/p&gt;

&lt;p&gt;– Pero imagínate que te lías y asignas el valor de body a title y viceversa.&lt;/p&gt;

&lt;p&gt;– Es que no lo tienes que testear así.&lt;/p&gt;

&lt;p&gt;En primer lugar, buscamos testear comportamientos. Asignar los parámetros pasados a las propiedades no es un comportamiento en sí mismo.&lt;/p&gt;

&lt;p&gt;– ¿Pero no testeas que se crean objetos consistentes? Si hasta tienes una kata basada en eso en este mismo blog.&lt;/p&gt;

&lt;p&gt;– Por supuesto, pero lo que verificamos es que construimos objetos que cumplen reglas de negocio y mantienen invariantes. De nuevo: la inicialización de propiedades no es un comportamiento.&lt;/p&gt;

&lt;p&gt;– ¿Entonces?&lt;/p&gt;

&lt;p&gt;– La forma adecuada de testear esto es testeando su &lt;em&gt;handler&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Imaginemos algún servicio de reservas que notifica a la usuaria por correo electrónico que la reserva ha sido realizada correctamente. Esto se hace porque al realizar la reserva, se lanza un evento &lt;code&gt;BookingWasCreated&lt;/code&gt;, que contiene los datos básicos de la misma.&lt;/p&gt;

&lt;p&gt;Uno de los suscriptores o listeners de este evento es &lt;code&gt;SendConfirmationMessage&lt;/code&gt; que compone un mensaje y lo envía a través de un &lt;code&gt;Mailer&lt;/code&gt;, aquí representado con un doble.&lt;/p&gt;

&lt;p&gt;El razonamiento es el siguiente: si el evento está bien construido, el mensaje se construirá como se espera. Si el test falla porque el mensaje resultante es, por ejemplo, “Booked from 15/5/2021 to 15:35 @ 16:20”, está claro que en alguna parte se han cruzado los datos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SendConfirmationMessageTest extends TestCase
{
    public function testShouldNotify(): void
    {
        $event = new BookingWasCreated(
            'fran@example.com', 
            '15/5/2021', 
            '15:35', 
            '16:20'
        );

        $message = new Message('Booked from 15:35 to 16:20 @ 15/5/2021');
        $message-&amp;gt;to('fran@example.com');

        $mailer = $this-&amp;gt;createMock(Mailer::class);
        $mailer
            -&amp;gt;expect(self::once())
            -&amp;gt;method('send')
            -&amp;gt;with($message);

        $sendConfirmationMessage = new SendConfirmationMessage($mailer);

        $sendConfirmationMessage-&amp;gt;handle($event);  
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Todo esto aplica igualmente a DTO y otros objetos parámetros que solo llevan datos y no tienen comportamientos.&lt;/p&gt;

</description>
      <category>articles</category>
      <category>testing</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Refactor for better comprehension</title>
      <dc:creator>Fran Iglesias</dc:creator>
      <pubDate>Wed, 03 Mar 2021 16:03:17 +0000</pubDate>
      <link>https://dev.to/franiglesias/refactor-for-better-comprehension-4e6j</link>
      <guid>https://dev.to/franiglesias/refactor-for-better-comprehension-4e6j</guid>
      <description>&lt;p&gt;You are in front of a long-lived codebase and you understand… Well, not so much.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5qxqjlhmt5sma9a5hrh.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5qxqjlhmt5sma9a5hrh.jpeg" alt="Reading and understanding code can be hard" width="800" height="600"&gt;&lt;/a&gt;&lt;em&gt;Reading and understanding code can be hard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You want the code to tell you its story in order to add your own part to it. Nevertheless, you may find a code base, or some piece of it, that is a little pandemonium, where you cannot obtain too much information easily. You need to interpret how some concepts are represented and how some processes are reflected. Maybe, you try to read the documentation, but it can be outdated, redundant, useless, or non-existent.&lt;/p&gt;

&lt;p&gt;You need to refactor for better comprehension before your start implementing a new feature or fixing that bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactor for comprehension&lt;/strong&gt; is the process we perform to evolve present code structure to another one that is more understandable. One that reflects better the state of business knowledge and one that is easier to operate with.&lt;/p&gt;

&lt;p&gt;In fact, we should consider code as an executable representation of the business or domain knowledge. Tests, on the other hand, are another representation of the same knowledge, built around the outcomes of the production code.&lt;/p&gt;

&lt;p&gt;When we need to work with a messy piece of code, we surely will need to introduce some changes that should improve our ability to understand what’s going on. We have three areas in which to intervene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Production code itself&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test covering that production code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comments and documentation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we start talking about how to make code easier to understand, we should first learn about the “two hats metaphor”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two hats to rule them all
&lt;/h2&gt;

&lt;p&gt;The two hats metaphor by Kent Beck helps us to understand something very important about refactoring.&lt;/p&gt;

&lt;p&gt;The basic idea is that you must not mix refactoring with changing the behavior of a piece of code.&lt;/p&gt;

&lt;p&gt;So, you work on preparatory refactoring crafting structural changes wearing the &lt;em&gt;refactor hat&lt;/em&gt;. Then, you commit that set of modifications as a whole. Once committed, you work in the behavioral change and commit it wearing the &lt;em&gt;change behavior hat&lt;/em&gt;. After that, you may need to put on the &lt;em&gt;refactor hat&lt;/em&gt; again to tidy up things.&lt;/p&gt;

&lt;p&gt;Beck says that the preparatory refactoring consists on reorganizing code to make the change of behavior easy and safe to apply:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-250733358307500032-863" src="https://platform.twitter.com/embed/Tweet.html?id=250733358307500032"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-250733358307500032-863');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=250733358307500032&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;You can’t wear two hats at the same time… Well, you shouldn’t. So, we ask you to put the &lt;em&gt;refactor hat&lt;/em&gt; right now because we are going to introduce some nice ideas about how to improve the storytelling abilities of the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor for better names
&lt;/h2&gt;

&lt;p&gt;The first tool to make code explain itself is to choose names wisely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwxa69amvep5fhhrlv0d.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwxa69amvep5fhhrlv0d.jpeg" alt="Naming things is also a teamwork" width="800" height="592"&gt;&lt;/a&gt;&lt;em&gt;Naming things is also a teamwork&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Naming is reputed as one of the most difficult things in computer science, along with invalidating the cache, but you don’t have the obligation to spot on the first try. You can rename things for very good reasons, among others:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A concept that is represented in code with a name that doesn’t describe it accurately from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A concept that could have evolved and its name became obsolete.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A name that could be too general or too narrow for the concept that it describes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can apply a &lt;em&gt;rename refactor&lt;/em&gt; in these situations. Let’s see some examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Name coupled to implementation
&lt;/h3&gt;

&lt;p&gt;Consider this variable name:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Leaving aside other questions about the code, what is the problem with this name? As we can see, the name mentions the current specific time limits decided by the business.&lt;/p&gt;

&lt;p&gt;So, if those limits change someday, we could end with something like the first line of the following example, so we should change the variable name in order to keep it consistent with business requirements.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We will need to do the same every time those limits are changed. Nevertheless, it’s easy to forgot updating the name at some point, and that will cause some moments of anxiety to the future developer.&lt;/p&gt;

&lt;p&gt;We can see that a more abstract concept is arising: the idea that there is a period of time (&lt;em&gt;overnight&lt;/em&gt;) that requires a special treatment and that is defined by certain time limits. Probably, when talking about this feature with the business people or users, someone asked something similar to “What if the appointment is cancelled &lt;em&gt;overnight&lt;/em&gt;?“. As programmers we need to define that with some kind of temporal marks, but the concept itself isn’t tied to a precise hour range.&lt;/p&gt;

&lt;p&gt;We could express it this way:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, we have a more expressive name that doesn’t depend on implementation details. The concept &lt;em&gt;overnight&lt;/em&gt; is an abstraction easier to understand than an arbitrary time interval, so its name doesn’t need to change frequently.&lt;/p&gt;

&lt;p&gt;This very same idea can be applied to function and method names.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to rename things
&lt;/h3&gt;

&lt;p&gt;You can use the tools provided by your IDE, taking into account some safety measures.&lt;/p&gt;

&lt;p&gt;You will have no problems when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;changing a variable name inside a method or function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;changing a private method name inside a class.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most IDEs offer the rename refactor. At the end, it’s a &lt;em&gt;find and replace&lt;/em&gt;, but IDEs can work at the syntax tree level, so it is usually more precise than us to complete this refactor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9zi9zt2w8byq6bkd062.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9zi9zt2w8byq6bkd062.png" alt="The refactor menu in PHPStorm" width="287" height="353"&gt;&lt;/a&gt;&lt;em&gt;The refactor menu in PHPStorm&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For example, in the JetBrains IDEs you select the variable or function you want to rename, select Refactor, then Rename (caps-F6), type the new name, and you’re done.&lt;/p&gt;

&lt;p&gt;If you change a public function name, you may find that there are a lot of places affected, so we will use a more conservative approach.&lt;/p&gt;

&lt;p&gt;Let’s see an example: Imagine that you have this method in a class that you want to rename because its current naming doesn’t help too much. Sure, it’s funny, but a bit misleading. Nevertheless, the method is called in a lot of places, so a bulk rename involves risks:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;First of all, &lt;strong&gt;duplicate the function and rename the copy&lt;/strong&gt;. All calls to the original name are preserved and now you have a method with the better name that has the very same behavior.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, delete the body of the old function and write a call to the new one instead. This way, you avoid the code duplication without hurting existing uses of the old name.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Progressively, change the calls to the old method to the new one every time you find them and has something to do with your current task.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;When there are no calls to the old method remaining, delete it.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use *extract method *refactor provided by the IDE selecting the body of the method you want to rename and extracting it with the new name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the things had no name
&lt;/h3&gt;

&lt;p&gt;There is a well-known code smell called &lt;em&gt;magic numbers&lt;/em&gt;. It refers to primitive values that are present in the code but you don’t have any clue about what they mean.&lt;/p&gt;

&lt;p&gt;In the same line we have used in the previous example about the overnight period. the limits of this period are represented by numbers.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Those numbers were decided by business people. From the code point of view they are arbitrary values, and with the passing of time, their meaning can be forgotten. So, it is a good idea to give them names.&lt;/p&gt;

&lt;p&gt;The simplest way to do that is to convert these values into constants with a name:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;There are other potential improvements for this line, but we will leave them for future articles about refactoring for better design.&lt;/p&gt;

&lt;p&gt;This refactor can be applied to any arbitrary value you could find in a code fragment and needs explanation. But there are some more advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If those values change you don’t need to touch the code that uses them, lowering the chances to break something by accident.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If they are used in several places, you will have only one point to change them, guaranteeing consistency.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Break expressions into meaningful parts
&lt;/h3&gt;

&lt;p&gt;Complex expressions should be broken into parts in order to improve readability, but also to avoid potential errors. Tangled expressions with a lot of elements are a good place for bugs to hide, specially when we need to modify them.&lt;/p&gt;

&lt;p&gt;We can use the *extract variabl*e refactor, that consists of replacing part of a expression with a variable. A good rule of thumb is to apply this refactor to parts wrapped in brackets. Let’s see a typical example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Ok. This looks like a very simple expression, but it should help us to understand the intent of the refactor and how to proceed.&lt;/p&gt;

&lt;p&gt;The *&lt;em&gt;$this-&amp;gt;amount * vat **subexpression represents the concept of *tax or VAT amount&lt;/em&gt;. That concept could be expressed in a variable:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Now, the expression is easier to read. Imagine the same applied to more complex calculations.&lt;/p&gt;

&lt;p&gt;Sometimes, these kind of extractions reveal the need for a behavior, even for a public one:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;&lt;em&gt;Extract variable&lt;/em&gt; helps us abstracting concepts inside the scope of a method or function. &lt;em&gt;Extract method&lt;/em&gt;, instead, is useful when we identify public or reusable behaviors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explain conditional expressions
&lt;/h3&gt;

&lt;p&gt;Every conditional expression that is a combination of two or more single conditions is a good candidate to be encapsulated in a method with a name that provides a meaning.&lt;/p&gt;

&lt;p&gt;Imagine that we have a pricing schema that allow us to offer different prices to different age ranges.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;The conditions in this method apply the price for each age range. Let’s take a look at line 12. This condition expresses an age interval for adults. We could extract this to a method that explicitly states that idea:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Even single conditions could need to be extracted if they are not expressive enough or easy to understand. We can do the same for line 17:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;This is especially true when the condition is negated, because this kind of expression is more difficult to process when reading.&lt;/p&gt;

&lt;p&gt;Not to mention &lt;em&gt;negated negations&lt;/em&gt;. In that case, introduce a new method in the class being tested or a function that doesn’t need to be negated.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Introducing new methods is pretty cheap because they are not used in any other part of the code and you are not removing existing code. You can introduce progressively the use of the new code.&lt;/p&gt;

&lt;p&gt;By the way, we want to mention that this kind of boolean properties are usually best represented by positive names that are easy to process even when they are being negated. Nevertheless, you should pay attention to the business value of that property in order to choose the best name.&lt;/p&gt;

&lt;p&gt;We will back to conditional expressions in future articles about moving knowledge to the right place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactor to guard clause
&lt;/h3&gt;

&lt;p&gt;Conditionals can be tricky. And sometimes, in subtle ways. Take a look at this code, for example. It works perfectly, but can you spot the problem?&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;The problem is that the main responsibility of this method is under a condition. That makes the method name and body contradict each other in a way.&lt;/p&gt;

&lt;p&gt;As a general rule is better to check requisites first and &lt;em&gt;fail fast&lt;/em&gt; if they are not met. Or, as in this example, to &lt;em&gt;return early&lt;/em&gt; doing nothing.&lt;/p&gt;

&lt;p&gt;This kind of conditional is called a &lt;em&gt;guard clause&lt;/em&gt; and its purpose is to avoid invalid data to reach the main processing. Guard clauses are used to ensure preconditions are met before proceed with the job in that method. We can use assertions or throw an exception if we prefer to fail.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;h3&gt;
  
  
  Attack on the block
&lt;/h3&gt;

&lt;p&gt;Not only magic values need names. A lot of parts of the code will benefit from having good intent revealing names.&lt;/p&gt;

&lt;p&gt;In general, every cohesive code block could be extracted to a private method, giving it an expressive name. Let’s return to the &lt;em&gt;extract method&lt;/em&gt; refactor in a future post about long methods and classes, so we will pay attention for now to some refactor opportunities in which &lt;em&gt;extract method&lt;/em&gt; contributes to better understability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Body of loops&lt;/strong&gt;. It is a good practice to separate iterations from actions in loops. To do this, extract the body of the loop to its own method. You can see an example here:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Of course, in this example you could go functional, but take into account that this approach could be way more difficult to understand at first sight:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;&lt;strong&gt;Conditional legs.&lt;/strong&gt; This extraction can also be applied to the legs of conditionals, making them easier to understand at a higher level. You simply has to extract legs to private methods. Let’s consider this example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;We can extract the body of the true branch to its own method.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Now, we are hiding the details in the extracted method and can scan the conditional structure faster, digging deeper if we need.&lt;/p&gt;

&lt;p&gt;This also paves the way for applying further refactors. If you look at the code you will see that this part is pretty strange and it will need more work to get a better organization.&lt;/p&gt;

&lt;p&gt;Extracting the branches of conditionals to methods reduces the indentation level and makes it easier to tidy things.&lt;/p&gt;

&lt;h2&gt;
  
  
  To be continued…
&lt;/h2&gt;

&lt;p&gt;In this post your have seen some techniques that can help your code to be a better storyteller, for you and for future developers.&lt;/p&gt;

&lt;p&gt;In new posts we will talk about other areas of improvement. For example, how to deal with long classes and methods. Those long code blocks usually mix responsibilities and are difficult to manage and test.&lt;/p&gt;

&lt;p&gt;Also, we will address the problem of moving knowledge and responsibilities to their proper places, applying some well known design principles.&lt;/p&gt;

&lt;p&gt;Keep an eye on our blog for this series and other interesting posts.&lt;/p&gt;

</description>
      <category>refactoring</category>
    </item>
  </channel>
</rss>
