 Bueno, hola a todos. Os voy a hablar de que, bueno, de que todo es una trampa. Esta charla también es una trampa, ¿vale? No os voy a contar nada especialmente innovador, no os voy a dar ninguna bala de plata. Simplemente quiero hacer hincapié en algunas de las prácticas que se llevan haciendo muchos años y que muchas de ellas son realmente útiles y que se usan mucho menos de lo que debieran. Bueno, he separado la charla en dos partes, una donde hablo de un poco lo que es el mundo exterior, lo que es todo lo que nos depende de nosotros. Y otra parte que es luego nuestro dominio, nuestro cortijo, que es donde realmente podemos hacer lo que queramos y la he estructurado en una serie de consejos, una serie de prácticas que considero realmente útiles para evitar caer en esas trampas o no caer en esas trampas porque no podemos evitar caer en diferentes trampas, sino salir de ellas lo antes posible. Las trampas de las que hablo, por ejemplo, son cosas como yo estoy usando una biblioteca y de repente el mantenedor por el motivo que sea cambia de opinión, cambia de criterio y lleva la biblioteca por caminos que a mí no me interesan o la desmantiene o le pasa cualquier cosa al mantenedor y queda desmantenida la biblioteca. No solo pueden ser cambios del exterior, puede ser que de repente mi cliente, mi jefe, mi equipo decida que nuestras necesidades pasan a ser otras y esa biblioteca ya no cubre nuestras necesidades. Entonces, una de las cosas importantes es aislarse del mundo exterior y en la medida lo posible cuando las cosas cambien, que van a cambiar, estar preparado. El primer consejo es el uso de adaptadores para cualquier biblioteca que utilicemos o cualquier biblioteca de sea susceptible de cambio. Esto básicamente viene de la arquitectura hexagonal, del modelo de arquitectura hexagonal donde cualquier biblioteca de terceros, cualquier herramienta que pueda cambiar se aisla a través de un adaptador y lo llamamos siempre a través de ese adaptador. Un adaptador tiene diferentes ventajas. Si la biblioteca el día de mañana cambia o ya no me vale, puedo quitar esa biblioteca y simplemente cambiando el adaptador, puede usar cualquier otra biblioteca y el resto de mi código no debería percibir el cambio. También es muy interesante que muchas veces cuando usamos una biblioteca nos adaptamos a su interfaz, la biblioteca me da una serie de objetos, me da una serie de clases, me da una API, unos métodos que no tienen absolutamente nada que ver con mi modelo de negocio o con los datos con los que yo trabajo en mi arquitectura. Si nos acostumbramos a definir un adaptador para cada biblioteca que utilicemos o por lo menos para las bibliotecas más importantes que utilicemos podríamos definir la forma de trabajar con esas bibliotecas de una manera mucho más semántica. Y como digo, las bibliotecas se adaptan a las necesidades de nuestro modelo y no adaptamos nuestros modelos a las necesidades de la biblioteca. Otra cosa es que hay que tener cuidado con los adaptadores porque es muy fácil que permé estructuras de la biblioteca. Por ejemplo, puede ser relativamente fácil que acabemos devolviendo objetos o instancias de clases de la propia biblioteca. Por eso hay que tenerlo aislado para en el futuro poder cambiar ese adaptador por cualquier otra biblioteca y que funcione correctamente. El consejo número dos es establecer contratos para esos adaptadores. Tiene que estar muy definido cómo van a funcionar esos adaptadores para que en el futuro podamos cambiarlos. Yo tengo aquí una clase que realmente tiene cuatro métodos y llame a una biblioteca aquí detrás, pero no está claro ni qué hacen las funciones, ni qué deben devolver, ni qué parámetros deben recibir. Realmente ese adaptador no nos vale. Para poder definir los contratos, mi preferencia personal, esto es una cuestión de opinión, sería hacerlo a través de Tejo Unitario o Test de Integración. La idea es que yo ejecuto una batería de test y si la batería de test pasa, el adaptador es válido para mi arquitectura. Puedes definir test, puedes definir clases extractas, puedes escribir documentación sobre el contrato que debe cumplir. Como digo, mi preferencia son los tests, pero en principio la decisión es del equipo de desarrollo. Bueno, la ventaja de tener test, como digo es, si necesitamos sustituir en algún momento la biblioteca para cualquier desarrollador, aunque no ya está implicado en el proyecto, es muy, muy fácil hacer cumplir un contrato basado en test. Yo pongo esta biblioteca y estas son las funciones que tienen que funcionar y tienen que pasar estos tests. Yo los implemento y realmente es muy sencillo. Bueno, aislarse con... El consejo número 3 sería aislarse del mundo exterior con el concepto de repositorios. En principio, el concepto de repositorios se utiliza sobre todo para almacenamiento de datos, para aislarse principalmente de las bases de datos y consiste en un adaptador más donde realmente la complejidad suele ser bastante mayor porque ponemos una API con una semántica de mis datos, de qué datos voy a guardar, cómo los voy a guardar, de qué datos voy a guardar, de qué datos quiero obtener y realmente el repositorio implementa cómo se guardan esos datos. Ese repositorio, en los casos más habituales, podría ser, por ejemplo, una base de datos SQL con SQL Alchemy o el ORM de Django almacenando en un Postgre o un MySQL o SQLite, pero realmente da igual, porque realmente yo no defino la estructura de la base de datos, no defino necesariamente cómo se va a almacenar. Yo defino qué datos necesito o qué datos quiero obtener y qué datos quiero guardar y después, si es un Postgre, si es un Mongo, si es un servicio de Amazon, me da igual. Realmente no me importa mientras se cumpla la interfaz. Este mismo concepto se puede utilizar para API externas y cosas similares, cualquiera pide almacenamiento o cualquier sistema donde yo publique cosas, por ejemplo, podría usarse un modelo de repositorio. También es muy interesante que los repositorios sean plugables. La idea de un repositorio plugable es que si yo, por ejemplo, a día de hoy tengo una implementación en Postgre y quiero hacer una implementación en Mongo, me puedo escribir mi implementación entera en Mongo y comprobar a deshabilitar la implementación en Postgre y habilitar la implementación en Mongo. Esto es especialmente interesante si hacemos muchos tests porque podemos hacer una implementación en memoria, por ejemplo. No es nada difícil hacerse un repositorio que almacene datos en un diccionario en memoria en Python y realmente hace que los test vayan realmente rápido. No estoy hablando de un SQLite en memoria. No tiene nada que ver. Son órdenes de magnitud de diferencia en la velocidad. Evidentemente, una implementación de repositorio en memoria no se puede usar en producción, pero para ejecutar la batería de tests es muy útil. Como digo, el mismo concepto de establecer contratos para los adaptadores se aplica a los repositorios y establece contratos a los repositorios. El día de mañana, por el motivo que sea, aparece una necesidad nueva de cambiar de SQL a no SQL por el motivo que sea o alguna base de datos masiva tipo Cassandra. Lo puedes hacer, siempre y cuando el interfaz no cambie. Realmente, si tienes los contratos establecidos es muy fácil de implementar. Vuelta de lo mismo. Tienen que ser plugables. Deberían ser plugables. Es muy interesante la implementación de uno en memoria para pasar todos estos tests. Vale. Con esto, realmente nos estamos aislando de prácticamente cualquier biblioteca externa y cualquier acceso a datos que podamos tener como pueden ser bases de datos, APIs de terceros. Está todo aislado. Lo que queda es código nuestro. No estaríamos hablando de código de frameworks que estaría aislado. No estaríamos hablando de código de bibliotecas que estaría aislado. Solo tendríamos código de lógica de nuestro negocio o de lógica de presentación, pero código nuestro. Entonces, ¿qué hacemos con nuestro código? Vale, mi primer consejo es dividir vencerás. El código es bastante complejo de mantener. A medida que va pasando el tiempo, las cosas se van desmadrando. Normalmente las cosas no crecen como deberían crecer. Entonces mi idea es, el concepto es intentar dividir las aplicaciones en los conjuntos más pequeños que tengan sentido. No dividir por dividir, pero si vemos que nuestra aplicación tiene capas claramente diferenciadas. Por ejemplo, si yo tengo una API, una API REST, esa implementación de la API es una capa parte, es una aplicación aparte, es un componente aparte. Y ese componente debería estar completamente aislado del resto. Si yo tengo una interfaz web con plantillas de JINJA o lo que sea, esa interfaz web debería ser un componente aparte. El mi lógica de negocio, donde trabajo con las entidades de negocio, no debería tener nada que ver con la API REST o con la interfaz web. Todos esos componentes deberían estar separados y deberían conocerse solo a través de una API que no tiene por qué ser una API remota, no tiene por qué ser una API que vaya a través de REST o que esté claramente separada en diferentes procesos. Puede ser perfectamente la biblioteca de Python. Podemos estar llamando a esa API como funciones o métodos de clases. Pero lo importante es que esa API esté claramente separada y claramente dividida. Y luego, evidentemente, las necesidades de comunicación las definiremos con nuestras necesidades. Para ciertos proyectos, no necesito microservicios, pero este concepto permitiría a quien necesita microservicios o quien pueda gestionar microservicios hacerlo en ese formato. Y bueno, el resumen de esto es que un componente de 100.000 líneas de código que interactúan entre ellas, las 100.000 líneas de código es muchísimo más difícil de mantener que 10 componentes de 10.000 líneas de código que hacen una cosa concreta. Entonces, el resumen del Consejo de Dividido en CERAC es componentes pequeños y completamente aislados a ser posible. Vale. Consejo 6, TDD por lo menos hace test, por favor. El... En mi opinión, TDD es el caso ideal porque no... TDD no va a hacer test. TDD va de definir o de diseñar nuestra aplicación alrededor de los tests. Empezamos, partimos de los tests y vamos definiendo nuestra aplicación. La ventaja de esto es que nuestra aplicación es testeable. No sé si habéis intentado... Alguien ha intentado alguna vez añadir test en una aplicación que llevara dos años de desarrollo. Es un error. Normalmente no puedes testear la mayor parte de las cosas porque no están pensadas para hacer testeadas. Bueno, como decía antes se pueden implementar repositorios rápidos para los tests con repositorios basados en memoria y cosas similares. Se pueden moquear los adaptadores si es necesario. Hay veces que no es necesario moquearlos y, por supuesto, se pueden hacer muchísimos tests de integración, pero los tests de integración deberían ejecutarse siempre en un continuous integration. Un Jenkins, un Travis o lo que sea. Y bueno, simplemente eso. Intentar tener test de integración que se ejecuten en el servidor y que los tests de desarrollo normal vayan todo lo rápido que puedan porque si tardas tres minutos en pasar los tests de tu aplicación no vas a pasar los tests de tu aplicación normalmente. Vale, Clean Code. Yo creo que hay un libro, no sé si alguien conoce el libro Clean Code. Espero que sí. El libro Clean Code es un libro de Robert Martin. Es realmente un libro lleno de obviedades pero de obviedades que hasta que no las lees no te das cuenta de lo evidente que es. Y la verdad que mi recomendación es que cualquiera que quiera escribir código de calidad le eche un ojo al Clean Code por lo menos y intenta aplicar todas las normas que pueda o todas las reglas que pueda dentro de su equipo o introduciéndolas gradualmente empezando por unas pocas y ir añadiendo más, pero en cualquier caso son consejos muy buenos. Y bueno, en principio algunos de esos consejos de los que yo considero básicos los he puesto aquí. Por supuesto seguir unas reglas de estilo no tiene por qué ser Pepocho, si vuestro equipo prefiere otras reglas de estilo, está bien pero seguir algunas reglas de estilo en mi opinión para Python, Pepocho son más que correctas. El nombre de las variables, de las funciones de las clases, parece una viedad pero también tiene que tener sentido, muchas veces ponemos la clase y ponemos la variable A o la variable B y a eso le damos un valor o variables con iniciales que solo sabes qué significa en el momento en el que las escribe, cosas de este tipo. Las funciones deberían ser pequeñas incluso muy pequeñas a ser posibles y con pocos parámetros cuando yo veo una función que tiene 6 parámetros a mí me entra a urticaria, la combinatoria posible de 6 parámetros de entrada en una función es brutal es decir, eso es muy complicado de probar los casos por los que puede pasar. Y por supuesto esto viene de la programación funcional o algo muy habitual en la programación funcional es reducir al mínimo los efectos laterales como escrituras en base de datos, todo este tipo de cosas, las funciones la función ideal es una función pura en la que los valores de entrada, el valor de la salida solo depende de los valores de la entrada entonces eso es súper fácil de testear, es súper fácil de paralelizar tiene un montón de ventajas no es posible hacer ninguna aplicación sin efectos laterales sin escribir a base de datos o sin leer de base de datos sin escribir a pantalla y cosas similares pero si se puede reducir al mínimo y acotarlo a ciertas zonas de nuestra aplicación nada más. Vale, octavo y último consejo, cuidado con los frameworks los frameworks pueden o no ser la mejor solución para los problemas, en cualquier caso sean o no sean la mejor solución tienen la tendencia a colocarse en el centro de nuestra arquitectura en el centro de nuestra arquitectura debería estar nuestra lógica de negocio y los frameworks evidentemente el que desarrolla un framework el centro de su lógica de negocio es su framework evidentemente porque es el proyecto que están haciendo pero no tiene porque ser el centro de nuestra arquitectura, normalmente cuando tú construyes alrededor ponés en el centro algo y construyes alrededor estás atado a eso muchas veces nos atamos a frameworks como Django donde te dan un montón de cosas ya hechas y te definen una serie de buenas prácticas donde Django es el centro de nuestra aplicación si yo el día de mañana quiero prescindir del ORM de Django o quiero prescindir de alguna parte o algún componente de Django y usar el mío propio puede ser bastante doloroso también os digo que los frameworks que en general no se dejan llevar a los extremos de nuestra arquitectura por las buenas suelen ser relativamente complicado llevar frameworks como Django a los extremos encapsularlos en adaptadores encapsularlos en repositorios todo eso Django no está pensado así entonces no es fácil hay otros frameworks que probablemente los facilitan más como podría ser Flask hacer un micro framework probablemente sí sea más fácil encapsularlo y llevarlo a los extremos bueno y después de la verdad que he ido como un tiro he ido más rápido de lo que esperaba pero bueno bueno después de esto básicamente lo que os quería contar es que hay muchas prácticas hay muchas cosas que no se hacen que habitualmente nosotros empezamos a escribir código y empezamos a tirar líneas y que tenemos una biblioteca que decimos pues necesito hacer una query pues urlelib pues urlelib me da lo que necesito sí pues venga meto la llamada aquí en medio de todo mi código mi lógica de negocio tengo una llamada urlelib el día de mañana quiero meter request y tengo que ir buscando todas las llamadas urlelib y sacándolas otro lado todo ese tipo de cosas hacen que el día de mañana cuando quiera cambiar cosas las cosas den mucho más problemas el no tener test cuando yo quiero cambiar las cosas el día de mañana y mi aplicación sea grande voy a tener mucho más problemas el no tener un repositorio cuando quiera cambiar mi almacenamiento de datos por lo que sea voy a tener un problema todo este tipo de consejos lo que hacen es que dan un poco de más de trabajo para hoy no dan demasiado más dan un poco más de trabajo para hoy pero el día de mañana te ahorras mucha sorpresa y bueno como decía por donde seguir mi recomendación por donde seguir si os interesa todo este tipo de calidad del código y calidad del software sería echando un ojo a los principios solid son cuatro o cinco principios muy básicos de cómo hacer aplicaciones robustas y que aguanten bien el paso del tiempo el libro clean code de Robert Martin es un libro bastante a menos en realidad es está lleno de consejos que en principio parecen obvios pero como digo hasta que no te los ponen delante no te das cuenta de que hay que seguirlos el object oriental software de Bertrand Meyer también tiene un montón de información de cómo hacer software en el mundo de la orientación a objetos de calidad y bueno test driving development de Ken Beck que también es un libro muy interesante si os interesa el tema de TDD y nada hasta aquí ha ido muy rápido pero bueno preguntas? yo tengo una qué opinas por ejemplo cuando estabas hablando de repositorios qué opinas si tienes diferentes fuentes de datos yo que si tienes datos en S3 y luego tienes tus propios ficheros o lo que quieras tu opinión es que deberías crear un único adaptador para el repositorio si el modelo de datos al final o tendría que crear un adaptador para cada uno de los diferentes repositorios que tengo eso depende un poco es una decisión de diseño y en mi opinión dependería de qué datos se almacenas en cada lado y de cómo vas a acceder a esos datos si realmente tiene sentido yo tengo estas necesidades yo defino esta API del repositorio que para mí es una única API y lo que pasa es que todos los tipos de rendimiento unas cosas las guardan en S3 y otras en ficheros para mí es un único repositorio porque realmente estoy pidiendo exactamente los mismos datos si son dos tipos de datos claramente separados que se acceden a través de APIs diferentes si puede tener sentido que digas no tengo un repositorio para las fotos de avatar de los usuarios y tengo otro repositorio para los adjuntos por ejemplo eso podría tener sentido porque se guardan en diferentes sitios de datos depende un poco del caso y de sobre todo de la interfaz que quieras dar pues ya te digo si tu repositorio al final cuando construís un repositorio construís una interfaz de acceso a datos muy orientada a resolver tu problema y después por detrás ya verás como lo resuelves pero tú resuelves tu problema de almacenamiento de datos entonces hay veces que son dos hay veces que es uno gracias por allí había una pregunta también sobre el tema de las librerías ¿usted qué opinas de utilizar un virtual environment para aislar las versiones de librerías que alguien utiliza? vamos a ver la aislamiento de las bibliotecas como planteó yo es el concepto de tener bloques de código que actúan como frontera con las bibliotecas que utilice independientemente de si las instalan en el sistema o virtual environment yo soy muy fan de los virtual environments es decir, yo si todo lo que hago con python lo hago en virtual environment probablemente no sé qué partiquetes de python tengo instaladas en el sistema y me preocupa demasiado porque cualquier proyecto que hago con python lo hago en virtual environment con lo cual personalmente creo que es lo suyo porque si no si qué pasa que siempre tienes más de un proyecto o igual no a la vez pero sí que tienes más de un proyecto y de repente aparece un bus en un proyecto antiguo lo que sea y tienes que irte a ese proyecto y tienes que poder arrancarlo si tú siempre estás a la última versión de todo una cosa con yang 1.3 no va a funcionar con yang 1.8 te lo aseguro entonces tienes que tener instalado el paquete en concreto en cada virtual environment la mejor opción es virtual en más que paquetes del sistema seguro la pregunta es sobre los test hablas del mundo exterior y del mundo interior entonces los mocs para el mundo exterior es lo suyo en el mundo interior si no tienes problemas de performance moc o no moc es una cuestión de opinión los puristas dirían que si son tres unitarios la funcionalidad testeada por ese test debería estar moqueada yo, si no necesito si no me genera un problema de rendimiento si no si no moquear algo hace que el test no vaya más lento yo no moqueo por el simple hecho de que si no pierdo demasiado performance y a la vez estoy testeando otras partes de igual, a mí de igual donde salte la liebre lo que me importa es que salte la liebre me importa que aparezca el bug no si aparece el bug justo en el test que está fallando si el test que está fallando está fallando porque otra funcionalidad está mal y no lo he caza o con el otro test pues me va igual yo personalmente de hecho el concepto de moc para el modelo, para el repositorio yo por ejemplo prefiero no aplicarlo porque no sé si alguien intentó moquear el ORM de Django no lo hagáis es horroroso entonces tener que moquear ciertos tipos de cosas es muy complicado y hay veces que soluciones como un repositorio en memoria o una implementación fake aunque no necesariamente un moc sino un adaptador que me devuelve resultados un poquito fixate es casi mejor solución con moc y no tienes que estar metiendo en tu test, bloquecer código que explican que voy a decirle que me va a devolver para mi los mocs están muy bien pero los uso con mesura bueno, yo tengo una pregunta sobre la ley no me sale la palabra para leer más fácil el código que has comentado que en más adecuado hacer funciones más pequeñas con menos parámetros pero eso en proyectos grandes no implica es mucho más difícil seguir un poco no tener que haber un equilibrio en realidad en realidad puede que seguir el flujo puede parecerte más complicado pero eso se combina con otro tipo de técnicas como que las funciones solo deberían tener un alcance de su mismo nivel de abstracción entonces normalmente defines una función de más alto nivel que ejecuta una serie de acciones una serie de pasos de más bajo nivel esas acciones están encapsuladas en funciones que son de más bajo nivel que normalmente están a continuación en el código esas funciones de más bajo nivel a su vez pueden subdividirse en funciones más pequeñas y normalmente lo que te genera es que el problema si es si está muy arriba lo encuentras claramente y entiendes el código que estás leyendo y si está muy abajo tienes que escarbar pero en realidad es relativamente fácil escarbar lo que es infernal es escarbar es en un código donde yo tengo un método que tiene 6 parámetros y que tiene 140 líneas de código que tampoco es ninguna locura una función de 140 líneas de código no hay dios que la entiendan yo no la entiendo seguro reducir a funciones más pequeñas que te digan ahora estoy haciendo el pasito 1 ahora estoy haciendo el pasito 2 y ahora estoy haciendo el pasito 3 al final lo lees y dices ya sé lo que estás haciendo por lo menos sé lo que estás intentando hacer y ahora vamos a ver en qué paso estás fallando es cierto que puede complicar un poco la trazabilidad pero aumenta muchísimo la legibilidad y al final es un problema mucho mayor entender el código que trazarlo en mi opinión alguna otra pregunta pues muchas gracias a todos