<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>función</title><link>https://devtia.com/</link><description>Llevamos empresas a la nueva era digital. Te ayudamos a mejorar tus procesos a través de las métricas.</description><language>es</language><pubDate>Sat, 04 Apr 2026 11:31:30 +0200</pubDate><lastBuildDate>Sat, 04 Apr 2026 11:31:30 +0200</lastBuildDate><generator>DEVTIA</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><ttl>3600</ttl><item><title>Las semanas previas</title><link>https://devtia.com/post/las-semanas-previas</link><description><![CDATA[<p>Todo proyecto exitoso debe empezar con un trabajo previo al comienzo del mismo, en aras de cumplir expectativas la organizaci&oacute;n previa del proyecto es primordial.</p><p>En DEVTIA otorgamos una gran importancia al transcurso de las semanas previas al comienzo. En ellas se deber&aacute;n realizar algunas acciones que ser&aacute;n determinantes. En esta fase, el &eacute;xito depender&aacute; de ambas partes, y en gran medida del esfuerzo que sea capaz de realizar el propio cliente.</p><div class="row"><div class="col-sm-4"><p>La idea es que el d&iacute;a que de comienzo tu proyecto tengamos una idea clara del objetivo del proyecto, abundantes detalles sobre la implementaci&oacute;n y los datos necesarios para realizar la carga inicial de informaci&oacute;n.</p><p>Asignaremos a tu proyecto a un experto que en base a la propuesta realizada comenzar&aacute; a solicitar informaci&oacute;n adicional. Posiblemente se realizar&aacute;n algunas <strong>sesiones conjuntas en las que se explicar&aacute;n los detalles de la soluci&oacute;n propuesta</strong> y en la que se tomar&aacute;n ciertas decisiones en conjunto con el cliente.</p></div><div class="col-sm-8"><p><img alt="semanas previas al comienzo del proyecto" loading="lazy" src="/uploads/content/image/image/33bad3b91b7e5b80fb59c6257dae3634428f40ac.png" title="semanas previas al comienzo del proyecto"></p></div></div><p><strong><a href="/post/nuestra-metodologia">Nuestra metodolog&iacute;a</a> exige que nuestros clientes se impliquen</strong>, y es en esta fase donde el cliente tiene una carga de trabajo m&aacute;s importante, as&iacute; que durante estas semanas puedes esperar que te pidamos algunas de estas tareas:</p><hr><h2><strong>An&aacute;lisis de la competencia</strong></h2><p>Un buen punto de partida para un <strong>proyecto de desarrollo de software</strong> es ver c&oacute;mo han resuelto otros tus mismos problemas. No se trata de copiar a la competencia, sino de obtener las mejores ideas de cada sitio y luego evolucionarlas seg&uacute;n corresponda. <strong>Nosotros somos expertos en el desarrollo de software pero no conocemos en profundidad tu negocio, por lo que nadie mejor que t&uacute; para hacer este an&aacute;lisis.</strong></p><h2><strong>Escribir documentaci&oacute;n</strong></h2><p>Necesitamos que pongas por escrito tus ideas. Entre otras cosas queremos saber:</p><ol><li>&iquest;Cu&aacute;l es la situaci&oacute;n actual?</li><li>&iquest;Cu&aacute;les son los objetivos del proyecto?</li><li>&iquest;Qu&eacute; niveles de acceso son necesarios?</li><li>&iquest;Cu&aacute;les son las principales funcionalidades?</li></ol><h2><strong>Recopilar datos</strong></h2><p>Necesitamos que nos facilites los datos iniciales que quieres que tenga tu proyecto. Por ejemplo, si partimos de una aplicaci&oacute;n anterior, puedes ir trabajando con la base de datos previa para tener listos los datos para la nueva aplicaci&oacute;n.</p><h2><strong>Resolver dudas</strong></h2><p>Seg&uacute;n vayamos teniendo m&aacute;s informaci&oacute;n del alcance probablemente vamos a exponerte dudas, as&iacute; como sugerencias e ideas.</p><h2><strong>Sesiones</strong></h2><p>Independientemente de todo el trabajo anterior vamos a tener una o m&aacute;s sesiones de puesta en com&uacute;n, ya que nosotros creemos firmemente en la comunicaci&oacute;n directa.</p><p>En estas sesiones repasaremos contigo el trabajo que has realizado, as&iacute; como te expondremos el trabajo que hemos desarrollado nosotros. <strong>En equipo tomaremos las mejores decisiones para tu proyecto.</strong></p>
]]></description><guid>https://devtia.com/post/las-semanas-previas</guid><pubDate>Mon, 04 May 2020 11:39:00 +0200</pubDate></item><item><title>Maneja Intellij IDEA como una bestia: refactoring</title><link>https://devtia.com/post/maneja-intellij-idea-como-una-bestia-refactoring</link><description><![CDATA[<div class="alert alert-warning mt-5 mb-5" role="alert">En esta entrada mostramos los atajos por defecto de teclado disponibles para WINDOWS / LINUX. Consulta en la web de Jetbrains los atajos correspondientes a OSX.</div><p><strong>Intellij IDEA</strong> junto con el resto de "sabores" del mismo como es <strong>PHPStorm</strong>, <strong>PyCharm</strong>, <strong>WebStorm</strong> es considerado por muchos <strong>el mejor entorno de desarrollo integrado que existe en la actualidad</strong>.</p><p>Esta entrada forma parte de una serie en la que vamos a aprender a usar este editor de forma profesional, a trav&eacute;s del conocimiento de los diferentes atajos de teclado disponibles, as&iacute; como de otras funcionalidades muy &uacute;tiles para el d&iacute;a a d&iacute;a.</p><p>En esta entrada vamos a centrarnos en una de las caracter&iacute;sticas m&aacute;s potentes del IDE <strong>la refactorizaci&oacute;n</strong>.</p><h2>Recomendaciones previas</h2><p><strong>No es necesario que te aprendas todos el primer d&iacute;a.</strong> Te recomendamos elegir dos o tres atajos de teclado. Los que te parezcan m&aacute;s &uacute;tiles y los apuntes en un posit junto a la pantalla para que no se te olviden. Practica con ellos durante algunos d&iacute;as y una vez que los tengas dominados, contin&uacute;es tu aprendizaje con otro grupo de dos o tres.</p><p>El primer atajo de teclado que vamos a recomendarte en cada una de las entradas, es encontrar una acci&oacute;n.</p><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Encontrar una acci&oacute;n</h3><p><kbd>ALT</kbd> + <kbd>SHIFT</kbd> + <kbd>A</kbd></p><p>Con este atajo podemos lanzar el men&uacute; de selecci&oacute;n de acci&oacute;n. Este atajo es de los m&aacute;s &uacute;tiles ya que te muestra el atajo de la acci&oacute;n que est&aacute;s buscando por lo que puedes utilizarlo cuando no te acuerdes del atajo que quieres lanzar de forma que vas a poder aprenderlos poco a poco.</p></div><div class="col-sm-4"><img alt="encontrar una acci&oacute;n" class="img-fluid" loading="lazy" src="/uploads/content/image/image/d9cc3c9f067bd8216717c701ade184884bede663.gif"></div></div><h2>Refactorizaci&oacute;n con Intellij IDEA</h2><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Renombrar variable, funci&oacute;n, m&eacute;todo o clase</h3><p><kbd>SHIFT</kbd> + <kbd>F6</kbd></p><p>Con este atajo podemos renombrar una variable, una funci&oacute;n o m&eacute;todo o una clase. El entorno de desarrollo buscar&aacute; todas las apariciones de dicho elemento y los actualizar&aacute;. Si se trata de una clase cambiar&aacute; el nombre del fichero que lo contiene.</p></div><div class="col-sm-4"><img alt="renombrar variable, funci&oacute;n, m&eacute;todo o clase" class="img-fluid" loading="lazy" src="/uploads/content/image/image/c4e615b53f8a8c639453f093aabb7c205e8b27d2.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer constante</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>C</kbd></p><p>Con este atajo podemos extraer un valor a una constante de la clase.</p></div><div class="col-sm-4"><img alt="extraer constante" class="img-fluid" loading="lazy" src="/uploads/content/image/image/e840c4447b04571a32c26d5696e5a35aa99d3708.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer propiedad</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>F</kbd></p><p>Con este atajo podemos extraer un valor a una propiedad de la clase.</p></div><div class="col-sm-4"><img alt="extraer propiedad" class="img-fluid" loading="lazy" src="/uploads/content/image/image/59447d269e1d10833af7b01c2b04434c61272fc8.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer variable</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>V</kbd></p><p>Con este atajo podemos extraer un valor a una variable del m&eacute;todo.</p></div><div class="col-sm-4"><img alt="extraer variable" class="img-fluid" loading="lazy" src="/uploads/content/image/image/e2548704fe27c86eb6653f59f89ad908726b54d6.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer parametro</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>P</kbd></p><p>Con este atajo podemos extraer un valor a un par&aacute;metro del m&eacute;todo.</p></div><div class="col-sm-4"><img alt="extraer parametro" class="img-fluid" loading="lazy" src="/uploads/content/image/image/5c3804d89ebb582dea37ff8343c79864495d9779.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer m&eacute;todo</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>M</kbd></p><p>Con este atajo podemos extraer un bloque de c&oacute;digo a un m&eacute;todo de la clase. El entorno de desarrollo buscar&aacute; coincidencias del mismo fragmento de c&oacute;digo y nos ofrecer&aacute; reemplazarlas por el nuevo m&eacute;todo.</p></div><div class="col-sm-4"><img alt="extraer m&eacute;todo" class="img-fluid" loading="lazy" src="/uploads/content/image/image/40dcdc71ccdbc0dd74ebc15cbb4423c3e6cee0b4.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Cambiar la firma de un m&eacute;todo</h3><p><kbd>CTRL</kbd> + <kbd>F6</kbd></p><p>Con este atajo podemos cambiar la firma de un m&eacute;todo. El entorno de desarrollo buscar&aacute; todas las llamadas a ese m&eacute;todo y actualizar&aacute; el c&oacute;digo con la nueva firma.</p></div><div class="col-sm-4"><img alt="cambiar la firma de un m&eacute;todo" class="img-fluid" loading="lazy" src="/uploads/content/image/image/f90e90f2fd7bd0cb2d7d3c4ac7a33113ec7de473.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Mover una clase</h3><p><kbd>F6</kbd></p><p>Este atajo nos va a permitir mover una clase a otro directorio. El entorno de desarrollo actualizar&aacute; el namespace y buscar&aacute; todas las referencias y las actualizar&aacute; tambi&eacute;n.</p></div><div class="col-sm-4"><img alt="mover una clase" class="img-fluid" loading="lazy" src="/uploads/content/image/image/63acca6bd89952ba6f1721cee62d639f33520904.gif"></div></div><p class="mt2"></p>
]]></description><guid>https://devtia.com/post/maneja-intellij-idea-como-una-bestia-refactoring</guid><pubDate>Tue, 27 Oct 2020 12:44:27 +0100</pubDate></item><item><title>Código limpio: Pruebas automáticas</title><link>https://devtia.com/post/codigo-limpio-pruebas-automaticas</link><description><![CDATA[<p>En la &uacute;ltima entrada repasamos qu&eacute; deber&iacute;as tener en cuenta a la hora de realizar una correcta <a href="/post/codigo-limpio-gestion-de-errores">gesti&oacute;n de errores</a>. En esta entrada vamos a realizar una peque&ntilde;a intruducci&oacute;n a las pruebas autom&aacute;ticas, &iquest;qu&eacute; son? &iquest;para qu&eacute; sirven? y sobre todo que caracter&iacute;siticas tienen que tener para ser realmente &uacute;tiles.</p><h2>&iquest;Pruebas autom&aacute;ticas?</h2><p>Las pruebas o la bater&iacute;a de pruebas es un software que debe escribirse en paralelo a nuestro propio software y cuya responsabilidad es comprobar que nuestro software funciona correctamente. B&aacute;sicamente es <strong>software que prueba nuestro software</strong>.</p><p>Puede que intuitivamente pienses que construir una bater&iacute;a de pruebas, es trabajo adicional, ya que probablemente tardar&aacute;s m&aacute;s tiempo y por lo tanto tu trabajo ser&aacute; m&aacute;s caro para tus clientes pero esto no es as&iacute;.</p><p>Cuando tienes una buena bater&iacute;a de pruebas, la primera "derivada" es que vas a cometer menos errores. Menos errores supone menos tiempo arreglando errores y m&aacute;s tiempo programando. Adem&aacute;s supone menos frustraci&oacute;n en los usuarios.</p><p>La segunda derivada es que puedes hacer cambios con mayor confianza, y esto te hace ir m&aacute;s r&aacute;pido. Las pruebas detectar&aacute;n que has roto, y simplemente tendr&aacute;s que arreglarlo, un equipo con una buena bater&iacute;a de pruebas termina trabajando mucho m&aacute;s r&aacute;pido que un equipo que no las tiene.</p><p>Hay una tercera derivada y es que al desarrollar tu bater&iacute;a de pruebas te va a permitir dise&ntilde;ar mejor software, pero esto es algo en lo que profundizaremos en una entrada m&aacute;s adelante.</p><h2>&iquest;Si es tan bonito por que no todo el mundo hace pruebas autom&aacute;ticas?</h2><p>Cuando escribes pruebas debes ser tan cuidadoso c&oacute;mo con el c&oacute;digo de producci&oacute;n. Todo lo que hemos comentado ya en esta serie sobre elegir buenos <a href="/post/codigo-limpio-nombres">nombres</a>, <a href="/post/codigo-limpio-funciones">funciones</a>, <a href="/post/codigo-limpio-argumentos">argumentos</a>, etc son cosas que tambi&eacute;n aplican a las pruebas autom&aacute;ticas.</p><p>Escribir buenas pruebas requiere cierta t&eacute;cnica y ocurre que muchos programadores o equipos comienzan haciendo pruebas, y al no ser pruebas de calidad terminan teniendo una bater&iacute;a de pruebas que supone m&aacute;s un problema que una soluci&oacute;n. Tras esta mala experiencia desisten, por eso en esta entrada vamos a explicarte c&oacute;mo escribir buenas pruebas.</p><h2>Escribe tus pruebas "PRIMERO"</h2><p><img alt="Escribe pruebas r&aacute;pido" loading="lazy" src="/cache/thumb_1200_0400/uploads/content/image/image/dcc63077554f52edf72fcc4d5c15300636e851dc.jpg" title="Escribe pruebas r&aacute;pido"></p><p><em>FIRST</em> o PRIMERO es un acr&oacute;nimo que se utiliza para describir las caracter&iacute;sticas que deber&iacute;a tener una prueba.</p><h3><em>Fast</em> o R&aacute;pidas</h3><p>Una prueba, as&iacute; como la bater&iacute;a de pruebas a la que pertenecen deber&iacute;an ser tan r&aacute;pidas c&oacute;mo sea posible. &iquest;Cu&aacute;nto? Pues depende del tipo de prueba y del proyecto. El autor no se moja en este aspecto, pero en mi opini&oacute;n deber&iacute;as mantener cada prueba por debajo de 1 segundo.</p><h3><em>Independent</em> o independientes</h3><p>Unas pruebas no deber&iacute;an depender de otras. Esto te permitir&aacute; ejecutar aisladamente las pruebas que fallen en lugar de tener que ejecutar toda la bater&iacute;a o un conjunto de pruebas.</p><h3><em>Repeatable</em> o repetibles</h3><p>El resultado de una prueba deber&iacute;a ser siempre el mismo y no variar si la ejecutas varias veces. De esta forma, podr&aacute;s ejecutar una prueba una y otra vez, hasta que arregles el error. De lo contrario tendr&iacute;as continuamente errores que luego no vas a poder reproducir.</p><h3><em>Self-validating</em> o validaci&oacute;n autom&aacute;tica</h3><p>Lo que quiere decir esta regla es que las pruebas no deber&iacute;an depender de una revisi&oacute;n, por ejemplo que el resultado fuera un texto que luego necesitar&aacute;s leer para saber si es correcto o no. La prueba debe pasar o no pasar, no deber&iacute;a haber otro resultado posible.</p><h3><em>Timely</em> o en el momento correcto</h3><p>El autor introduce en este cap&iacute;tulo las reglas del ciclo de TDD: rojo, verde y refactorizaci&oacute;n, pero no quiero profundizar mucho sobre este aspecto aqu&iacute;, porque tendremos el libro "Test Driven Development: By Example" por en este blog y en el canal de youtube m&aacute;s pronto que tarde.</p><h2>Show me the code</h2><p>Supongamos que tenemos la clase rectangulo que vimos en el cap&iacute;tulo de <a href="/post/codigo-limpio-estructuras-vs-objetos">estructuras vs objetos</a>.</p><pre><code class="php">class Rectangle
{
    private $height;
    private $width;

    public function __construct(float $height, float $width)
    {
        $this-&gt;height = $height;
        $this-&gt;width = $width;
    }

    public function area(): float
    {
        return round($this-&gt;height * $this-&gt;height, 2);
    }
}</code></pre><p>Escribir una prueba es una tarea muy sencilla, normalmente es simplemente una clase que tiene que extender de alg&uacute;n tipo de framework de pruebas, as&iacute; es como se hace con <code>phpunit</code></p><pre><code class="php">class RectangleTest extends \PHPUnit\Framework\TestCase
{
    public function test_area()
    {
        $rectangle = new Rectangle(1.11, 2.22);
        $this-&gt;assertSame(2.46, $rectangle-&gt;area());
    }
}</code></pre><p>F&aacute;cil, pero &iquest;que ocurre al ejecutarlo?</p><div class="alert alert-danger" role="alert">Failed asserting that 1.23 is identical to 2.46.</div><p>Efectivamente hab&iacute;a un error en el c&oacute;digo. En este caso lo hemos introducido a posta, pero es un tipo de error que se puede cometer f&aacute;cilmente.</p><p>&iquest;Has visto cual?</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-pruebas-automaticas</guid><pubDate>Sat, 29 Aug 2020 11:32:50 +0200</pubDate></item><item><title>Código limpio: Estructuras vs objetos</title><link>https://devtia.com/post/codigo-limpio-estructuras-vs-objetos</link><description><![CDATA[<p>En esta entrada vamos a ver que son las estructuras de datos y los objetos, y que ventajas puede tener el uso de uno u otro tipo. Vamos a implementar exactamente lo mismo con las dos aproximaciones para ver c&oacute;mo se comporta cada una.</p><h2>Estructuras de datos</h2><p>Una estructura de de datos, es una clase o un objeto que expone la informaci&oacute;n que contiene. En el ejemplo hemos evitado el uso de getters y setter, pero ser&iacute;a exactamente lo mismo si los tuviera, lo consideraremos una estructura, porque expone su informaci&oacute;n. Otras clases ser&aacute;n responsables de realizar las funcionalidad que necesitemos.</p><pre><code class="php">class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        return $shape-&gt;radius * $shape-&gt;radius * Geometry::PI;
    }
}</code></pre><h2>Objetos</h2><p>Con el mismo ejemplo si usaramos el paradigma de la orientaci&oacute;n a objetos, debemos ocultar cu&aacute;l es la informaci&oacute;n y la estructura de los datos internos, y debemos exponer la funcionalidad que queremos.</p><pre><code class="php">class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this-&gt;radius * $this-&gt;radius * self::PI;
    }
}</code></pre><p>Aunque el ejemplo es muy sencillo nos permite entender la diferencia. Una estructura de datos es un elemento sencillo que solamente contiene informaci&oacute;n, mientras que la l&oacute;gica se encuentra en otras clases. Un objeto es una abstracci&oacute;n que expone su funcionalidad.</p><h2>A&ntilde;adir nuevas estructuras de datos / objetos</h2><p>Vamos a&ntilde;adir un nuevo tipo: <code>Rectangle</code> y vemos a ver que cambios son necesarios en cada aproximaci&oacute;n.</p><div class="row mb40 mt40"><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    public $topLeft;
    public $height;
    public $width;
}

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        if ($shape instanceof Rectangle) {
            return $shape-&gt;height * $shape-&gt;width;
        }

        if ($shape instanceof Circle) {
            return $shape-&gt;radius * $shape-&gt;radius * Geometry::PI;
        }

        throw new InvalidArgumentException();
    }
}</code></pre>
Estructura de datos</div><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    private $topLeft;
    private $height;
    private $width;

    public function area(): float
    {
        return $this-&gt;height * $this-&gt;width;
    }
}

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this-&gt;radius * $this-&gt;radius * self::PI;
    }
}





    </code></pre>
Orientado a objetos</div></div><p>&iquest;Que diferencia ves entre ambos?. En la primera aproximaci&oacute;n adem&aacute;s de a&ntilde;adir la nueva estructura, hemos tenido que modificar <code>Geometry::area()</code>. En el ejemplo es un cambio muy sencillo, pero nos sirve para identificar que cuando a&ntilde;adimos nuevas estructuras vamos a tener que revisar el c&oacute;digo que las utiliza para que sea capaz de manejar esta nueva estrucutra, lo cual nos puede llevar a a&ntilde;adir errores en un c&oacute;diugo que ya estaba funcionando.</p><p>En cambio en la aproximaci&oacute;n orientada a objetos no hemos tenido que modificar ni una coma del c&oacute;digo que ya ten&iacute;amos, y simplemente hemos tenido que a&ntilde;adir un nuevo objeto.</p><h2>A&ntilde;adir funcionalidad</h2><p>Vamos a a&ntilde;adir una nueva funcionalidad, el c&aacute;lculo del per&iacute;metro: <code>perimeter</code> a ver que tal se comporta cada una de nuestras implementaciones.</p><div class="row mb40 mt40"><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    public $topLeft;
    public $height;
    public $width;
}

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        if ($shape instanceof Rectangle) {
            return $shape-&gt;height * $shape-&gt;width;
        }

        if ($shape instanceof Circle) {
            return $shape-&gt;radius * $shape-&gt;radius * Geometry::PI;
        }

        throw new InvalidArgumentException();
    }

    public function perimeter($shape): float
    {
        if ($shape instanceof Rectangle) {
            return 2 * $shape-&gt;height + 2 * $shape-&gt;width;
        }

        if ($shape instanceof Circle) {
            return 2 * Geometry::PI * $shape-&gt;radius;
        }

         throw new InvalidArgumentException();
    }
}</code></pre>
Estructuras de datos</div><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    private $topLeft;
    private $height;
    private $width;

    public function area(): float
    {
        return $this-&gt;height * $this-&gt;width;
    }

    public function perimeter(): float
    {
        return 2 * $this-&gt;height + 2 * $this-&gt;width;
    }
}

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this-&gt;radius * $this-&gt;radius * self::PI;
    }

    public function perimeter(): float
    {
        return 2 * self::PI * $this-&gt;radius;
    }
}








    </code></pre>
Orientado a objetos</div></div><p>En este caso, nuestra primera aproximaci&oacute;n ha sido m&aacute;s sencilla. No hemos tenido que tocar el c&oacute;digo que ya ten&iacute;amos funcionando y s&oacute;lo hemos tenido que a&ntilde;adir una nueva funci&oacute;n.</p><p>En cambio en la aproximaci&oacute;n orientada a objetos hemos tenido que tocar cada una de las clases que ten&iacute;amos para a&ntilde;adirles esta funcionalidad. El ejemplo es muy sencillo, pero nos sirve para identificar el problema.</p><h2>Conclusiones</h2><p>A nivel de l&iacute;neas de c&oacute;digo no existe una diferencia sustancial. Tampoco es razonablemente m&aacute;s sencillo de implementar o de mantener una u otra aproximaci&oacute;n, pero cuando utilizamos estructuras, tendremos cierta facilidad cuando queramos a&ntilde;adir nueva funcionalidad, mientras que cuando utilizamos objetos tendremos facilidades para a&ntilde;adir nuevos tipos.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-estructuras-vs-objetos</guid><pubDate>Sun, 19 Jul 2020 15:29:55 +0200</pubDate></item><item><title>Código limpio: Deja de comentar</title><link>https://devtia.com/post/codigo-limpio-deja-de-comentar</link><description><![CDATA[<p>En esta entrada vamos a analizar cu&aacute;ndo y c&oacute;mo es conveniente el uso de comentarios durante la fase de desarrollo de software.</p><h2>bloques de c&oacute;digo</h2><p>Por regla general <strong>deber&iacute;as evitar el uso de comentarios</strong>. Los comentarios pueden parecer una herramienta muy &uacute;til ya que cuando estamos desarrollando puede que nos encontremos con que queremos explicar mejor c&oacute;mo funciona nuestro c&oacute;digo o por que hemos tomado una determinada decisi&oacute;n.</p><p>Pero cuando usamos un comentario de c&oacute;digo donde antes ten&iacute;amos un problema: que nuestro c&oacute;digo no era suficientemente sencillo de entender, ahora tenemos dos: nuestro c&oacute;digo sigue sin entenderse con facilidad, y ahora adem&aacute;s debo ocuparme de mantener los comentarios que he realizado.</p><p>Puedes pensar que mantener ese comentario no es mucho trabajo, pero que tal si esa energ&iacute;a qu&eacute; has gastado en mantener un comentario la hubieras gastado en <strong>hacer algo m&aacute;s productivo</strong>. &iquest;mejor, no?.</p><p>Vemos un ejemplo con el <code>TokenStorage</code> del componente <code>Security</code> de <code>Symfony</code>.</p><pre><code class="php">namespace Symfony\Component\Security\Core\Authentication\Token\Storage;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Contracts\Service\ResetInterface;

/**
 * TokenStorage contains a TokenInterface.
 */
class TokenStorage implements TokenStorageInterface, ResetInterface
{
    // ..
    public function setToken(TokenInterface $token = null)
    {
        if ($token) {
            // ensure any initializer is called
            $this-&gt;getToken();
        }

        $this-&gt;initializer = null;
        $this-&gt;token = $token;
    }
    //..
}</code></pre><p>El autor de este c&oacute;digo necesitaba hacer algunas llamadas antes de configurar el token. Se dio cuenta que que no era sencillo de entender y que otra persona que viniera detr&aacute;s probablemente no entender&iacute;a el motivo de esa llamada, asi que utiliz&oacute; un comentario para aclararlo todo. &iquest;Pero que te parece si hubiera hecho esto?</p><pre><code class="php">namespace Symfony\Component\Security\Core\Authentication\Token\Storage;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Contracts\Service\ResetInterface;

/**
 * TokenStorage contains a TokenInterface.
 */
class TokenStorage implements TokenStorageInterface, ResetInterface
{
    // ..
    public function setToken(TokenInterface $token = null)
    {
        if ($token) {
            $this-&gt;callInitializer();
        }

        $this-&gt;initializer = null;
        $this-&gt;token = $token;
    }

    private function callInitializer(): void
    {
        $this-&gt;getToken();
    }
    //..
}</code></pre><p>Como ves <strong>hemos eliminado el comentario y hemos mantenido e incluso mejorado la expresividad</strong>, ya que el c&oacute;digo es igual de expl&iacute;cito. Por lo tanto siempre que nos veamos en la necesidad de a&ntilde;adir comentarios, debemos plantearnos si podr&iacute;amos mejorar la expresividad de nuestro c&oacute;digo mejorando los nombres que estamos utilizando o extrayendo m&eacute;todos como en el ejemplo.</p><h2>DocBlock y documentaci&oacute;n autogenerada</h2><p>Algunos editores de texto a&ntilde;aden documentaci&oacute;n de forma m&aacute;s o menos autom&aacute;tica que permite indicar los tipos de los argumentos y del valor devuelto, as&iacute; como informaci&oacute;n adicional.</p><p>Veamos un ejemplo</p><pre><code class="php">    /**
     * get movements
     *
     * @return array
     */
    public function getMovements()
    {
        return $this-&gt;movements;
    }</code></pre><p>Este tipo de documentaci&oacute;n <strong>no aporta ning&uacute;n valor adicional</strong> ya que simplemente est&aacute; repitiendo lo mismo que ya dice el nombre de la funci&oacute;n. Quiz&aacute; esto ten&iacute;a sentido antes de la versi&oacute;n 7, para ayudar al IDE en el autocompletado. Pero a partir de esta versi&oacute;n es mucho mejor eliminar estos comentarios y utilizar el tipado nativo del lenguaje.</p><pre><code class="php">   public function getMovements(): array
    {
        return $this-&gt;movements;
    }</code></pre><h2>Usos permitidos</h2><p>Por supuesto el autor contempla algunos casos en los que s&iacute; estar&iacute;a permitido y justificado el uso de comentarios.</p><h3>Licencias y textos legales</h3><p>Es un uso completamente justificado el incluir la licencia y otros textos legales en el encabezado de nuestros ficheros. Adem&aacute;s esto no supone ning&uacute;n trabajo adicional, por que es habitual utilizar sistemas que se encargan de a&ntilde;adir estos comentarios de forma autom&aacute;tica, y adem&aacute;s los IDE suelen ocultarlos por lo que no molestan.</p><h3>APIs</h3><p>Si est&aacute;s construyendo una api y probablemente quieras generar la documentaci&oacute;n a trav&eacute;s de alg&uacute;n sistema autom&aacute;tico, s&iacute; estar&iacute;a justificado el incluir toda esta informaci&oacute;n adicional en tu c&oacute;digo.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-deja-de-comentar</guid><pubDate>Fri, 17 Jul 2020 14:46:07 +0200</pubDate></item><item><title>Código limpio: El malvado argumento booleano</title><link>https://devtia.com/post/codigo-limpio-el-malvado-argumento-booleano</link><description><![CDATA[<p>En la entrada anterior revisamos la importancia de elegir buenos argumentos. En esta entrada vamos a centrarnos en entender los motivos de un tipo de argumento concreto. El argumento booleano. Para ello analizamos este tipo de argumentos en el c&oacute;digo del componente <code>Config</code> y el componente <code>Crawler</code> de <code>Symfony</code>.</p><h2>Confunden al lector</h2><p>En general los argumentos booleanos confunden al lector. Veamos un ejemplo, con una clase del componente <code>Config</code>. Esta clase en concreto es la responsable de buscar un fichero de un determinado nombre en un array de paths definidos en los que se podr&iacute;a encontrar.</p><pre><code class="php">namespace Symfony\Component\Config;

/**
 * FileLocator uses an array of pre-defined paths to find files.
 */
class FileLocator implements FileLocatorInterface
{
    // ..
    public function locate(string $name, string $currentPath = null, bool $first = true)
    {
        // ..
    }
   // ..
}</code></pre><p>&iquest;Sabr&iacute;as decirme qu&eacute; hace exactamente el tercer argumento? Quiz&aacute; tengas la siguiente intuici&oacute;n, si le pasamos <code>true</code> probablemente devolver&aacute; la primera coincidencia, pero &iquest;qu&eacute; ocurre si le paso un <code>false?</code>. Si realmente quiero entender esta funci&oacute;n voy a necesitar ir al c&oacute;digo y leer que es lo que hace.</p><p>Una vez que ya nos hemos le&iacute;do el c&oacute;digo entendemos que el funcionamiento es el siguiente:</p><ul><li>si le paso un <code>true</code>, me devolver&aacute; la primera coincidencia</li><li>si le paso un <code>false</code> me devolver&aacute; todas.</li></ul><p>&iquest;No se lo pondr&iacute;amos mucho m&aacute;s f&aacute;cil al lector si somos expl&iacute;citos de esta forma?</p><pre><code class="php">namespace Symfony\Component\Config;

/**
 * FileLocator uses an array of pre-defined paths to find files.
 */
class FileLocator implements FileLocatorInterface
{
    // ..
    public function locateAll(string $name, string $currentPath = null)
    {
        // ..
    }

    public function locateFirst(string $name, string $currentPath = null)
    {
        // ..
    }
   // ..
}</code></pre><p>Como siempre esto no significa que debamos duplicar el c&oacute;digo, ya que probablemente podemos encontrar la forma de que uno llame a otro o utilizar funciones privadas m&aacute;s peque&ntilde;as, para evitar cualquier duplicidad.</p><h2>Hace dos cosas</h2><p>En muchas ocasiones un argumento booleano indica que la funci&oacute;n hace dos cosas diferentes. Como ya vimos en la entrada dedicada a funciones, estas deber&iacute;an hacer una &uacute;nica cosa y deber&iacute;an hacerla bien.</p><p>Veamos un ejemplo con el componente <code>DomCrawler</code> de <code>Symfony</code>. Este componente permite crear un cliente que simula ser un navegador y que se utiliza en pruebas funcionales y para tareas de scrapping.</p><p>La funci&oacute;n que vamos a ver se llama despu&eacute;s de haber ejecutado una query sobre un documento HTML <code>$message = $crawler-&gt;filterXPath('//body/p')-&gt;text();</code></p><pre><code class="php">namespace Symfony\Component\DomCrawler;

/**
 * Crawler eases navigation of a list of \DOMNode objects.
 */
class Crawler implements \Countable, \IteratorAggregate
{
    // ..
    /**
     * Returns the text of the first node of the list.
     *
     * Pass true as the second argument to normalize whitespaces.
     */
    public function text(string $default = null, bool $normalizeWhitespace = true)
    {
        // ..
    }
    // ..
}</code></pre><p>&iquest;No da la sensaci&oacute;n de que el segundo argumento responde a una necesidad del programador que a una decisi&oacute;n de dise&ntilde;o? &iquest;Qui&eacute;n realizar&iacute;a un dise&ntilde;o as&iacute;? Obviamente el siguiente ejemplo es mejor.</p><pre><code class="php">namespace Symfony\Component\DomCrawler;

/**
 * Crawler eases navigation of a list of \DOMNode objects.
 */
class Crawler implements \Countable, \IteratorAggregate
{
    // ..
    public function text(string $default = null)
    {
        // ..
    }

    public function normalizeWhitespace(string $text)
    {
        // ..
    }
    // ..
}</code></pre><p>Y una vez realizado este dise&ntilde;o, &iquest;no parece evidente que <code>normalizeWhitespace</code> deber&iacute;a ir en otra clase? esto lo veremos m&aacute;s adelante en la entrada dedicada a las clases.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-el-malvado-argumento-booleano</guid><pubDate>Sun, 12 Jul 2020 13:31:24 +0200</pubDate></item><item><title>Código limpio: Argumentos</title><link>https://devtia.com/post/codigo-limpio-argumentos</link><description><![CDATA[<p>En esta entrada vamos a profundizar en la importancia que tienen los argumentos y ver algunas reglas para elegirlos y ordenarlos correctamente.</p><h2>N&uacute;mero de argumentos</h2><p>Seg&uacute;n el autor podr&iacute;amos clasificar las funciones o los m&eacute;todos seg&uacute;n el n&uacute;mero de argumentos posibles.</p><p><strong>Mon&aacute;dicaos</strong> tienen un s&oacute;lo argumento, son f&aacute;ciles de testear y f&aacute;ciles de entender. No hay lugar a dudas en cuanto a que es lo que hace y probablemente ser&aacute; bastante sencillo crear un test unitario para la misma. Siempre que podamos debemos basar nuestra api en este tipo de funciones.</p><p><strong>Di&aacute;dicos</strong> tienen dos argumentos. Siguen siendo relativamente f&aacute;ciles de entender, y probablemente seguir&aacute;n siendo f&aacute;ciles de testear.</p><p><strong>Triadicos </strong>son las que tienen tres argumentos, comienza a complicarse la forma de testear, aumenta la complejidad ciclom&aacute;tica o n&uacute;mero de "caminos" posibles dentro de la funci&oacute;n, y se complica recordar los argumentos y su orden. Este tipo de funciones deber&iacute;an evitarse si es posible.</p><p><b>Cuatro o m&aacute;s argumentos</b> Este tipo de funciones tienen todos los problemas que ya hemos comentado pero en mayor expotente. Veamos un ejemplo a continuaci&oacute;n la funci&oacute;n que ejecuta el filtro <code>localizeddate</code> de twig.</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $date,
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $locale = null,
    $timezone = null,
    $format = null,
    $calendar = 'gregorian'
) {
    //..
}</code></pre><p>Habitualmente usamos esta funci&oacute;n de esta forma lo que la hace muy c&oacute;moda de usar</p><pre><code class="twig">{{ item.date|localizeddate }}
{{ item.date|localizeddate('short', 'none') }} {# el uso de las constantes es muy c&oacute;modo en el d&iacute;a a d&iacute;a. #}
</code></pre><p>El problema viene cuando queremos pasarle un formato personalizado. Es muy dificil de recordar cuales son los argumentos, y tenemos que ir a la documentaci&oacute;n para comprobarlo. Adem&aacute;s los primeros dos argumentos son ignorados lo que la vuelve un poco confusa.</p><pre><code class="twig">{{ item.date|localizeddate('none', 'none', null, null, 'MMMM y') }}
{{ item.date|localizeddate('full', 'full', null, null, 'MMMM y') }} {# ambas hacen lo mismo. #}</code></pre><p>&iquest;Que os parece si cambiamos esta funci&oacute;n por dos? En la primera usaremos las constantes definidas para <code>$dateFormat</code> y <code>$timeFormat</code> mientras que en la segunda usaremos un formato personalizado.</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $date,
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $locale = null,
    $timezone = null,
    $calendar = 'gregorian'
) {
//..
}

function twig_localized_date_filter_by_format(
    Twig_Environment $env,
    $date,
    $customFormat = null,
    $locale = null,
    $timezone = null,
    $calendar = 'gregorian'
) {
//..
}</code></pre><p>Hemos reducido un poco el n&uacute;mero de argumentos y hemos facilitado su utilizaci&oacute;n, ya que el uso de la segunda funci&oacute;n quedar&iacute;a en algo parecido a esto.</p><p class="text-muted">Nota: Aunque todav&iacute;a son muchos argumentos el primero es injectado por <code>twig</code> asi que contar&iacute;a uno menos.</p><pre><code class="twig">{{ item.date|localizeddate('da_igual_lo_que_pongas', 'da_igual_lo_que_pongas', null, null, 'MMMM y') }} {# antes #}
  {{ item.date|localizeddate_by_format('MMMM y') }} {# mejor esta no? #}</code></pre><h2>Grupos de argumentos</h2><p>En el caso en el que tenemos una funci&oacute;n con un grupo de argumentos relacionados entre s&iacute;, <strong>podemos agruparlos todos ellos en una clase</strong> de forma que nuestro c&oacute;digo mejora en su legibilidad. Es lo que han hecho aqu&iacute;.</p><p>&iquest;Que os parece si crearamos una clase para almacenar la configuraci&oacute;n de la localizaci&oacute;n <code>$locale</code>, <code>$timezone</code>, <code>$calendar</code>. Es muy probable que este grupo de argumentos se repitan en otras funciones.</p><pre><code class="php">class Localization
{
    private $locale = null;
    private $timezone = null;
    private $calendar = 'gregorian';

    public function __construct($locale, $timezone, string $calendar)
    {
        $this-&gt;locale = $locale;
        $this-&gt;timezone = $timezone;
        $this-&gt;calendar = $calendar;
    }

}</code></pre><p>Con esto mejoramos mucho la legibilidad del c&oacute;digo, veamos c&oacute;mo quedar&iacute;an nuestras funciones anteriores</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $date,
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $localization
) {
//..
}

function twig_localized_date_filter_by_format(
    Twig_Environment $env,
    $date,
    $customFormat = null,
    $localization
) {
//..
}</code></pre><h2>Orden de argumentos</h2><p>A partir de este punto todo lo que vamos a ver no aparece en el libro, pero si que hemos querido completarlo con algunos criterios adicionales. Deber&iacute;as tener en cuenta las siguientes condiciones a la hora de elegir el orden de los argumentos.</p><ul><li>Los m&aacute;s importantes primero.</li><li>Utiliza el contexto para que el lector sepa cual es el primer argumento.</li><li>Trata de agrupar por tipos.</li><li>Argumentos opcionales al final.</li></ul><h2>Nombres</h2><p>Los nombres de tus argumentos son parte de tu funci&oacute;n. Trata de que s&eacute;an lo m&aacute;s expec&iacute;ficos posibles, ayuda al lector a entender tu funci&oacute;n y lo que pueda esperar de ella, a trav&eacute;s de esos nombres. &iquest;Que te parece este cambio?</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $dateTime, // $date no era fiel a la realidad, ya que en ese argumento no importa s&oacute;lo la fecha, si no tambi&eacute;n la hora.
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $localization
) {
//..
}</code></pre><h2>Tipado</h2><p>El &uacute;ltimo apartado que me gustar&iacute;a ver en esta entrada es el tipado. La regla es muy sencilla. Tipa siempre que puedas.</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    \DateTime $date,
    string $dateFormat = 'medium',
    string $timeFormat = 'medium',
    Localization $localization = null
) {
//..
}

function twig_localized_date_filter_by_format(
    Twig_Environment $env,
    \DateTime $date,
    string $customFormat,
    Localization $localization = null
) {
//..
}</code></pre><p>El tipado tambi&eacute;n forma parte de tu funci&oacute;n, quiz&aacute; no tengas claro que es exactamente <code>$localization</code>, pero el tipado te ayuda a entender que se trata de una clase.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-argumentos</guid><pubDate>Sat, 04 Jul 2020 09:56:47 +0200</pubDate></item><item><title>Código limpio: Funciones</title><link>https://devtia.com/post/codigo-limpio-funciones</link><description><![CDATA[<p>En esta entrada vamos a ver qu&eacute; caracter&iacute;sticas tienen o no tienen que tener <strong>nuestras funciones</strong> para que puedan considerase <strong>c&oacute;digo limpio</strong>. Para ello vamos a analizar el componente Lock de Symfony por ser un componente bastante sencillo de entender. Este componente crea y administra bloqueos, un mecanismo para proporcionar acceso exclusivo a un recurso compartido al m&aacute;s puro estilo de los sem&aacute;foros en el lenguaje de programaci&oacute;n c.</p><h2>Hacer una &uacute;nica cosa</h2><p>Una funci&oacute;n solo deber&iacute;a hacer una &uacute;nica cosa, y adem&aacute;s deber&iacute;a de ser ser capaz de hacerla bien. Este concepto a la vez que es sencillo de entender es bastante complejo de explicar, as&iacute; que vamos a definir algunas caracter&iacute;sticas que pueden indicarte que una de tus funciones hacen m&aacute;s de una cosa.</p><h3>Demasiado larga</h3><p>Si una funci&oacute;n tiene <strong>demasiadas l&iacute;neas de c&oacute;digo</strong>, es muy probable que est&eacute; haciendo varias cosas a la vez. Deber&iacute;as tratar de encontrar bloques dentro de esa misma funci&oacute;n que hagan una cosa concreta e ir extrayendolas en funciones m&aacute;s peque&ntilde;as.</p><p>El l&iacute;mite exacto para considerarse demasiado larga es un poco difuso, pero por encima de 20 l&iacute;neas deber&iacute;as al menos plantearte si tu funci&oacute;n es demasiado larga.</p><h3>Secciones</h3><p>Como continuaci&oacute;n a lo anterior, <strong>si una funci&oacute;n puede dividirse en secciones</strong>, nos encontramos ante un caso claro de que esa funci&oacute;n hace m&aacute;s de una cosa.</p><p>A continuaci&oacute;n vemos la funci&oacute;n <code>Lock::acquire</code></p><pre><code class="php">/**
 * {@inheritdoc}
 */
public function acquire(bool $blocking = false): bool
{
    try {
        if ($blocking) {
            if (!$this-&gt;store instanceof BlockingStoreInterface) {
                throw new NotSupportedException(sprintf('The store "%s" does not support blocking locks.', get_debug_type($this-&gt;store)));
            }
            $this-&gt;store-&gt;waitAndSave($this-&gt;key);
        } else {
            $this-&gt;store-&gt;save($this-&gt;key);
        }

        $this-&gt;dirty = true;
        $this-&gt;logger-&gt;info('Successfully acquired the "{resource}" lock.', ['resource' =&gt; $this-&gt;key]);

        if ($this-&gt;ttl) {
            $this-&gt;refresh();
        }

        if ($this-&gt;key-&gt;isExpired()) {
            try {
                $this-&gt;release();
            } catch (\Exception $e) {
                // swallow exception to not hide the original issue
            }
            throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $this-&gt;key));
        }

        return true;
    } catch (LockConflictedException $e) {
        $this-&gt;dirty = false;
        $this-&gt;logger-&gt;notice('Failed to acquire the "{resource}" lock. Someone else already acquired the lock.', ['resource' =&gt; $this-&gt;key]);

        if ($blocking) {
            throw $e;
        }

        return false;
    } catch (\Exception $e) {
        $this-&gt;logger-&gt;notice('Failed to acquire the "{resource}" lock.', ['resource' =&gt; $this-&gt;key, 'exception' =&gt; $e]);
        throw new LockAcquiringException(sprintf('Failed to acquire the "%s" lock.', $this-&gt;key), 0, $e);
    }
}</code></pre><p>Podemos observar 3 secciones principales. En la primera trata de adquirir el bloqueo, en la segunda comprueba si el bloqueo deber&iacute;a haber expirado y en el &uacute;ltimo bloque realiza el manejo de errores. Podr&iacute;amos facilitarle mucho la vida al lector si extrajeramos esos bloques dej&aacute;ndolos en algo similar a esto.</p><pre><code class="php">/**
 * {@inheritdoc}
 */
public function acquire(bool $blocking = false): bool
{
    try {
        $this-&gt;tryAcquire($blocking);
        $this-&gt;releaseIfExpired();

        return true;
    } catch (\Exception $e) {
        return $this-&gt;handleException($e);
    }
}</code></pre><h3>switch</h3><p>Un bloque switch es probablemente un indicador de que <strong>una funci&oacute;n hace varias cosas</strong>. La forma de solucionar esto es extraer ese bloque switch para que la &uacute;nica cosa que haga nuestra funci&oacute;n es decidir qu&eacute; hacer en funci&oacute;n del valor que le pasemos a switch.</p><p>A continuaci&oacute;n vemos un caso v&aacute;lido de un bloque switch dentro de la clase <code>Store\StoreFactory</code>, ya que aunque tiene un elemento switch la funci&oacute;n hace una &uacute;nica cosa, en este caso devolver la instancia de <code>Store\PersistingStoreInterface</code> correcta en funci&oacute;n del tipo de conexi&oacute;n que estemos utilizando.</p><pre><code class="php">/**
     * @param \Redis|\RedisArray|...
     *
     * @return PersistingStoreInterface
     */
    public static function createStore($connection)
    {
        if (!\is_string($connection) &amp;&amp; !\is_object($connection)) {
            throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be [...], "%s" given.');
        }

        switch (true) {
            case $connection instanceof \Redis:
            case $connection instanceof \RedisArray:
            case $connection instanceof \RedisCluster:
            case $connection instanceof \Predis\ClientInterface:
            case $connection instanceof RedisProxy:
            case $connection instanceof RedisClusterProxy:
                return new RedisStore($connection);
            // en total hay 32 casos diferentes.
        }

        throw new InvalidArgumentException(sprintf('Unsupported Connection: "%s".', $connection));
    }</code></pre><h2>Un s&oacute;lo nivel de abstracci&oacute;n</h2><p>Dentro de una misma funci&oacute;n debemos mantenernos siempre dentro del mismo nivel de abstracci&oacute;n.</p><p>Por ejemplo si tuvi&eacute;ramos una capa de dominio de negocio y una capa de persistencia de datos, dentro de una misma funci&oacute;n <strong>no deber&iacute;amos pasar de un nivel a otro</strong>, por lo cual tendr&iacute;amos funciones con l&oacute;gica de negocio y funciones con l&oacute;gica de persistencia de datos.</p><h2>Procesamiento de errores</h2><p><img alt="procesamiento de errores" loading="lazy" src="/cache/thumb_1200_0400/uploads/content/image/image/cacadb988f34f4c8084fcdecd5efc0ff23e25a7e.jpg" title="procesamiento de errores"></p><p>Empezaremos indicando que el procesamiento de errores es mucho m&aacute;s sencillo <strong>a trav&eacute;s de excepciones</strong> que de c&oacute;digos de error, pero esto lo veremos mucho m&aacute;s en profundidad en el cap&iacute;tulo de procesamiento de errores.</p><p>El problema que tienen los bloques <code>try / catch</code> es que dificultan mucho la lectura.Si tenemos una funci&oacute;n donde adem&aacute;s de la l&oacute;gica de la funci&oacute;n tiene uno o m&aacute;s bloques <code>try / catch</code>, probablemente deber&iacute;amos extraer la parte de la l&oacute;gica de negocio y dejar esa funci&oacute;n s&oacute;lamente con los bloques try catch tal y c&oacute;mo hicimos con la funci&oacute;n <code>Lock::acquire</code> en el ejemplo anterior.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-funciones</guid><pubDate>Wed, 03 Jun 2020 10:42:06 +0200</pubDate></item><item><title>¿Cómo documentamos?</title><link>https://devtia.com/post/como-documentamos</link><description><![CDATA[<p>Procurar que comprendas a la perfecci&oacute;n como cliente c&oacute;mo funciona la herramienta que desarrollaremos exclusivamente para tu empresa es algo a lo que, en DEVTIA, le damos la importancia que merece.</p><p>La mayor&iacute;a de las empresas de <strong>desarrollo de software</strong> optan por realizar un largo documento explicativo con <strong>informaci&oacute;n sobre la herramienta</strong>, algo as&iacute; como una enorme gu&iacute;a. Nosotros no creemos en este m&eacute;todo, documentamos de una manera totalmente distinta y que ha aportado gran valor a cada uno de nuestros clientes.</p><p>En este art&iacute;culo, te contaremos <strong>porqu&eacute; en DEVTIA no documentamos</strong>, y c&oacute;mo logramos obtener resultados mejores que redactando un enorme manual que nadie va a leer.</p><h2>&iquest;Por qu&eacute; no documentamos?</h2><p>Son tres simples motivos los que nos llevaron a tomar la decisi&oacute;n de no documentar, o al menos no de la manera tradicional:</p><ol><li>Recuerda la &uacute;ltima vez que compraste un electrodom&eacute;stico, &iquest;te le&iacute;ste la gu&iacute;a de instrucciones entera, o quiz&aacute;s, solo te concentraste en buscar c&oacute;mo se configuraba el wifi? Algo similar pasa con el desarrollo software, <strong>nunca o casi nunca se va leer un documento de estas caracter&iacute;sticas completamente, as&iacute; que decidimos no incluirlo</strong>. Consideramos que es una p&eacute;rdida de tiempo tanto para nosotros como para el cliente en caso de que decida leerlo.</li><li><strong>Desactualizaci&oacute;n</strong>. La documentaci&oacute;n se desactualiza casi al momento de realizarla y m&aacute;s cuando se trata de desarrollos que se encuentran en constante transformaci&oacute;n y en el que se incluyen funcionalidades que pueden cambiar el funcionamiento de la herramienta radicalmente.</li><li><strong>Cada cliente es un mundo</strong>. Si quisi&eacute;ramos realizar una documentaci&oacute;n para cada tipo de cliente tardar&iacute;amos semanas en hacerlo y si quisi&eacute;ramos hacer uno gen&eacute;rico no encontrar&iacute;amos la forma de que todos los clientes lograran mostrarse satisfechos con &eacute;l.</li></ol><h2>Entonces, &iquest;c&oacute;mo lo hacemos?</h2><p>Antes de explic&aacute;rtelo, nos gustar&iacute;a especificar que todo lo que vamos a exponer a continuaci&oacute;n, est&aacute; vivo y en constante cambio jugando el cliente un papel fundamental en ello.</p><h3>En tu idioma</h3><p>Cuando se trabaja con <strong>herramientas gen&eacute;ricas</strong> muchas veces ocurre que estas no est&aacute;n disponibles en el idioma del cliente. El usuario final no entiende completamente los mensajes de la plataforma y esto hace que se puedan producir errores o confusiones al existir t&eacute;rminos diferentes. Nosotros lo tenemos muy claro: <strong>absolutamente todo en el idioma del cliente</strong>.</p><h3>Tu aplicaci&oacute;n tu lenguaje</h3><p><strong>Utilizamos el lenguaje propio del cliente, que es un factor capital</strong>. Cada campo de la herramienta que vaya a ser utilizado en la empresa ha de ser f&aacute;cilmente reconocible para cualquiera de los trabajadores que la utilicen. Por ello, priorizamos el lenguaje que el cliente utiliza para denominar a cada uno de los campos, men&uacute;s y casillas que componen el sistema de la manera que m&aacute;s les convenga.</p><p>Por ejemplo, si en tu empresa cuando a una persona interesada en vuestro producto os deja sus datos de contacto lo denomin&aacute;is &ldquo;solicitud&rdquo;, aparecer&aacute; &ldquo;solicitud&rdquo; en cada uno de los mensajes y textos designadas para ello en lugar de cualquier otra denominaci&oacute;n como, por ejemplo,&ldquo;lead&rdquo; u "oportunidad" que es como se suele denominar de forma m&aacute;s generica. Esto hace que tu equipo se familiarice con la terminolog&iacute;a de la herramienta desde el primer d&iacute;a.</p><h3>Bloques de ayuda y men&uacute;s contextuales</h3><p>Creemos que el mejor sitio para almacenar la documentaci&oacute;n es la propia aplicaci&oacute;n. Por eso cada vez que un cliente nos hace una pregunta, del tipo, &iquest;c&oacute;mo funciona esto? &iquest;c&oacute;mo se calcula exactamente ese dato? se lanzan dos acciones. Adem&aacute;s de resolver la duda acudimos a la pantalla o pantallas que originaron la duda del cliente y completar la informaci&oacute;n all&iacute; mismo, generamos una leyenda que sirva como "<strong>gu&iacute;a de posibles problemas a resolver</strong>".</p><p>A&ntilde;adimos <strong>men&uacute;s contextuales</strong> o informaci&oacute;n en bloques de ayuda en los que, colocando el cursor del rat&oacute;n encima, <strong>aparecer&aacute; una breve descripci&oacute;n del elemento y que hemos considerado relevante</strong> como podr&iacute;a ser una determinada f&oacute;rmula, un nuevo t&eacute;rmino incluido en la herramienta o informaci&oacute;n acerca de un comportamiento. Veamos un ejemplo.</p><div class="row section section-with-image"><div class="col-sm-2"></div><div class="col-sm-8"><figure class="figure"><img alt="Al pasar el rat&oacute;n por encima aparece un pop up con las formulas" class="figure-img img-fluid rounded" loading="lazy" src="/cache/thumb_1200_0800/uploads/content/image/image/2af41478252b5f514310ab115b4d65d278815759.jpg"><figcaption class="figure-caption">Al pasar el rat&oacute;n por encima del icono de ayuda (i) aparece un globo con la f&oacute;rmula que se utiliza para calcular ese valor y la explicaci&oacute;n de la misma.</figcaption></figure></div><div class="col-sm-2"></div></div><h3>M&oacute;dulo de ayuda</h3><p>Es habitual que durante el desarrollo de una aplicaci&oacute;n el equipo de desarrollo genere diferentes esquemas, tablas, etc que les permitan tanto entender lo que tienen que hacer como para que el cliente los valide antes de ponerse manos a la obra. Muchas veces esta informaci&oacute;n tan importante se pierde en tu <strong>gestor de correo</strong> y con el paso de los meses o incluso de los a&ntilde;os es posible que nadie sepa donde est&aacute;.</p><p>Por eso nosotros implementamos dentro de un m&oacute;dulo de ayuda todos los esquemas y tablas que generamos durante el proceso de desarrollo. De esta forma se nos facilitan las siguientes tareas.</p><ol><li>Tenerlo siempre disponible.</li><li>Que no se pierda.</li><li>Mantenerlo f&aacute;cilmente actualizado. Si te piden un cambio, primero cambias el esquema y luego la propia aplicaci&oacute;n.</li></ol><div class="row section section-with-image"><div class="col-sm-6"><figure class="figure"><img alt="Matriz de roles y permisos" class="figure-img img-fluid rounded" loading="lazy" src="/cache/thumb_1200_0800/uploads/content/image/image/44fa05940434ae06977d539b50f9c10dee6b4feb.jpg"><figcaption class="figure-caption">En esta matriz de roles y permisos es muy sencillo comprobar que puede y que no puede hacer cada uno de los perfiles que se han definido.</figcaption></figure></div><div class="col-sm-6"><figure class="figure"><img alt="Al pasar el rat&oacute;n por encima aparece un pop up con las formulas" class="figure-img img-fluid rounded" loading="lazy" src="/cache/thumb_1200_0800/uploads/content/image/image/d332898fb9963e9901c7d9385cbb82a2eb6a9239.jpg"><figcaption class="figure-caption">En este esquema puede comprobarse f&aacute;cilmente los pasos para activar una tarjeta y que acciones ocurren en cada paso.</figcaption></figure></div></div><h3>Consistencia en la estructura</h3><p>Todos nuestros desarrollos suelen disponer de <strong>una estructura bastante similar que planificamos adhoc</strong>. Los listados de todos los elementos de la herramienta (contactos, facturas, miembros del equipo&hellip;etc) van a ser iguales en su funcionamiento para que no se produzcan confusiones y para que el aprendizaje de la herramienta se agilice entre los empleados. Lo mismo pasa con cada una de las fichas que forman parte del desarrollo.</p><p>Una vez que te familiarices con la estructura con la que trabajamos te ser&aacute; muy intuitivo entender nuevos m&oacute;dulos incluso aunque nadie te los explique.</p><h3>Men&uacute;s y breadcrumbs</h3><p>Creemos que es necesario utilizar <strong>men&uacute;s tanto horizontales como verticales que te acompa&ntilde;en en todo momento</strong> durante la utilizaci&oacute;n de nuestras <strong>soluciones digitales </strong>y que te permitan saber exactamente donde est&aacute;s y que opciones tienes disponibles.</p><h3>M&oacute;dulo de auto ayuda</h3><p><strong>El lenguaje propio utilizado en una empresa es un factor determinante para que se desarrolle una buena comunicaci&oacute;n</strong> entre empleados y para que, al empezar a utilizar una nueva herramienta, todos y cada uno de los componentes de la plantilla obtenga un nivel similar en la comprensi&oacute;n de su uso.</p><p>En todos nuestros desarrollos incorporamos un m&oacute;dulo al que llamamos de &ldquo;autoayuda&rdquo; al que tendr&aacute;n acceso todos los miembros del equipo. Este m&oacute;dulo consiste en que en cada pantalla, hay un bloque de ayuda que puede cambiar cualquier miembro del equipo con los permisos adecuados. Es una forma de que el cliente, pueda completar la informaci&oacute;n que nosotros ya hemos incluido en el sistema, sin la necesidad de que nosotros participemos en ese proceso.</p><div class="row section section-with-image"><div class="col-sm-2"></div><div class="col-sm-8"><figure class="figure"><img alt="Matriz de roles y permisos" class="figure-img img-fluid rounded" loading="lazy" src="/cache/thumb_1200_0800/uploads/content/image/image/4411317e2907e78c46b19aa59e91ea823e408154.jpg"><figcaption class="figure-caption">T&uacute; mismo puedes editar y completar los textos que aparecen en el bloque de ayuda de cada p&aacute;gina.</figcaption></figure><div class="col-sm-2"></div></div><p>Logramos as&iacute;, que adem&aacute;s de la ayuda que nosotros ofrecemos,<strong> los usuarios de las herramientas se retroalimenten del conocimiento de toda la plantilla.</strong></p></div>
]]></description><guid>https://devtia.com/post/como-documentamos</guid><pubDate>Wed, 17 Jun 2020 17:29:19 +0200</pubDate></item><item><title>¿Qué es un CRM?</title><link>https://devtia.com/post/que-es-un-crm</link><description><![CDATA[<p>En los tiempos que corren, dotar a los clientes de una compa&ntilde;&iacute;a de un trato exclusivo para hacerles ver que no son un n&uacute;mero m&aacute;s en la base de datos es esencial para incentivar la compra de productos y servicios.</p><p>En Devtia, <strong>somos expertos en las plataformas CRM</strong>, que ayudan alcanzar este trato exclusivo. En este art&iacute;culo te contamos todo lo que debes saber sobre estas plataformas que han revolucionado el mundo empresarial.</p><h2>&iquest;Qu&eacute; es CRM?</h2><p>Un CRM (Customer Relationship Management) es una herramienta que a trav&eacute;s de la combinaci&oacute;n de pr&aacute;cticas, estrategias y ciertos procedimientos tecnol&oacute;gicos <strong>logran gestionar las interacciones y estad&iacute;sticas de clientes o leads a lo largo de su relaci&oacute;n con la empresa</strong>. Uno de los principales objetivos del CRM es mejorar y optimizar estas relaciones, con el objetivo de hacer de un lead un cliente fidelizado.</p><p>Los sistemas CRM<strong> recopilan informaci&oacute;n y datos de clientes a trav&eacute;s de los distintos canales de los que la empresa dispone</strong> tales como sitio web, redes sociales, correo electr&oacute;nico, tel&eacute;fono, whatsapp y diversas estrategias de marketing como podr&iacute;a ser la publicidad en motores de b&uacute;squeda.</p><p>Un CRM b&aacute;sico ser&iacute;a aquel que:</p><ul><li>Ayuda a tener una base de datos segmentada</li><li>Consultar ventas</li><li>Consultar la comunicaci&oacute;n existente entre empresa y clientes e incluso o</li><li>Automatizar procesos.</li></ul><p>En las versiones m&aacute;s avanzadas se puede hacer &ldquo;magia&rdquo; utilizando t&eacute;cnicas de predicci&oacute;n para acabar sugiriendo nuevos productos que encajen con el perfil del cliente (tal y como hace, por ejemplo, Amazon). Adem&aacute;s de a&ntilde;adir numerosas funciones adicionales que hacen que un CRM se vuelva mucho m&aacute;s &uacute;til.</p><h2>Beneficios de un CRM</h2><ol><li><strong>Mejora la informaci&oacute;n del negocio.</strong></li><li><strong>Automatizaci&oacute;n de tareas.</strong></li><li><strong>Anal&iacute;tica de los datos e informes disponible en pocos clics.</strong></li><li><strong>Aumento de la eficiencia y reducci&oacute;n de tiempos.</strong></li><li><b>Comunicaci&oacute;n entre departamentos m&aacute;s satisfactoria.</b></li></ol><h2>Caracter&iacute;sicas de un CRM</h2><p><img alt="componentes" loading="lazy" src="/cache/thumb_1200_0200/uploads/content/image/image/8663a76e754a71a4295327fbe26089d9154e02a4.jpg"></p><p>Para que una herramienta CRM sea capaz de obtener el m&aacute;ximo rendimiento en cada una de las acciones que se realicen en ella, deber&iacute;a estar formado por los siguientes componentes:</p><ul><li><strong>Gesti&oacute;n de tu base de datos de contactos: </strong>Es la raz&oacute;n de ser de un CRM, el elemento principal. Con este tipo de herramientas tendr&aacute;s una interfaz donde podr&aacute;s acceder a todos los contactos que se ha considerado que deben aparecer en &eacute;l. Tambi&eacute;n se podr&aacute; consultar cualquier dato referente al contacto; nombre, apellidos, correo, n&uacute;mero de tel&eacute;fono...etc.</li><li><strong>Gesti&oacute;n de llamadas, correos y tareas: </strong>a trav&eacute;s del propio CRM y sin necesidad de utilizar herramientas externas, se podr&aacute;n programar correos a enviar al contacto, agendar una llamada pendiente de hace tiempo, gestionar las tareas que se han de realizar con este contacto y cualquier otra acci&oacute;n que facilite la comunicaci&oacute;n y la eficiencia. El resumen de las acciones pendientes del cliente podr&aacute;n verse en el escritorio de la herramienta, facilitando la gesti&oacute;n al personal de la empresa.</li><li><strong>Hist&oacute;rico de interacciones con el cliente: </strong>toda acci&oacute;n que se realice quedar&aacute; registrada en el programa (llamadas, si coje el tel&eacute;fono, compra de producto, actitud del cliente ante determinadas ofertas, c&oacute;mo se obtuvo ese contacto...). De esta manera, todo el equipo tendr&aacute; acceso a todas las acciones que se han realizado con un determinado cliente y se facilitar&aacute; el orden y el control de la empresa. La gesti&oacute;n entre compa&ntilde;eros ser&aacute; mucho m&aacute;s eficiente con esta caracter&iacute;stica del CRM.</li><li><strong>Propuestas personalizadas: </strong>La capacidad de crear ofertas con un distinto grado de personalizaci&oacute;n dependiendo del tipo de cliente, siendo enviadas con un solo clic, es una de las grandes ventajas que pueden ofrecer los CRM. No todos tienen capcidad para realizar este tipo de propuestas, pero los que Devtia realiza, a no ser que el cliente indique lo contrario, lo tendr&aacute;n.</li><li><strong>Vinculado al departamentos importantes de la empresa: </strong>Cualquier registro o movimiento que el cliente realice llegar&aacute; a todas las &aacute;reas que est&eacute;n incluidas en el sistema. De esta manera, por ejemplo, un cliente que manifiesta su inter&eacute;s a trav&eacute;s de un comercial en adquirir un vuelo, ser&aacute; traspasado al departamento de producci&oacute;n para que finalice el proceso de adquisici&oacute;n del mismo.</li><li><strong>Informes y alertas: </strong>El reporting, necesario en cualquier &aacute;mbito empresarial, estar&aacute; muy presente en cada una de las herramientas del software, pudiendo acceder a datos relevantes al instante. Adem&aacute;s, en casi todos los CRM, se pueden programar determinadas alertas que sean trascendentes para la actividad comercial de la empresa; una llamada, una reuni&oacute;n de un departamento de la empresa o un d&iacute;a se&ntilde;alado en el calendario dada su importancia a la hora de lanzar una nueva campa&ntilde;a online, hay muchas opciones para personalizar estas notificaciones.</li><li><strong>Cuestionarios y encuestas de satisfacci&oacute;n: </strong>Saber y conocer a la perfecci&oacute;n cu&aacute;les son los factores que aumentan la satisfacci&oacute;n y c&oacute;mo de contentos se encuentran los consumidores con la relaci&oacute;n existente con la empresa, es primordial. Realizar encuestas y cuestionarios para conocer lo anterior, es un proceso que se puede realizar en la plataforma facilmente y que aportar&aacute; datos de gran importancia que podr&aacute;n servir de precedente para realizar cambios importantes.</li><li><strong>Geolocalizaci&oacute;n</strong>: algunos sistemas CRM incluyen servicios de geolocalizaci&oacute;n que ayudan a crear campa&ntilde;as de marketing basadas en ubicaciones f&iacute;sicas de las personas.</li><li><strong>Inteligencia artificial:</strong> aunque parezca futurista, <strong>la IA ya est&aacute; m&aacute;s que incorporada en los sistemas CRM</strong>. Ayuda a identificar patrones repetitivos de compra para, por ejemplo, predecir el futuro de su comportamiento y enviar ofertas seg&uacute;n sus movimientos.</li></ul><h2>Devtia y CRM</h2><p>Somos expertos totales en CRM, actualmente estamos trabajando con:</p><ul><li><strong>Salesforce: </strong>un software de CRM que ayuda a las empresas a conectarse con sus clientes utilizando la tecnolog&iacute;a cloud, de la que ya os habl&aacute;bamos en este art&iacute;culo. Es uno de los softwares m&aacute;s prestigiosos y con m&aacute;s renombre, adem&aacute;s de uno de los m&aacute;s utilizados.</li><li><strong>Hubspot: </strong>la herramienta de inbound marketing por excelencia. Esta herramienta ha adquirido gran popularidad entre los profesionales del marketing, aunque su rendimiento en cualquier &aacute;mbito es excelente.</li><li><strong>CRM desarrollados a medida seg&uacute;n las necesidades del cliente: </strong>como te hemos explicado en numerosos art&iacute;culos, este es nuestro producto estrella y uno de los motivos por lo que naci&oacute; nuestra empresa. Inf&oacute;rmate en estos art&iacute;culos;</li></ul>
]]></description><guid>https://devtia.com/post/que-es-un-crm</guid><pubDate>Thu, 12 Mar 2020 16:55:57 +0100</pubDate></item></channel></rss>