Blogs

CESINF 2022

Otro año más que la organización del CESINF ha querido contar conmigo. Este año, he querido aportar mi granito de arena hablando de un tema transversal que se está normalizando cada vez más y he notado, hablando con amigos y conocidos, que no siempre lo llevamos de la mejor forma. ¿A que qué refiero? Al trabajo remoto. He preparado una sesión contando varios puntos que considero clave después de casi 5 años trabajando en remoto. He tenido la suerte de estar en una empresa Lean Mind la cual se ha basado desde sus inicios en el trabajo remoto. Hemos aprendido cómo trabajar en remoto a nuestro ritmo y no por obligación a causa de una pandemia mundial. En esta sesión he hablado de las reuniones, trucos para evitar el estrés mental y sobre todo he intentado dar una visión de ambas opciones: trabajo remoto y trabajo en oficina. Son dos opciones que tenemos y he intentado mostrar los puntos fuertes y no tan fuertes de cada una de ellas. No hay una mejor que la otra, son diferentes y cada una funciona mejor o peor dependiendo de la persona. Si quieres ver esta sesión, la he grabado y subido a mi canal de YouTube. Espero que te sea de ayuda y te haga reflexionar. Además, todos los recursos de la charla están públicos en GitHub. De todas formas te dejo los enlaces más importantes: Diapositivas Guión de la charla

Como controlo mi tiempo con Toggl

En estos días hemos estado hablando @ulisesantana y yo sobre Toggl. Una herramienta de tracking de tiempo que llevo usando ya unos cuantos años y se ha vuelto en mi día a día casi sin darme cuenta. Con esta aplicación puedo ver como uso mi tiempo. Principalmente, la uso para controlar las tareas que hago en las horas de trabajo. En esta publicación les voy a contar como lo tengo configurado y como hago uso de ella. Lo primero que suelo hacer cuando empiezo en un proyecto nuevo, es crear un “cliente”. Este cliente es para quien estamos haciendo el trabajo y luego, dentro de este cliente, voy añadiendo los proyectos internos en los que estoy metidos entre otras cosas. Como puedes ver, en mi caso tengo dos clientes principales. Uno de ellos “Lean Mind” tengo solamente el proyecto “formación” y en el caso de Clientito, según el proyecto interno tengo varios proyectos, reuniones o simplemente tiempo que no sé muy bien como clasificar (otros). Suelo ser bastante estricto y cada vez que empiezo una tarea. Lo primero que hago es ir a Toggl, darle al play, poner el nombre a la tarea y encajarla dentro de uno de los proyectos. Esto, de alguna forma me ayuda a centrarme en lo que estoy haciendo y pensar un poco antes de empezar. Una vez ya estoy trabajando de vez en cuando consulto a ver cuanto tiempo llevo (sobre todo si estoy atascado) para ver si ha pasado mucho tiempo o no. Me suele pasar que me pongo a trabajar en algo y el tiempo pasa volando sin darme cuenta. De esta manera sé cuanto es ese tiempo. Suelo obligarme a parar en caso de que no esté avanzando para alejarme un poco del problema, sobre todo si ya llevo un tiempo considerable. En estos momentos suele ser cuando reviso el correo, leo slack, contesto a compañeros, reviso PR, etc. Cuando esto ocurre lo que me gusta hacer es volver a darle al play cuando regreso a la tarea en la que estaba sin haber parado el tiempo anterior. De esta manera contabilizo los bloques de tiempo que he dedicado a una tarea en el día. Ejemplo Supongamos estoy creando un nuevo endpoint para obtener gatos. Empiezo a las 10:10 y estoy hasta las 12:20. Paro porque estoy atascado con algo y me paso 10 min revisando slack y demás. Cuando a las 12:31 vuelvo a ponerme con la tarea vuelvo a decirle a Toggl que estoy haciendo de nuevo la misma tarea “duplicando” la entrada. De esta forma me ayuda a ver si me he pasado de tiempo en la última sesión de “pomodoro”. Gestionar el tiempo de esta forma me es muy útil cada mañana cuando al sentarme en el PC voy a reportar las horas de trabajo. Digo que es útil porque me basta con ir a la parte de reportes en Toggl y buscar el tiempo que dediqué el ayer en Clientito y ponerlo en el excel o donde toque. También me es muy útil para mirar las horas de formación que llevo esta semana o este mes y poder dedicarle más o menos tiempo. Por ejemplo esta semana dediqué 2 horas y 19 minutos en revisar grow, preparar un artículo nuevo sobre big data. En mi caso me es útil para no volverme muy loco con el tiempo que le dedico a las cosas. Reviso de vez en cuando y actuar en consecuencia 😸. Además de, en cada reunión diaria o semanal saber decir en que he estado trabajando para no quedarme en blanco 😂

CESINF 2021 - Código sostenible

El pasado día 2 de diciembre me brindaron la oportunidad de participar en el VII congreso de estudiantes de ingeniería informática (CESINF) en la universidad de la laguna, Tenerife. Ciertamente no esperaba tener tan buena recepción tanto por parte de la organización como de los asistentes al evento. Desde el momento de mi llegada me sentí arropado por todas las personas allí presentes. Si bien es cierto que algunas ponencias (de las pocas a las que pude asistir) se alargaron más de lo previsto, pero la organización supo como gestionarlo quitando tiempo de la parte final de preguntas y ajustando algunos de los eventos internos que tenían preparados. En cuanto a mi ponencia, como podrás imaginar, trataba sobre código sostenible. Un tema tan amplio que cuando me tuve que sentar a pensar sobre que quería hablar se me venían infinidad de ideas a la cabeza y no sabía cuál elegir entre todas ellas. Finalmente opté por hablar sobre que es el código sostenible, por qué es importante, una introducción al tema sobre todo. Sé que no ha sido la mejor charla que he dado ni de lejos, pero sí que ha sido la que he preparado en menos tiempo 😂 y sobre un tema que no había presentado antes. También es cierto que no he podido nombrar todos los temas que me gustaría, porque son demasiados. Por suerte desde, Lean Mind hemos escrito un libro dónde están todos los consejos que te puedas imaginar, trozos de código, algún ejercicio y muchas historias sobre código sostenible. Para obtenerlo tan solo tienes que ir a codigosostenible.com Por suerte pude grabarme con el teléfono y se escucha más o menos bien, así que he podido hacer un montaje para subir a YouTube por si quieres verlo. Además de esto, todos los recursos (diapositivas y lo que tenía pensado decir) está en GitHub de manera pública. Te dejo todos los enlaces más adelante en este mismo artículo 😉 En general las sensaciones han sido muy buenas y me gustaría repetir esta ponencia en el futuro. Prepararla un poco mejor e ir mejorando. ¡Gracias a todos por el apoyo! Recursos Repositorio con recursos Video en YouTube

He participado en coding is caring

El pasado día 11 de Octubre de 2021 me invitaron al canal de Twitch de CodignIsCaring con mis amigas Yodra y Maria. Hemos hablado de muchas cosas entre ellas IA y big data pero sobre todo nos hemos reído bastante. No te prometo que vayas a salir de ese directo con mucho conocimiento de estos temas pero te lo vas a pasar bien seguro. La idea era hacer una introducción a estos temas ya que no conocían nada sobre ellos. Y sin duda, la clave si te gustaría iniciarte en el mundo del Big Data ya sea como ingeniero de datos o científico de datos es la plataforma de Kaggle. Mostré un curso de iniciación gratuito para IA y otros recursos para aprender big data. Hicimos algunas partes del curso juntos, me hicieron muchas preguntas y les preparé un regalo que me hacía mucha ilusión. Una template para poder ejecutar tus notebooks de JupyterLab desde la consola sin necesidad de tener que tener un servicio de Jupyter, todo con Docker. Si quieres jugar con algo de lo que hemos hecho en el directo tienes todos los recursos en este repositorio de github.

pipenv VS venv

Virtualenv fue la primera version para gestionar entornos en python, esto evolucionó a pipenv para tener una interfaz más cómoda y fácil de usar. En la propia web de python recomiendan usar pipenv. Tool recommendations: If you’re familiar with Python packaging and installation, and just want to know what tools are currently recommended, then here it is. Application dependency management: Use Pipenv to manage library dependencies when developing Python applications. See Managing Application Dependencies for more details on using pipenv. ¿Cómo usar pipenv? Para instalar pipenv primero necesitas tener instalado pip. Una vez lo tengas ejecuta lo siguiente pip install pipenv Después de esto, puedes crear un entorno de la siguiente forma pipenv install Esto buscará un fichero pipenv, si no existe creará un nuevo entorno y lo activará. Si lo comparas con el flujo de trabajo que anteriormente se usaba con virtualenv, el de pipenv está muy simplificado. Una vez creado puedes activarlo ejecutando este comando pipenv shell Para instalar un nuevo paquete ejecuta pip install package , pipenv añadirá automáticamente el paquete a tu fichero Pipfile. Otra opción es instalar paquetes solamente para le entorno de desarrollo como mostramos a continuación pip install <package> --dev Recursos 🔗 Application-dependency-management Pipenv vs virtualenv vs conda environment pipenv or virtualenv , which one is better to use?

He participado en mí primer Datathon

El pasado día 17 de septiembre participé en mi primer datathon y, ciertamente, la experiencia ha sido extraña y mejorable 🤷‍♀️ Antes de este evento había participado en otro tipo de hackathones dónde tienes una serie de ideas (proyectos) y ciertos recursos. Con esto y un tiempo limitado (un fin de semana normalmente) tienes que desarrollar la idea en cuestión y traerla a la vida. En este tipo de eventos no se espera que termines con una aplicación funcionando que puedas sacar al mercado. Lo que se busca en este tipo de eventos es hacer algo que sea una prueba de concepto a ver que limitaciones tienen las ideas y hasta dónde se puede llegar y si te gusta la idea y el proyecto poder seguir con él por tu cuenta. En este caso era un hackathon referente a los datos, de ahí el nombre, datathon 😂 En este concretamente había dos proyectos. El primero de ellos referente a la parte de machine learning, específicamente centrado en la parte de deep learning con computer vision dónde los equipos que se enfrentasen a ese proyecto tendrían que clasificar los diferentes elementos en imágenes de fondos marinos. Un proyecto muuuuy interesante en el que no participé por mi falta de experiencia en estos campos 😝 Por otro lado, tenemos el proyecto de smart destinations. Proyecto en el cual tenemos varios datasets de muchos tipos y orígenes de datos con los cuales tenemos que plantear el proyecto que más nos encaje para apoyar el turismo en las islas canarias. Cuando digo muchos, son muchos, más de 350 archivos de datos. Equipos Los equipos creo que ha sido la tarea más difícil de gestionar por parte de la organización. Al ser un evento híbrido algunas personas están en remoto y otras físicamente en el evento. Esto hizo que los equipos se dividiesen en dos grupos: equipos remotos y equipos físicos. En mi caso estaba dentro del equipo remoto y creo que aquí ha sido dónde empezaron los problemas… Mi equipo no estaba muy dispuesto a tener reuniones en vivo por videollamada para poder sincronizarnos. Esto hizo que al limitarse todo a comunicación escrita hubiera varios malentendidos, algunas conversaciones fuesen más tensas de lo que realmente debían ser y otros problemas menores. Algo tan simple como una presentación de los miembros del equipo teniendo en cuenta avisar de dónde nos encontrábamos físicamente hubiese evitado confusiones con los nombres de las Islas Canarias que hubo 😂 A esto se le suma la diferencia horaria, ya que algunos miembros están en América, la vida de cada uno de nosotros y el estrés inherente en estas competiciones por estar en una contrarreloj constante. Todo esto concluye en un pequeño desastre organizativo a escala de equipo. Smart Destinations Cuando me plantearon este proyecto me di cuenta de que el mayor problema que teníamos con este proyecto era analizar todos los datos que teníamos y buscar la relación entre ellos, más que terminar un proyecto como tal. Buscarle un sentido a un subconjunto de los datos para darle un uso real era el problema de este proyecto. Y así nos lo hizo saber nuestro tutor en el segundo día. Nuestra solución Dicho esto ¿Qué piensas que deberíamos hacer? 🤔 a mí se me ocurre algo como dividirnos los más de 350 conjuntos de datos entre el equipo, ver que tenemos entre manos, cuáles están relacionados y pensar en un posible uso de ellos, ¿no? Pues mi equipo de manera casi unánime (yo fui quien opinó distinto) decidió tomar la idea de una de las personas del grupo que tenía clara e implementarla buscando entre los datos que teníamos que encajar con ella 🤷‍♀️ Yo tampoco entiendo por qué ocurrió de esta forma, no pude hacerles cambiar de idea. A partir de este punto, perdí un poco el interés. Ayudé en todo lo que pude, pero no con las mismas ganas con las que he asistido a otros eventos similares. Y como pudo acabar si no… Conclusiones Finalmente nuestra solución no estuvo ni entre las tres primeras. Yo no pude estar el día de la presentación y no sé que han dicho sobre nuestra propuesta, pero no me sorprende el resultado. A día de hoy 20 de diciembre de 2021 unos cuantos meses más tarde de la finalización del evento sigo esperando por el supuesto documento dónde nos dirían que puntos han tenido en cuenta para valorar nuestra propuesta y por qué no ha sido premiada. Este punto es el que más tengo que recriminar a la organización 😤 Recursos Evento Datathon Charla Datathon 2021- DotCSV by Carlos Santana Datathon Gran Canaria Sept 2021 Datathon Gran Canaria Sept 2021

Gráficas de distribución

Existen multitud de gráficos para visualizar la distribución de nuestros datos y poder ver patrones. Hoy le toca el turno a dos gráficos con los que podremos ver de mejor forma como se distribuye la frecuencia de aparición de una variable respecto de otra. Estamos hablando del histograma (histogram) y el kernel density estimate (KDE, no sé cómo traducirlo 🤷‍♀️) Histograma Un histograma no es una gráfica de barras, puede parecerlo pero tiene diferencias como que en este caso el valor por el que se distribuyen nuestra frecuencia no es un valor categórico, es continuo. ¿Esto que quiere decir? Con esto nos referimos a que el eje de las X no está separado por grupos de manera natural (como puede ser el estado de una tarea; pendiente, hecho, por hacer) en este caso es continuo y existen infinitos valores entre un punto y el siguiente. Es por esto que, en este tipo de gráficos tenemos que agrupar nuestros valores en clases (bins). Estas clases simplemente son un rango para poder hacer la agrupación por ejemplo, si tenemos valores desde 0 hasta 1 podríamos decidir agruparlos en clases de 1. Esta primera clase, va a tener una barra para los valores que se encuentran en este rango [0, 1), otra para [1, 2) y de esta forma todos los valores o nos puede interesar agrupar en rangos de dos. Esto dependerá del uso que le queramos dar y los valores que tengamos disponibles. A diferencia de los gráficos de barras, los histogramas no pueden ordenarse por frecuencia de aparición ya que pierden el sentido de continuidad y lo que representan. Con un histograma podemos ver como se distribuyen los datos. Kernel Density Estimate (KDE) Este tipo de gráficos funciona de forma similar que el histograma que hemos visto anteriormente con la diferencia de que en este caso muestra de manera continua la evolución de los valores en lugar de separarlos por rangos. De igual forma podemos ver la evolución de una variable respecto de otra. Con este tipo de gráficas, como su propio nombre indica, podremos identificar patrones referentes a la distribución de los datos como puede ser el rango con mayor frecuencia de aparición, etc. Recursos https://corporatefinanceinstitute.com/resources/excel/study/histogram/

Disciplina mejor que motivación

Últimamente me han preguntado varias veces como consigo sacar tiempo y ganas después del trabajo para lograr seguir un rato más generando contenido. Aparte de esto me he topado con varias publicaciones y vídeos motivacionales hablando de lo importante que es estar motivado, tener un trabajo que te encante para disfrutar cada día, etc. 😃 No vengo a desmentirlo ya que ayuda muy mucho para tener más energías pero, cuándo eres una persona exigente que cada día busca mejorar más y más llegará un punto en el que te cansarás hasta de tu hobby favorito. Llegará un momento en el que te sentirás estancado y que no avanzas, quieres dar más pero no ves salida y dudarás sobre si realmente te gusta tanto 😣 Es cierto que la motivación te ayuda a avanzar, pero en este punto del que hablamos no existe motivación que pueda con tu cabeza y entonces llega el turno de la disciplina 🧘 Ahora es cuando tienes que tener la suficiente fortaleza mental para saber decidir entre esperar la motivación (cuando sea que llegue 🙄) o hacer un esfuerzo y seguir avanzando por el camino que has decidido, continuando con tu objetivo 🎯 Motivación si, pero que no sea tú única razón para avanzar. Crea un hábito, una rutina, un camino que seguir incluso los días en los que la motivación no está contigo.

Este mundo no es en blanco y negro

Últimamente no sé si es que mi círculo cercano está cambiando o que estoy viendo la vida de otra forma, pero me he percatado de que cada vez más la sociedad en la que vivimos intenta polarizar toda opinión que escucha. En este artículo quiero reflexionar sobre este tema y sobre todo me gustaría conocer tu opinión al respecto para saber si realmente es porque estoy sesgado (al fin y al cabo no conozco a todas las personas del mundo 😂) Todo esto parte de un vídeo de GaryVee dónde habla de los diferentes tipos de inversiones que hay y como las personas de un grupo dicen que el resto de grupos no valen. Manda un mensaje de que algunas personas viven en el mundo del “o” en lugar del “y” diciendo que no tienes por qué escoger un tipo de inversión u otro puedes participar en tantos como quieras y, si tú estás en varios no tiene por qué significar que el resto no valen la pena (o al menos esto entendí yo 🤷‍♀️) 4 Minutes That Might CHANGE YOUR LIFE Esto me ha hecho pensar y reflexionar sobre situaciones que me han pasado en el pasado y me siguen ocurriendo como la que voy a explicar ahora. Mi experiencia Llevo prácticamente un año visitando un dietista porque quiero cuidar mi alimentación y comer bien para poder estar más saludable. Además de esto, he ido a entrenadores personales por la misma razón pero en el ámbito del deporte. Todo esto porque la mejor manera de avanzar es guiado por alguien que conoce la materia. Hace unas semanas hablé con el dietista (Antonio Juan) para decirle que hace años que quiero intentar reducir el consumo de carne y pescado (por motivos éticos) y que no había dado el paso por miedo a hacerlo de manera incorrecta por haberme dejado nutrientes en la dieta y que esto me afecte a mi salud. Después de hablar con él y solventar mis dudas damos el paso y dejo de tomar carne y pescado en mi dieta. Por otro lado esto lo hago la mayor parte del tiempo, pero no todos los días porque los fines de semana al salir de casa es más complicado y no me gusta negarme a planes con amigos porque tenga que comer carne. Aquí es dónde empiezan algunas personas a juzgar y polarizar las decisiones que he tomado cuando les explico la situación, he escuchado comentarios del tipo: “es que para ser vegetariano a medias no hagas nada” (porque tomo leche y huevos), “ya claro de lunes a jueves mucha verdura, pero luego los fines de semana bien que comes carne”, etc. Todo este tipo de comentarios hacen que parezcas el malo porque te intentan hacer pensar que estás actuando mal por no “ser vegetariano al 100% el cien por cien del tiempo” y parece que esto que estás haciendo no vale de nada. Reflexión Y aquí es dónde el vídeo me ha hecho reflexionar sobre varios temas. ¿Por qué soy bueno o malo por hacer algo que (aparentemente) no hace daño a nada? Más bien sirve de ayuda. Existe una gran escala de grises y no tenemos por qué clasificar en blanco o negro las decisiones. ¿Por qué tengo que clasificarme en cierto grupo social? Yo no he dicho que sea o deje de ser vegetariano. Siempre planteo la explicación con el enfoque de que he reducido el consumo de carnes animales y nada más. Soy yo y mis circunstancias. ¿Por qué parece que tengo que justificar el reducir el consumo de carnes y por otro lado no tenemos que justificar el que si la consumamos? Conclusión No hago este artículo para juzgar a nadie ni quejarme de la situación, lo hago con la idea de que la próxima vez antes de hacer yo un comentario sobre la forma de actuar de cierta persona, me piense dos veces si realmente aporta algo o no. Si realmente hago ese comentario porque busco una respuesta o porque tengo miedo. Porque desconozco cómo dar el paso que ya esa persona ha dado. Para sentirme bien conmigo mismo por estar dónde mismo estaba hace un año tranquilo, sin cambios y parecer más listo por no hacer nada al respecto, lanzo comentarios desmereciendo a la otra persona. Agradecimientos Antonio Juan, nutricionista y psicólogo (en sus ratos libres 😂) Steven, entrenador y colega. Gary Vee, porque vas al grano sin dudar. Carlos Blé, por este pedazo de artículo. Carlos Ríos y Rafael Santandreu, por el pedazo de podcast y por la siguiente frase. La manera de transformar el mundo, si quieres hacerlo, es desde la alegría. -Rafael Santandreu Quejarse es inútil y una pérdida de tiempo, no lo pienso hacer. -Stephen hawking

Roles dentro del big data

Es normal que en cualquier ámbito de la vida existan roles para definir el alcance y los límites que tiene una persona dentro de un contexto. En parte es lo que hacemos cuando le damos nombre a las profesiones, no tiene las mismas responsabilidades un desarrollador que un tester de calidad, por ejemplo. Dentro del ámbito del Big Data específicamente ocurre lo mismo, existen varios roles dependiendo de las responsabilidades que tengas en este tipo de proyecto. En este artículo vamos a explicar alguno de ellos. Ingeniero de datos Data engineer Este rol es el encargado de obtener y guardar los datos. Esto incluye preparar los pipelines para la extracción de datos automática desde cada fuente con el objetivo de unificar nuestro centro de datos. En resumen, extraer, transformar y guardar los datos crudos, crear un buen data lake. Esto quiere decir que este rol ha de conocer tecnologías en la nube como Amazon S3, Azure storage, kafka, spark y hadoop entre otros. Científico de datos Data scientist Es un profesional que evalúa, genera, divide e integra el conocimiento del negocio y los datos que se guardan del mismo (en el data lake ya mencionado). Básicamente diseña modelos analíticos, define algoritmos y queries con el objetivo de mejorar el negocio en cuestión. Tienen conocimientos en programación y diferentes herramientas estadísticas como SPSS. Además de estos conocimientos, un científico de datos ha de saber sobre matemática y estadística para poder diseñar estos modelos de forma correcta. Arquitecto de datos Data Architect Se encargan de que el ecosistema big data funcione correctamente y con la menor fricción posible. Organiza, administra, maneja y gobierna la infraestructura Big Data en grandes clusters. Necesita tener experiencia en Java, MapReduce, Hive, HBase, PIG, Sqoop, Linux/Unix, configuración del cluster, nodos de datos, etc. Otros roles Aparte de los roles ya mencionados, podremos escuchar nombrar otros como desarrollador (data developer), ingeniero de machine learning, analista de datos, desarrollador de visualización de datos (data visualization developer) en función de las necesidades de cada empresa y la especialización que requiera el puesto. ¡Si crees que nos hemos dejado algún rol relevante no dudes en decirlo por cualquiera de nuestras redes sociales! Recursos consultados Te dejo la lista de enlaces que he consultado para escribir este artículo aparte de mi propia experiencia. Big Data Roles & Job Responsibilities - Different Types Of Big Data Jobs Job Roles and Responsibilities in Big Data | Career in Big Data Job Roles Big Data - Roles and Responsibilities in Big data jobs Data Engineer vs. Data Scientist: What They Do and How They Work Together Data Scientist vs. Data Engineer: What’s the Difference?

Gráficas de relación

Cuando nos enfrentamos a un conjunto de datos nuestro cerebro intenta crear relaciones entre los distintos conjuntos. Esto es normal, ya que como seres humanos que somos, siempre intentamos buscar una relación, es la forma que tenemos de asociar acciones. Si bebo agua me quita la sed, a mayor número de ventas de videojuegos en una consola más popular es ¿o no? Estas relaciones que buscamos inconscientemente pueden tener o no sentido y es ahí dónde entra el papel del analista de datos. Debe ser quien determine si la una relación entre ciertos conjuntos de datos tiene sentido o no. Hoy nos centraremos en como podemos facilitarnos la vida para poder ver estas relaciones gracias a una serie de gráficos pensados para esto, relacionar datos. Gráfico de barras (barplot) Este gráfico presenta su fuerte cuando se trata de agrupar por valores categóricos y comparar sus valores numéricos como puede ser, ventas de juegos por categorías de los mismos. El gráfico anterior parece sencillo de hacer, pero si no te percatas de algunas características puedes caer en ciertos errores típicos. El gráfico está ordenado. Con este tipo de gráficos solemos buscar comparar entre las categorías, por eso es mejor dar los datos ordenados, para facilitar esta comparación a la vista. Empieza desde cero. Aunque parezca obvio es fácil ignorar los valores cuando todas las barras son mayores de 200, por ejemplo. Debe de partir desde cero siempre. Colores con sentido. En este caso buscamos resaltar como disminuye el valor entre las diferentes islas, por esto todas las barras tienen un mismo tono verde más o menos intenso. Si buscamos resaltar ciertos valores es interesante darle un color más potente a estos y dejar el resto de datos con colores más apagados, por el mismo motivo de antes, facilitar al usuario obtener la información que busca. Incluir anotaciones de valores. Como podemos ver en el ejemplo cada barra tiene su valor exacto. Esto puede resultar útil sobre todo, cuando dos de ellas están prácticamente en el mismo punto y difícilmente podemos ver la diferencia o simplemente este valor nos es útil, que también puede darse el caso. Voltear el gráfico si son muchos valores. Existe un caso en el que voltear el gráfico tiene sentido, cuando tenemos tantos valores por los que clasificar que se solaparían entre sí. En estos casos es casi obligatorio cambiar la posición de los valores como mostramos a continuación. Gráfico de puntos (Scatter plot) En este caso el gráfico de puntos es más interesante si nos encontramos con dos variables de tipo numérico a diferencia del caso anterior dónde uno de los valores a comparar era de tipo categórico. Como todos los gráficos de este artículo será de utilidad a la hora de comprobar relaciones entre datos de nuestro conjunto. Por ejemplo, podemos ver si existe una relación entre la cantidad de accidentes de tráfico y la velocidad a la que iba el vehículo. Un punto a destacar interesante de este tipo de gráficos es que nos permite añadir una tercera variable categórica a tener en cuenta. Por ejemplo, si queremos comprobar si existe relación entre el peso, la altura y el género de un bebé recién nacido podremos hacerlo de la siguiente forma. Como podemos ver estas gráficas nos ayudan a ver una correlación entre los datos, pero tenemos que tener cuidado porque nuestras interpretaciones pueden estar equivocadas. Podemos estar viendo una relación clara que está ahí por causa de otro campo de nuestros datos. Por ejemplo, si vemos una relación entre el año y el número de crímenes en una población. En esta relación se ve como claramente cuando avanzamos en el tiempo más crímenes ocurren y podríamos concluir que con el tiempo las personas se vuelven más propensas al crimen, pero en realidad este aumento está relacionado con la población que aumenta. Es decir cuanta más población la probabilidad de que ocurra un crimen es mayor y no tiene que ver el tiempo en esto. Es por ello que tenemos que cuidar y hacer un análisis completo de los datos. La correlación no implica causa En este tipo de gráficos puede ser de gran ayuda añadir una línea de tendencias para dejar más claro hacia donde va la relación del conjunto. Problemática 🤦‍♂️ Las gráficas de puntos presentan una problemática que por suerte tiene varias soluciones, el overplotting. Este término indica que podemos estar superponiendo puntos sin darnos cuenta. Y si nos paramos a pensarlo tiene sentido. En el ejemplo anterior de los bebés que han nacido con cierto peso y altura si nos fijamos los puntos son más grandes que en el primer caso y son semitransparentes. Con este detalle podemos ver si hay una aglomeración en un punto de la gráfica, ya que se verá de un color más intenso, como ocurre en el punto 58 del eje de las equis. Otra opción es darle un tamaño distinto a los puntos en función de las repeticiones que tenga, por ejemplo. O un degradado de los mismos. Aunque llegados a este punto puede que nos interese más usar un mapa de calor 👀 De igual forma, si el valor exacto no nos es tan importante como la relación en sí, podemos aplicar un desplazamiento mínimo a los puntos para que pueda verse mejor la densidad. Esto es un tipo de gráfico especial, el swarm plot. Swarm plot Este último tipo de gráficos resulta de gran utilidad cuando queremos tener un gráfico de puntos, pero uno de nuestros ejes no es numérico como por ejemplo, la cuenta total de las mesas de un restaurante según el día de la semana. Si no aplicamos jitter nos queda de esta forma, el jitter no es más que la separación de la que hablábamos antes. Aplicando esta separación quedaría de la siguiente forma. Recursos Para escribir este artículo he consultado muchos artículos y recursos dispersos, pero de los más interesantes que he encontrado han sido precisamente los de seaborn, la librería más conocida en Python para esto. Barplot Scatterplot Swarmplot Plotting with categorial data Visualizing statistical relationships

Primeras impresiones de Twitch

Hace unos días una compañera me preguntó: ¿hey qué tal llevas eso de hablar solo? y pensé: “ostras, ha notado que estoy loco 😅” pero antes de que hablase me aclaró. Me refiero a los directos en Twitch y demás, ¿Qué tal vas? Sensaciones Ciertamente, no sé muy bien como explicarlo. Han sido muchas sensaciones en tan poco tiempo. Al inicio choca bastante, empiezas el directo y no sabes bien que hacer… ¿Empiezo ya a hablar o espero que alguien entre? ¿Voy contando lo que estoy haciendo o espero que alguien pregunte? ¿Hablo más alto o más bajo? ¿Se me escucha? Muchas preguntas que nadie va a responder y nunca sabrás la respuesta pues cada uno de nosotros debería de encontrar su estilo. Una vez empecé el primer directo con la motivación iba narrando todo lo que contaba con muchos ánimos y energía, pero al poco tiempo (apenas 15 min) todo iba decayendo y terminaba casi que susurrando para mis adentros. Esto es algo de lo que no me daba cuenta y gracias a que he subido a Youtube todas las sesiones y que he pedido a personas cercanas que revisen los vídeos y me hagan comentarios al respecto he podido ir mejorando. Como iba diciendo, empiezas no hay nadie viendo y se te puede llegar a olvidar de que te podrían estar viendo, en mi caso no ha supuesto problema, no he sentido tensión o nervios, estoy acostumbrado a compartir pantalla mientras trabajo con mis compañeros. La primera vez que alguien te sigue, no sabes qué hacer. Intentas agradecerle que te esté siguiendo, pero cuando vas a leer el nombre… No sabes ni por dónde empezar o donde hacer las pausas un show 😂 Y luego, la primera persona que te pregunta algo en el chat, con suerte te das cuenta de ello y puedes responderle… En mi caso no fue así y tardé bastante tiempo en percatarme (lo siento 😢) Ahora que ya llevo más de 30 horas de directos la cosa cambia. No tengo ese problema de ir bajando los ánimos (muy a menudo) he visto que lo que a mi mejor me funciona es narrar constantemente lo que hago y que estoy penando. Me he acostumbrado a hablar tanto rato seguido y apenas pierdo la voz al terminar. Me doy cuenta de cuando hablan por el chat y la verdad que le he cogido el gusto y voy a seguir con el proyecto. Datos Casi no hago la primera sesión y realmente ocurrió porque me comprometí con mis compañeros (María e Isaac) cuando les dije en febrero hago mi primer directo, es uno de mis objetivos de este trimestre. Y claro… era ya día 25 y me preguntaban: “Era para febrero de este año, ¿no?” Con estas ganas empecé un sábado 27 de febrero, con problemas, imprevistos, cosas que mejorar pero, empecé. Y ahora casi tres meses y unas 34 horas más tarde me han visto casi 100 personas 😱 que locura. No he anunciado nada estas sesiones y aún con estas me han encontrado tantas personas, gracias. ¿Qué es lo que hago? Estas sesiones son mi manera de obligarme a cumplir un reto que tengo para este 2021 el cual consiste en dedicar 100 horas a estudiar y mejorar mis habilidades y conocimientos en Big Data. He llamado al proyecto #100HorasDeDatos y los directos son la forma de verificar que realmente he dedicado el tiempo que me he propuesto. De momento hemos repasado Python, aprendido visualización de datos con la librería Seaborn y hemos descubierto como tratar, modificar, unir y filtrar datos con la librería Pandas. Además de esto, hemos usado un conjunto de datos libres para hacer nuestras pruebas sin guías, totalmente por nuestra cuenta. Aparte de grabar y subir a Youtube todas las sesiones también dejamos en un directorio de Github todos los recursos que vamos usando y en este mismo blog voy sacando artículos relacionados con lo que aprendemos. Para aprender todo esto estamos usando una web bastante conocida en este ámbito, Kaggle. Todo el rato hablo en plural aunque parezca que estoy solo delante de la pantalla porque al final no estoy solo, 100 personas han pasado por aquí y, aunque no han dicho mucho por el chat, han ayudado a que esto ocurra y siga pasando. ¡Gracias a todos! Canal de Twitch

Porque me gustan los notebooks de Jupiter

Cuando me inicié en este mundo no tenía claro que diferenciaba un notebook de un script de Python con texto de por medio. Ahora que ya he normalizado su uso puedo mirar atrás y comentar las principales ventajas que le veo. Los datos se mantienen en memoria La principal ventaja que veo a trabajar con notebooks en la fase de exploración de los datos, si tenemos cuidado, es que la lectura inicial de los datos desde el origen ocurre una vez aunque “rompas” los datos. Me explico, normalmente al inicio solemos leer desde un recurso externo (archivo CSV, API, base de datos, etc.) y guardamos esta información en una variable. Este proceso puede ser más o menos costoso dependiendo del volumen de los datos. Si por alguna razón modificamos los datos guardados en esta variable tendremos que volver a leer la toda la información. Lo bueno que tiene los notebooks es que este proceso de lectura inicial queda aislado en el primer bloque de código el cual no vuelves a ejecutar hasta que creas una sesión nueva. ¿A qué me refiero con que queda aislado? En los notebooks podemos ejecutar solamente el código que se ejecuta dentro de un bloque específico. Teniendo esto en cuenta, podemos tener un primer bloque que inicia las variables con los datos crudos y luego tener variables nuevas las cuales son una copia del conjunto de datos inicial con el que trabajaremos. De esta manera si borramos o modificamos la información que había en el conjunto inicial no necesitamos leer de nuevo desde el origen de los datos, ya que tenemos en memoria el conjunto de datos inicial en esta primera variable. En este caso podemos ver como tenemos un bloque inicial dónde cargamos las librerías y los datos con los que vamos a trabajar en la variable df y posteriormente hacemos una asignación en wii_games del sub-conjunto de datos No tenemos que ejecutar constantemente A diferencia de un script el cual si o si tienes que ejecutar desde el inicio para poder ver un resultado en los notebooks, como mencionamos en el apartado anterior, basta con ejecutar un bloque de código específico. En todo momento tenemos cargadas las variables y funciones que hemos ejecutado previamente. Podemos decir que se trata de un script dinámico. Esto tiene sus inconvenientes, si tenemos una función de ayuda en un bloque de código el cual no hemos ejecutado aún porque está por debajo del actual o porque no hemos ejecutado todos los bloques en esa sesión no tendremos disponible la función. Al final tenemos que tener en cuenta, al igual que un script, el orden de ejecución de los bloques si queremos evitar problemas más adelante. Es por esto que es más fácil trabajar con prueba y error, pues si ejecutamos un bloque y falla bastará con hacer las modificaciones que creamos y ejecutar de nuevo, sin necesidad de lanzar todo el código anterior si ya ha sido ejecutado. Fácil de compartir con un usuario no técnico Por último, estos documentos son fáciles de compartir con usuarios no técnicos. Podemos configurar un notebook para ocultar piezas de código que no aporten a quien vamos a compartir y solamente vea el resultado final de la operación con un texto aclarativo. De igual forma, podemos crear informes dinámicos los cuales muestren resultados en función de los datos de entrada. Esto último es muy útil si, por ejemplo, mensualmente tenemos que mostrar los resultados de ventas, devoluciones y otros datos de nuestro producto y esta información nos viene por un fichero externo en CSV con un formato conocido. Podemos calcular lo que necesitemos y mostrar resultados según el archivo que carguemos cada vez.

¿Cual es la diferencia entre variables cuantitativas y cualitativas?

Cuando estamos trabajando con datos más temprano que tarde nos damos cuenta de que no todos los datos son iguales. Desde el tipo del dato (número, cadena de texto, número natural, etc.) hasta la forma de interpretar que tenemos esas mismas variables. No es lo mismo el número de quejas sobre un tema que el tamaño de una carretera. Estas diferencias tienen un nombre el cual vamos a explicar hoy. Variable categórica o cualitativa Estas variables están formadas por un número finito de opciones válidas (categorías). Un dato categórico no tiene por qué tener un orden lógico. Por ejemplo, el sexo de una persona. Esta variable tiene tres categorías posibles: [hombre, mujer, otro] como ya hemos dicho son opciones limitadas y no tienen un orden. Estas categorías pueden estar representadas por números por otros motivos, pero esto no significa que dejen de ser categóricas (1- hombre, 2-mujer, 3-otro) Variables cuantitativas Este tipo de variables son numéricas y se expresan con cifras y, a su vez, se clasifican en dos tipos dependiendo de la continuidad de las variables. Cuantitativas discretas Las variables de tipo discretas tienen como característica que el número de valores entre dos valores cualesquiera es finito y contable. Por ejemplo el número de quejas de los usuarios sobre un tema, si tomamos el valor 5 y el 9 sabemos que entre estos dos valores existe el 6, 7 y 8. Cuantitativa continua Por otro lado las variables continuas, tienen un número infinito de valores entre dos cualesquiera. Si tomamos el ejemplo anterior, pero en este caso hablamos de tamaño de una carretera entre 5 y 9 existen infinitos valores posibles 5.1, 5.01, 5.0001111, etc. Este tipo de variables no tiene porqué ser solamente numérica también la fecha y hora se considera continua.

¿Qué son las series y los dataframes? Librería Pandas

Pandas es la librería más usada en el mundo del big data si trabajamos con Python. Esta librería tiene unas entidades propias para gestionar los datos las cuales son similares a las que ya existen en el lenguaje. Esto me ha llamado la atención y es por ello que he investigado cuales son las principales diferencias entre las listas de una y dos dimensiones respecto a las series y dataframes. Listas y series Por orden de complejidad lo primero que veremos son las listas de python (arrays) y las series provenientes de pandas. Las listas en python es un objeto el cual contiene cualquier tipo de dato dentro en un orden. Como su nombre indica tenemos una lista de elementos de la siguiente forma: lista = [0, 1, 'dos', 'tres', 12] Como has podido observar esta lista no tiene porque ser de elementos de un mismo tipo. En el ejemplo se puede ver como mezclamos números enteros y cadenas de texto. Esta es la primera de las diferencias con las series, en estas últimas los elementos que contienen han de ser siempre del mismo tipo. Esta es una de sus características principales debido a que una serie se puede considerar como una columna de un conjunto de datos la cual siempre ha de ser del mismo tipo. Por ejemplo, si consideramos la columna nombre en un conjunto de datos esta siempre ha de ser una cadena de texto. Además de esto, las series tienen un índice el cual puede ser no numérico y lo podremos definir explícitamente. d = {'a': 1, 'b': 2, 'c': 3} ser = pd.Series(data=d, index=['a', 'b', 'c']) ser # a 1 # b 2 # c 3 # dtype: int64 Como vemos en este ejemplo definimos un mapa d el cual usaremos para crear nuestra serie y luego le indicaremos cuales serán los índices del resultado. De esta forma podremos acceder a los datos de la serie de forma más “cómoda” con el nombre que le hemos dado a los índices. En el caso de las listas nativas de Python estos índices van definidos por el orden en el que se encuentran partiendo desde cero. Esto mismo nos pasará en las series si no definimos índices. Listas de dos dimensiones y dataframes Una vez hemos comprendido en el apartado anterior las diferencias entre listas y series podremos ver con mayor facilidad en que difieren las listas de dos dimensiones y los dataframe ya que, estos son un conjunto de los anteriores. Al igual que en el caso ya visto, una lista de dos dimensiones nativa de Python puede contener cualquer tipo de dato en su interior: números enteros, cadenas de texto ¡o incluso otras listas! sin embargo, un data frame dentro contiene únicamente series. Por esta razón, los datos dentro de un data frame tienen un tipo por cada columna (serie) d = {'col1': [1, 2], 'col2': [3, 4]} df = pd.DataFrame(data=d) df # col1 col2 # 0 1 3 # 1 2 4 df.dtypes # col1 int64 # col2 int64 # dtype: object

Cómo facilitar una retrospectiva

Hoy en día raro es el equipo en el que trabajamos y no usa las ceremonias “típicas”, o alguna de ellas, provenientes del framework Scrum. Hablamos de reunión diaria, planificación del sprint, revisión del sprint, retrospectiva, etc. También conocidas en Inglés como daily meeting, sprint planning, retrospective, etc. En esta ocasión venimos a hablar en concreto sobre la retrospectiva, pero antes de empezar te contamos en que consiste. Una reunión de retrospectiva consiste en una sesión de unos 90 minutos (a decidir por el equipo). Se recomienda hacer este tipo de reuniones después de cada ciclo o sprint, pero cada equipo las gestiona de formas diferentes. El principal objetivo es ver la calidad, relaciones, procesos del equipo en el último ciclo de desarrollo. Para ello, vamos a responder tres preguntas en base en lo que hemos hecho hasta ese punto. ¿Qué funciona bien? ¿Qué no funciona bien? ¿Qué vamos a intentar de forma diferente? Para poder responder a estas preguntas en equipo tenemos 5 fases en las cuales el principal objetivo es que el equipo se abra y comparta sus sensaciones y temores de manera anónima 1. Preparar el escenario El objetivo de esta es llegar a la retro con una buena actitud. Para esto se pueden hacer dinámicas de equipo como, dar 5 min para buscar una foto en internet que defina su estado actual. Después de este tiempo cada persona muestra su foto y cuenta brevemente por qué se encuentra de esa forma. De esta manera, todos conoceremos mejor cuál es el estado del resto y el equipo podrá hacer un ejercicio de empatía. 2. Generar los datos En caso de que no sea nuestra primera retrospectiva tendremos tareas que revisar generadas en la sesión anterior. Este es un buen punto para hacer esta revisión y tener una mejor idea del punto en el que nos encontramos. Aquí tendremos que ayudar a todas las personas a compartir su punto de vista. Para conseguir esto tenemos que crear un espacio donde todo el mundo ponga sus respuestas a las preguntas. Es importante que este espacio sea compartido y para todos los participantes para que cada una de las personas sientan la tranquilidad de que pueden poner lo que sienten o piensan sin miedo. Recuerda, todos vemos el mundo desde diferentes puntos de vista ¡cuenta el tuyo! 3. Revisar e indagar Ahora llega el momento de ver los datos que tenemos, las respuestas a las diferentes preguntas, entender por qué están ahi, agrupar las que se refieren a un mismo tema y, en definitiva, tener una vision general del estado del equipo. Con esto podremos todos entender mejor cuáles son las preocupaciones del equipo, si un mismo tema se repite varias veces deberíamos darle importancia ¿o no? Ahora que tenemos un punto de vista general del equipo podemos decidir cuáles son los temas que queremos tratar. Esto lo conseguiremos votando los temas, por ejemplo, si somos un equipo de 5 personas daremos 5 puntos a cada una de ellas los cuales los podrá usar para darle importancia a las tareas que considera importante. Al final de esta votación veremos cuáles son los puntos más votados, cuáles deben de ser nuestro foco par buscarle una solución o mejora. Lo que nos lleva a la siguiente fase de la reunión. Es importante tener en cuenta que no debemos dar muchos puntos para repartir o los puntos quedarán tan repartidos que no quedará claro cuál de los temas son los que más preocupa al equipo. Si tenemos pocos puntos que dar, 5 como habíamos dicho, tendremos que pensar mejor dónde usarlos. 4. Decidir que hacer En este punto veremos los temas más votados y entre todos decidiremos acciones a tomar. Estas acciones son importante que sean lo más específicas posible. Por ejemplo, si decimos “mejorar los test automáticos” no queda claro cuál es el objetivo ni como lo abordaremos, esta acción puede interpretarse como mejorar la calidad de los test o aumentar la cobertura de test. Es por ello que sería mejor una acción más parecida a “aumentar la cobertura de test hasta el 80%”, de esta forma queda claro, no hay espacio para interpretaciones y, por otro lado, es más fácil evaluar en la siguiente retrospectiva si hemos logrado los objetivos o no y porque. De esta forma dejaremos claro el plan de acción que vamos a seguir en lugar de quedarse en una declaración de intenciones. 5. Cerrar la retrospectiva Como en cada sesión al final de la misma deberíamos dejar unos minutos para cerrar todos los temas que han quedado abiertos y si no podemos cerrarlo dejarlo escrito para retomarlo más adelante. Además de esto, es buena idea hablar sobre los puntos que se pueden mejorar para la siguiente retrospectiva. Aparte de esto, en mi caso me gusta escribir un correo a modo de resumen de la sesión. Puede estar más o menos elaborado, eso depende de ti. Este resumen lo hago sobre todo a modo de recordatorio para cuando estemos preparando la siguiente sesión, tener una especie de log que poder consultar y no tener que estar recordando. Este correo me gusta además, no mandarlo sobre la marcha con el tema en caliente. Prefiero redactarlo al final de la ceremonia y dejarlo programado para que se envíe al día siguiente a primera hora o dentro de dos días. Con esto dejamos que el equipo asimile la información que hemos obtenido y la refrescamos mediante el correo posterior cuando la mente esté más fresca. Que NO es una retrospectiva NO es un punto para culpar y señalar a otras personas. De hecho es bueno dejar claro que las retrospectivas independientemente de lo que descubramos y comprendamos en esta sesión, todos comprendemos que cada uno de nosotros hizo el mejor trabajo que pudo, dado el tiempo que tenía, habilidades, recursos disponibles y su situación. NO es otra reunión más en la que hablar un rato y salir de ella igual que entramos. Es importante comprender el equipo e intentar mejorarlo. Herramientas Llegados a este punto puedes estar pensando como puedes facilitar este tipo de sesiones. La primera y más directa es facilitar estas sesiones físicamente con post-it, una pizarra y unos cuantas pegatinas para puntuar. Pero hoy en día, en los tiempos que corren nos toca facilitar estas sesiones en remoto y para ello hay múltiples herramientas online específicamente para ello. Te dejaremos algunas de las que hemos probado a continuación. Metro retro Parabol Miro Recursos What is a retrospective? Scumizate ¿Qué es una retrospectiva?

Escribir correos con sentido

Hace unas semanas desde English 4 Devs me invitaron a una sesión sobre hablar en público. De primeras pensaba que sería una como tantas con los mismos tres consejos, pero… nada de eso 😁 Fue una sesión muy interactiva, con ejercicios por medio para poner en práctica lo aprendido y poder asentar mejor las ideas. Uno de los puntos que más valoré entre todos los que vimos ha sido que hablar en público no ocurre solamente cuando facilitamos una charla sobre un tema con sus diapositivas, etc. Hablar en público va desde escribir un mensaje de texto por un grupo hasta mandar un correo a varias personas. Y estas situaciones ocurren con más frecuencia que facilitar una charla, a diario de hecho. Es por esto que después de la sesión decidí crear una plantilla en mi correo la cual consta de 4 puntos, los cuatro puntos que debemos tener en cuenta a la hora de escribir un buen discurso. 1. Tesis La idea central sobre la que vas a hablar. 2. Razones Las ideas de soporte por las que crees que tu tesis es cierta. 3. Evidencias Ejemplos concretos a través de los cuales aumentan la credibilidad de tus ideas. Datos, estudios, citas, experiencias vividas, etc. 4. Conclusión ¿Qué quieres que el oyente haga con toda esta información? Puede parecer excesivo para escribir un correo a los compañeros del trabajo, pero, si te paras a pensar y antes de escribir el correo intentas primero responder estas preguntas te darás cuenta si realmente tiene sentido o no lo que quieres escribir. Por ejemplo, en un ataque de ira puedes sentir la necesidad de mandar un correo para quejarte sobre algo que te ha pasado. Pero, si completas estos puntos podrás darte cuenta de que realmente no ganarás nada con este correo. tesis: quiero mostrar mi enfado porque Cristian no quiere hacer pair programing conmigo. razones: hoy le he dicho que hagamos pair y me ha dado largas. evidencias: mi palabra. conclusión: me molesta que María me rechazara para trabajar juntos sin darme argumentos Como puedes ver la conclusión nos hace ver que realmente este correo no debería ir como una queja a otras personas. En su lugar deberíamos hablar con María personalmente para solucionar este problema. Como has visto nos hemos ahorrado mandar un correo y hacer sentir mal a una persona porque nos hemos planteado que queremos conseguir con este mensaje.

Comprendiendo los heatmap (mapa de calor)

Este tipo de gráfico, mapa de calor, es muy útil a la hora de comparar valores numéricos de una relación. Es decir, si por ejemplo tenemos las ventas de videojuegos clasificadas por plataforma y año como se muestra a continuación Plataforma Año Ventas Wii 2000 100 Wii 2001 150 Wii 2002 50 DS 2000 98 DS 2001 118 DS 2002 134 Si esta tabla de datos fuera mucho mayor con cientos de miles de entradas sería bastante complicado hacer una comparación de sus datos con otro tipo de gráficas. Con el mapa de calor podremos hacer un eje de coordenadas como si de un mapa de hundir la flota se tratase. Dónde, en los ejes en lugar de letras y números tengamos el nombre de la plataforma y el año en el que ocurren las ventas. Y luego dentro de este mapa pondremos los valores que corresponda a cada intersección. La parte interesante viene ahora, este mapa además colorea con un degradado todas las casillas de tal manera que las que tengan un valor mayor sea de un color más intenso y las de menor valor de un tono más claro. Con esto lo que logramos es ver patrones más fácilmente de momentos en los que han habido muchas ventas. Te dejo un ejemplo a continuación Como puedes ver en la imágen anterior la Play 1 tuvo su pico de ventas en 1998. También podemos ver como entre 2008 y 2012 hubo un gran numero de ventas repartido entre diferentes plataformas. Para poder crear este tipo de gráficos con la librería Seaborn en Python necesitaremos que los datos que los datos estén formateados de tal manera que las cabeceras sean uno de los ejes y los índices el otro. Estos dos son los únicos datos que pueden no ser numéricos el resto debe de serlo. El conjunto de datos (dataset) que usaremos tendrá la siguiente forma: 2000 2001 2002 Wii 100 250 50 DS 98 118 2002 import seaborn as sns heatmap_data = create_data_frame_with_sales_per_year_and_platforms(data) ax = sns.heatmap(heatmap_data, annot=True) Este ejemplo ha salido de las sesiones que he hecho en directo aprendiendo visualización de datos. Puedes ver las sesiones grabadas en youtube o en directo por Twitch además subo a github todo los ejercicios que vamos haciendo y los apuntes.

Persistencia

He escuchado varias veces decir que alguien no usa Docker porque al borrar el contenedor o al parar una base de datos toda la información se perderá en el olvido pero, nada más lejos de la realidad. Docker persiste la información en nuestro sistema no lo crea y lo mantiene en un limbo virtual 👻 Con esto quiero decir que cuando creamos un contenedor de Docker automáticamente, si no se lo indicamos, nos creará un directorio en nuestro sistema para guardar toda la información que se encuentre dentro del mismo. La clave está en que nosotros podemos definir dónde queremos que haga este mapeo, incluso decirle que directorio concreto queremos que mapee dentro del contenedor. Esto se hace con los volúmenes Por ejemplo, si partimos de nuestra fake-api que hemos creado anteriormente en la cual primero copiamos nuestro código en el momento de la creación de la imagen con la sentencia COPY . /app dentro de nuestro archivo de configuración Dockerfile. En el momento de la creación de la imagen no tenemos que hacer nada especial, ejecutaremos la sentencia igual que hasta ahora docker build -t criskrus/fake-api . La diferencia viene cuando creamos el contenedor. En este caso tendremos que indicar al contenedor que directorio tenemos que mapear. A nosotros para este ejemplo nos interesa que nuestro código fuente el cual modificamos en la máquina anfitriona se modifique automáticamente en el contenedor para mostrar los cambios. Esto es de bastante utilidad cuando estamos en fases de desarrollo. Para hacer esto tendremos que añadir una nueva opción en el comando de creación indicando que el directorio actual dónde está todo nuestro código corresponde con el código que se ejecuta dentro del contenedor. Esto se hace de la siguiente forma -v $(pwd):/app, con esta opción decimos que el directorio actual se mapee al directorio /app del contenedor. Tenemos que usar el comando pwd porque tenemos que indicar la ruta absoluta. Por lo tanto el nuevo comando con el que crearemos el contenedor queda de la siguiente forma. docker run --rm -it -p 8080:8080 -v $(pwd):/app criskrus/fake-api Ahora, sin parar el contenedor, podemos hacer modificaciones en el código y veremos como se actualiza de manera automática en el contenedor. Como tenemos instalado nodemon esta actualización del servidor ocurrirá automáticamente al actualizar el código sin necesidad de actualizar o hacer nada en nuestro contenedor. Con esto podremos persistir y mantener en nuestra máquina local lo que ocurra en el contenedor y nos interese mantener: configuración, logs, copias de seguridad de una base de datos, etc

Trabaja con Docker sin el terminal

Si eres de esas personas que no le gusta mucho trabajar desde el terminal o, simplemente tienes un día en el que no te apetece escribir mucho, estás de enhorabuena. Existen varias extensiones tanto para Visual Studio Code como intellij con las que podrás gestionar docker, sus contenedores y mucho más. Visual Studio Code Para este editor existen dos extensiones que harán tú día a día con Docker mucho más cómodo. La primera de ellas tiene prácticamente todo lo que necesitas: auto completado, crear imágenes con tan solo hacer click derecho en un dockerfile y decirle que haga el build y la parte que más me gusta un panel dónde puedes gestionar todo, TODO 😲 Este panel se abre cuando pulsamos sobre la extension y podremos ver todos nuestros contenedores arrancados o parados. Las imágenes que tenemos localmente y sus diferentes etiquetas, volúmenes, redes y lo que más me gusta podremos hacer limpieza de manera cómoda y visual. Y esto es solo lo que destaco de esta extensión para Docker, si quieres saber más no dudes en consultar su documentación Docker - Visual Studio Marketplace Conectarme a un contenedor Si a esta primera extensión le sumamos la que te voy a presentar a continuación podrás salir de más de un apuro. En ocasiones me ha pasado que estoy desarrollando y dentro de un contenedor el código no funciona como yo quiero y no tengo logs o necesito revisar si el código está tal cual yo espero. Esto lo puedo hacer desde consola, accediendo a los ficheros por terminal pero, porque no hacerlo con un editor 😉 Con esta extension podemos ir a nuestro apartado de contenedores, dar click derecho y seleccionar “Attach Visual Studio Code”. Después de unos segundos tendremos un editor el cual está conectado directamente al contenedor. Con él podremos acceder y modificar ficheros dentro del mismo además de, tener tantas terminales como queramos sin problema alguno. Remote - Containers - Visual Studio Marketplace Intellij Si en tu caso usas el Intellij tengo buenas y no tan buenas noticias para ti. Existe la posibilidad de hacer esto mismo desde nuestro Intellij IDEA. Podemos gestionar nuestros contenedores e imágenes de manera visual y cómoda, ver un resumen de cada contenedor, que puertos expone, abrir un terminal asociado con el mismo, etc. Pero, no podremos abrir un IDE asociado al contenedor como hemos visto hace un momento. Todo ello desde la pestaña de “servicios” Y ahora es cuando te cuento la parte no tan buena… esta funcionalidad a día de hoy solo está disponible en la versión ultimate de este editor. Así que tendrás que tener una licencia para poder hacer uso de ella 🙄

Comandos útiles

En el CLI de docker existen muchas opciones, aquí te dejo las que más suelo usar explicadas. docker container run -d IMAGE segundo plano -i IMAGE mantiene el input abierto (interactivo) -t IMAGE asigna un pseudo terminal. Estas dos últimas las suelo usar juntas casi siempre -it de esta forma (si la imagen lo permite) al arrancar se me queda una terminal abierta esperando ser usada desde dentro del contenedor. -p 8000:5000 IMAGE mapear el puerto para poder acceder. El 8000 mi máquina se asigna al 5000 del contenedor --rm IMAGE elimina el contenedor al pararlo -e MY_VARIABLE=foo IMAGE al crear el contenedor establece la variable de entorno MY_VARIABLE con el valor foo docker container ls -a muestra todos los contenedores equivalente a docker ps -a ls -q lista los contenedores arrancados y solo muestra los ID equivalente a docker ps -q exec CONTAINER COMMAND, ejecuta dentro del contenedor en comando. Uno bastante sencillo sería docker exec -it ubuntu bash inspect CONTAINER, muestra las propiedades el contenedor Limpiar Docker Este apartado lo considero bastante importante porque de primeras puede parecer que Docker no genera archivos pero después de un tiempo trabajando con él puedes tener en tu disco duro varios GB de volúmenes sin usar. Conviene limpiar de vez en cuando. docker container prune elimina todos los contenedores parados docker container rm CONTAINER elimina el contenedor docker container rm -v CONTAINER elimina el contenedor y el volumen asociado docker system prune --volumes elimina todas las imágenes, contenedores y sus volúmenes asociados.

Optimización de imágenes

En este artículo veremos un comportamiento importante a la hora de crear nuestras imágenes. Organización en capas Como veremos más adelante en nuestros Dockerfile cuando configuramos nuestra imagen ejecutamos ciertos comandos y cada comando RUN, COPY o ADD es considerado una capa. Con cada nueva instrucción de este conjunto mencionado tenemos una nueva capa que se pone sobre la anterior. Cuando nosotros creamos una imagen basada en Node, por ejemplo, esta imagen que creamos está basada en la capa de node que viene de DockerHub de node. Y, sobre de ella, nosotros decidimos que más ponerle. Como puede ser nuestros ficheros fuentes, instalar nuestras dependencias, etc. Lo bueno de que funcione de esta forma es que la primera vez que creamos la imagen tardará un poco en instalar todas las dependencias de nuestro proyecto y ejecutar todos los comandos que le hemos indicado. Pero, una vez esto por primera vez, Docker guarda en caché las capas y automáticamente comprobará que si la capa que vamos a crear es la misma que ya teníamos antes y la estamos creando sobre la misma que estaba antes el resultado será el mismo de siempre. Y Docker usará la misma que tiene guardada ahorrando tiempo en la creación de la nueva imagen. Imagen 1 Como vimos en como crear mi primer contenedor podemos partir de una imagen creada de la siguiente forma. # Dockerfile FROM node WORKDIR /app COPY . /app RUN npm install EXPOSE 3000 CMD node index.js El problema que presenta esta primera versión es que dado el funcionamiento en capas con el que trabaja Docker esta imagen cada vez que hagamos un cambio en el código fuente instalará de nuevo todas las dependencias del proyecto. Dado que las dependencias no es algo que cambie con frecuencia nos interesa que al construir la imagen estas capas se usen desde el caché. Para ello podemos hacer la siguiente modificación. FROM node WORKDIR /app COPY ./package.json ./package.json COPY ./package-lock.json ./package-lock.json RUN npm install COPY ./src ./src CMD node index.js De esta forma la primera mitad del fichero Dockerfile hasta RUN npm install será usado desde caché, ya que no cambia cuando hacemos nuevos desarrollos en nuestra aplicación. Como puede ser: añadir un nuevo endpoint. Agrupar capas Otra opción muy común cuando trabajamos con imágenes que parten de un sistema linux, por ejemplo, y queremos instalar ciertas dependencias es agrupar en una misma capa toda la instalación de paquetes. Esta idea está dentro de la documentación de buenas prácticas FROM ubuntu RUN apt-get update && apt-get install -y \ bzr \ cvs \ git \ mercurial \ node WORKDIR /app COPY ./src ./src CMD node index.js Multi stage building Otra opción común si queremos evitar tener dependencias en el despliegue las cuales son necesarias en la construcción de nuestra aplicación podemos usar multi stage build (construcción de imágenes multi-estado) Esta técnica también se encuentra dentro del manual de buenas prácticas de Docker. Con ella podemos crear varias imágenes en un solo fichero Dockerfile las cuales construyen parte de nuestra aplicación y copiar en la imagen final solamente los ficheros que necesitamos para funcionar. En este caso partimos de una imagen de node a la que le damos el nombre de builder, instalamos las dependencias y construimos la aplicación. Esta aplicación queda guardada en el directorio /build dentro de la primera imagen. A continuación, creamos una nueva imagen y copiamos solo la parte que hemos construido necesaria para que nuestra app funcione al público y la arrancamos. FROM node:10-alpine as builder WORKDIR /app COPY ./package.json ./package.json RUN npm install COPY ./src /app/src RUN npm run build FROM node:10-alpine WORKDIR /app COPY --from=builder /app/build/index.js ./index.js COPY --from=builder /app/node_modules ./node_modules/ EXPOSE 8080 CMD node ./index.js Con esta forma de construir imágenes docker nos ahorramos en la imagen final todos los datos y ficheros necesarios en la parte de construcción. Esto hace que la imagen final sea menos pesada al tener menos información.

¿Cómo crear mi primer contenedor?

Ahora vamos a ver como crear nuestro primer contenedor, para ello primero debemos crear una imagen de la que partir, para esto crearemos un fichero de configuración Docker. Este fichero se llama Dockerfile, en él se indican todas las instrucciones que tiene que seguir para crear la imagen y su contenido. Proyecto de ejemplo Para este ejemplo vamos a crear una imagen de Docker que contenga un servidor en Node con un endpoint que nos devuelva un texto. Para ello vamos a crear primero un directorio donde guardar todo el contenido del proyecto y dentro de este directorio de proyecto crearemos nuestro EndPoint de nuestro servidor. // index.js const http = require('http'); let app = http.createServer((req, res) => { res.writeHead(200, {'Content-Type': 'text/plain'}); console.log('Hello world log!'); res.end('Hello World!\\n'); }); app.listen(3000, '127.0.0.1'); console.log('Node server running on port 3000'); Una vez tenemos nuestro servidor con el que crearemos la imagen vamos a escribir nuestro fichero de configuración Dockerfile # Dockerfile FROM node WORKDIR /app COPY . /app RUN npm install EXPOSE 3000 CMD node index.js En este caso partimos ya de una imagen existente de Node y simplemente creamos un directorio de trabajo, copiamos nuestros ficheros dentro y ejecutamos ciertos comandos para instalar las dependencias y arrancar la app. Una vez tenemos los ficheros creados nos bastaría con ejecutar desde la consola docker build -t node:leanmind . en el mismo directorio dónde se encuentra todo nuestro proyecto. Con este comando le estamos diciendo a Docker lo siguiente: que nos construya una imagen build con el tag (etiqueta) leanmind -t node:leanmind. Este punto es importante si queremos diferenciarla de otras imágenes que no queremos sobrescribir en el directorio actual .. En este directorio es donde buscará el fichero Dockerfile Para comprobar si la imagen se ha creado correctamente ejecutamos docker images en la consola nos debe devolver algo similar a esto @criskrus:~/WorkSpace/node-example$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE node node:leanmind 8dd8c5d26f3d Few seconds ago 125MB Etiquetas de las imágenes Cuando creamos una imagen Docker podemos ponerle un tag o etiqueta. Lo que se suele hacer es dar un nombre a las etiquetas y una versión en las que nos encontramos, por ejemplo FROM node:12. Estas etiquetas las podemos ver en DockerHub en el apartado de tags. Para evitar colisiones con imágenes que ya existen y no queremos sobrescribir podemos organizar las imágenes que creamos bajo una ruta personalizada. Para ello basta con crear la imagen bajo una ruta docker build -t criskrus/node . De esta forma podemos tener en nuestra máquina descargada la imagen de node proveniente de DockerHub y nuestra versión personalizada con un nombre similar. Arrancar la imagen Con la imagen ya creada en el paso anterior arrancamos el contenedor basado en esa imagen con docker run --rm -P node:leanmind Esto nos deja la consola ocupada con los logs del servidor node que hemos arrancado. Para ver que hemos creado la imagen y arrancado bien el contenedor podemos ir a nuestro explorador web y visitar localhost:3000 Instrucciones del Dockerfile Como hemos en el Dockerfile aparecen ciertas palabras clave, le daremos el nombre de instrucciones, con las que hemos modificado el estado inicial de la imagen. La primera de todas FROM esta instrucción siempre es necesaria. Toda imagen de Docker parte de una base, bien puede ser una ya creada con cierto conjunto de herramientas como es nuestro caso FROM node o bien desde una imagen vacía FROM alphine. El fichero de Dockerfile pueden tener varias instrucciones las cuales están bien explicadas en la documentación oficial Dentro de las opciones de instrucciones disponibles en un Dockerfile se encuentran CMD y ENTRYPOINT quiero dedicarles un apartado especial porque fueron las dos que más me han costado aprender. Inicialmente me parecían lo mismo, pero no son para nada iguales. ENTRYPOINT es lo que se ejecutará al finalizar la construcción del contenedor El por defecto es /bin/sh CMD son los argumentos que recibe por defecto el comando del ENTRYPOINT FROM debian ENTRYPOINT ["/bin/ping"] CMD ["localhost"]

¿Cómo funciona Docker?

Para comenzar basta con instalar Docker en nuestra máquina. Dependiendo de nuestro sistema operativo tendremos que seguir unos pasos u otros. La documentación oficial de Docker es muy buena y nos hará de guía en este proceso. El caso de Windows es un poco especial, tienes que tener en cuenta que no puedes al mismo tiempo usar las máquinas virtuales y Docker porque da problemas con el Hyper-V. No estoy al día con este tema, uso Ubuntu, pero he escuchado hablar mucho sobre este problema a mis compañeros. Existen otra casuística dónde Docker no puede acceder al disco duro de la máquina y tienes que ir a la configuración de Docker Desktop para compartir el disco duro de la máquina. Una vez instalado nos basta con abrir nuestra terminal y ejecutar este comando docker run -it ubuntu. Cuando se ejecute esta instrucción estaremos dentro de una terminal nativa de ubuntu y tendremos una máquina linux lista para trabajar desde la consola sin problemas. Dentro de esta consola podremos ejecutar comandos como apt update apt install wget o lo que queramos. Todo lo que hagamos se mantendrá dentro de nuestro contenedor. Cuando queramos salir tendremos que ejecutar exit y volveremos a estar en nuestra máquina host. Y te preguntarás <<¿cómo es todo esto posible? ¿Cómo puedo tan rápidamente trabajar con una máquina linux sin miedos a contaminar mi máquina local?>> Todo esto es posible gracias a los namespaces y los cgroups. Con estas dos tecnologías podemos lograr que los contenedores consuman tantos recursos como nosotros le digamos (cgroups) y vean tanto como les correspondan (namespaces). Estas tecnologías no son para nada nuevas han existido en los sistemas unix desde hace años. En el caso de los namespaces desde el 2002 y los cgroups en el año 2007 fue su primera versión estable. Con esto quiero decir, que es una tecnología estable de la cual podemos confiar el correcto funcionamiento, no es la última moda que han sacado y están en su primera versión prematura. Comprobar que no es una máquina virtual Para ver que realmente estos procesos que ejecutamos dentro de nuestro contenedor están en nuestra máquina host podemos hacer un sleep en nuestro Docker de Ubuntu y, comprobar como se está ejecutando en nuestra máquina local. Para ello iniciamos en un terminal un contenedor de Ubuntu y dentro de él ejecutas sleep de 30 segundos, esto congelará la terminal durante 30 segundos. docker run -it --rm ubuntu sleep 30 Mientras este sleep siga en funcionamiento, en otra terminal en nuestro equipo donde hemos arrancado el contenedor hacemos lo siguiente ps -ejH y buscamos entre toda la salida que obtenemos el proceso del contenedor que debe de estar como proceso hijo de docker. El resultado debe ser algo similar a lo siguiente. ps -ejH # [...] 3358 3358 3358 ? 00:00:09 dockerd 3793 3793 3793 ? 00:00:14 docker-containe 9967 9967 3793 ? 00:00:00 docker-containe 9991 9991 9991 pts/0 00:00:00 bash 10776 10776 9991 pts/0 00:00:00 sleep # [...] Como hemos podido ver los comandos del contenedor están siendo ejecutados en nuestra máquina pero con la ayuda de los namespaces docker no nos da visión de los mismos y desde dentro del contenedor no podemos ver nada de lo que ocurre en la máquina host.

Conceptos básicos

Antes de empezar necesitamos tener una serie de conceptos claros que nombraremos bastante a menudo. Estos son imagen, contenedor y volumen. Estas palabras las usaremos con frecuencia, ya que son la base de Docker. Imagen Una imagen es una configuración para un contenedor, podemos hacer el símil cuando creamos una clase para instanciar un objeto en programación. Creamos un molde con cierta configuración y funciones en su interior que podemos instanciar (crear) tantas veces como queramos en varios objetos y siempre será de la misma forma. Una imagen de Docker sería nuestra clase en programación. Para configurar esta imagen como si de un fichero de programación se tratase Docker dispone de los Dockerfile, los cuales explicaremos más adelante. En ellos pondremos todos los datos que necesitamos para que funcione correctamente como las variables de entorno, comandos que ejecuta, etc. Contenedor Siguiendo con el símil anterior de la clase de programación y el objeto, un contenedor sería una instancia de la clase que hemos creado. Es decir, creamos una instancia que tiene toda la configuración que y funciones que le hemos indicado. Podremos crear tantas como queramos. Con esta clase ya definida da igual en que parte de nuestra aplicación la usemos siempre partirá de la misma base. De forma que siempre podremos trabajar con ella partiendo del mismo punto. Podemos crear tantos contenedores como nos sea necesario gestionándolos a nuestro gusto. Con esto quiero decir que un contenedor lo podemos crear, eliminar, parar, volver a arrancarlo, conectarnos a él, dejarlo en segundo plano, exponer los puertos que nos interesen, etc. Iremos viendo como hacer cuando llegue el momento. Volumen Además, cada contenedor tiene asociado con él un volumen. Los volúmenes son donde cada contenedor guarda la información que tiene en su interior como si de un “disco duro” propio se tratase. Estos volúmenes se encuentran en nuestro sistema y no dejan de ser carpetas que podemos ver. Este punto es importante tenerlo claro porque puede parecer “magia” cómo y dónde guarda la información los contenedores, ya que no está a simple vista en el sistema y solo la vemos dentro del propio contenedor. Y digo que es importante tenerlo en cuenta porque es normal en algún momento hacerse la pregunta: ¿Se pierde la información cuando apago el contenedor? La respuesta corta es no, la información no se pierde, como he dicho está en nuestro sistema en algún punto. La respuesta larga es que si paramos un contenedor la siguiente vez que lo arranquemos seguirá con todos los mismos datos en su interior tal cual lo habíamos dejado. Dado que como cada una de estas tres partes (imagen, contenedor, volumen) son independientes entre sí, una vez eliminamos un contenedor el volumen sigue existiendo en nuestro sistema, durante un tiempo.

¿Qué es Docker?

Docker es la forma de tener una misma configuración de un proyecto independientemente de la máquina o el sistema en el que te encuentres, siempre que tenga Docker instalado. Facilitando así los despliegues y desarrollos de aplicaciones. Además de esto con Docker podrás trabajar y jugar con distintas herramientas y tecnologías sin llenar de paquetes que no te interesan en tu máquina. Quiero dejar claro que Docker no es una máquina virtual, aunque de primeras puede parecerse bastante cuando veamos como funciona realmente comprobaremos que no se parece en nada. De primeras puedes pensarlo de esta forma, te hará más fácil entender el resto de conceptos, pero quiero dejar claro que no es una máquina virtual Como podemos ver en la imagen siguiente una máquina virtual necesita tener un sistema operativo desde cero sobre el de nuestra máquina host. Y luego, encima de este nuevo sistema tendremos las librerías necesarias y las aplicaciones que queremos probar. En cambio, Docker comparte el mismo núcleo del sistema y aislar las librerías que queremos usar dentro del contenedor. Es decir, en el fondo, Docker se está ejecutando en tu máquina, no en un sistema nuevo, pero aislar todos estos procesos y paquetes en una zona privada a la que nadie puede acceder (contenedor). Explicaré en otro post con más detalle.

Resumen 2018

“Quien mucho abarca poco aprieta” Esta es la frase que resume este año 2018 a la perfección. Ha sido un año con muchos cambios en todos los sentidos y a pesar de todas las vueltas que he dado creo que voy por el buen camino. He empezado tantas cosas este año y acabado tan pocas que hasta podría pensar que ha sido un mal año… Pero no lo ha sido, todo lo contrario, me atrevería a decir que ha sido de los mejores de mi vida hasta el momento. Este ha sido, de momento, el año que recordare con mas cariño. Aunque se viene, 2019 que mi madre… Me lo voy a comer 🤘🤘 Universidad El objetivo que tenía más claro en este pasado 2018 ha sido terminar con la universidad de una vez por todas. Estaba ya en mi último año en el que apenas me quedaban un par de asignaturas o tres, el trabajo fin de grado (del que quiero hablar largo y tendido en otro post) y las prácticas externas. Pero, resumidas cuentas terminé las asignaturas y me llevé una decepción con el trabajo fin de grado. Lo terminé y no estoy muy orgulloso de los resultados, pero aún así me pusieron una buena nota, 9.2 y lo que había empezado como un posible proyecto a seguir después de terminar la universidad quedó en un proyecto que hice con un tutor que no me ayudó en nada hasta dos días antes de la entrega jajajaja. De las prácticas externas hablaré a continuación. Puntuación: ⭐⭐⭐⭐✴ Practicas externas, beca Las prácticas externas comenzaron a finales del Enero, recuerdo que en ese mes tuve una semana que estaba en tierra de nadie. Estuve una semana en la que había terminado los exámenes y había mandado correos para las prácticas externas y nadie me respondía. No tenía NADA que hacer con mi vida por primera vez, no tenía clases, no tenía TFG, no tenía prácticas externas. Y no voy a mentir, me sentí bastante mal, nunca me había enfrentado a eso. Por suerte duró apenas una semana. Tras ello me respondieron a varios de los correos que había mandado para las prácticas externas pero había uno de los correos que me hacía más ilusión que el resto, este era el de Carlos Blé. Esperé un poco antes de contestar a alguno de ellos a la espera de que Carlos me respondiese, llevaba siguiéndole la pista desde hace unos años y siempre me había hecho ilusión conocerlo o trabajar con él. Y así fue, me respondió y me dijo que no habría problema con hacer las prácticas con él. Recuerdo esa primera entrevista antes de darme el sí definitivo, estaba muy nervioso, temblaba mi voz por momentos y recuerdo una frase que me dijo: “Ahora mismo no estamos contratando a nadie, si es lo que buscas en las prácticas quedarte trabajando no podemos” a lo que le respondí sin dudar: “No pasa nada, lo que yo busco es aprender” (me moría de ganas de poder aprender de un referente como es él) Al poco de la entrevista, ese Lunes siguiente comencé las prácticas con él junto a dos compañeros más Jorge y Alejandro. Ellos ya llevaban unos meses con Carlos y fueron los que me dieron las primeras clases de introducción: TDD, Clean Code, escribir un blog y algunas cosas más. Estas dos primeras eran y son las bases de su trabajo, con las cuales estoy de acuerdo que debe de ser algo obligatorio a día de hoy en el software que se crea. Las primeras semanas pasaron realizando unas katas sobre TDD. Más tarde las bases del clean code, con las cuales nos recomendó un libro para empezar a leer y con este libro se podría decir que empecé este blog. 📓 Este libro fue Clean code de Robert C. Martin, con el que decidí ponerme un objetivo, un capítulo a la semana el cual resumiría y publicaría en el blog. Y así hice, con alguna semana mejor que otra, con algún capítulo mejor que otro pero bueno lo he logrado 💪 Mientras publicaba esto continuamos con algún proyecto interno para trabajar con los compañeros e ir mejorando en nuestro pair programing, algo que había olvidado. Esta es otra de las bases que sigue Carlos, la programación en parejas a día de hoy la considero algo que se debe hacer casi a diario, al igual que con las dos técnicas anteriores (TDD y Clean code) se gana más tiempo del que se cree que se pierde a medio y largo plazo. Y sin darme cuenta pasaron los meses y ya había terminado las prácticas según lo que la universidad marcaba. Y entonces le pregunté si podría continuar por amor al arte, estaba muy contento de aprender tanto y poder mejorar cada día. No me puso pega y continué mientras terminaba el trabajo final de grado. Tras esto, fue cuando apareció la beca del ITER. Carlos nos la propuso a nosotros tres que estábamos con él de prácticas, una beca con la que poder compartir más tiempo con Carlos a diario y encima poder cobrar algo. La única pega era que esta ocurría en Tenerife lo que nos haría tener que “mudarnos” a la isla de en frente. ✈ Y solo con esta pega dudé mucho, le di bastantes largas a Carlos. Tenía mucho, miedo, no sabía que hacer. Irme a vivir con dos personas que solo conocía de hablar con ellos por una pantalla a diario. Que sí, eran y son super chachis y me llevo muy bien con ellos pero siempre tengo miedo a los cambios y nuevas cosas. Encima, para más dudas, Alejandro no podría venir con nosotros porque estaba en otro país haciendo las prácticas finales de ciclo y vendría en su lugar Rubén Tejera un chico que había estudiado con él y estaba deseando entrar con nosotros.😥 Las dudas se disiparon con una frase que mis padres me dijeron: “Si es lo que quieres inténtalo, que pase lo que pase te apoyamos. Y si te hace falta ayuda aquí la tendrás” Empecé la beca y fueron pasando los meses, los lenguajes de programación, los proyectos, compañeros que fueron entrando en la empresa. Porque Carlos justo en este periodo de la beca abrió la empresa Leanmind. Y se fue formando un grupo con el cual estar muy cómodo trabajando. Una experiencia de la cual estoy muy contento, además de conocer a más compañeros que no pudieron entrar en la beca con nosotros como son Jorge y Naomi.😊 Esto no había entrado en mis objetivos del 2018 pero quiero puntuarlo: ⭐⭐⭐⭐⭐⭐ (7⁄5) jajaja me ha encantado. Trabajo Y atención después de todo esto llegó “El Proyecto”. Después de todo este tiempo de becario con Carlos se nos ofreció un proyecto bastante interesante, en el cual teníamos que actualizar un software que ellos ya tenían en funcionamiento. El problema era que tenía el mismo replicado para distintas regiones y querían tener uno unificado. Además de esto, tendríamos que “limpiar” el código por el camino. El “problema” de este proyecto sería que el primer mes para la puesta en marcha tendría que estar en Berlín con su equipo de trabajo. Si ya solo tener que desplazarme a Tenerife me había hecho dudar, se pueden hacer una idea de cuantas dudas entraron en mí al saber que tendría que estar un mes allí. Al menos me valdría con hablar inglés. Y bueno, estuvimos hablando varios días sobre el proyecto y terminó por convencerme. Y hasta Alemania fui con Carlos, él se estuvo los dos primero días para la presentación. Y la verdad que fue mejor que bien, la oficina con los compañeros super bien, buen ambiente, todos muy simpáticos y el proyecto no pintaba tan mal. El proyecto tenía bastantes test, una arquitectura estándar de Django y se podía bucear por él con bastante comodidad. Aunque hubo una sorpresa de última hora, el compañero que había iniciado ese proyecto y que se suponía que estaría conmigo trabajando codo con codo justo una semana antes avisó que se iría de la empresa y entonces me vería tan solo acompañado de una chica que justo había entrado nueva esa semana. Pero bueno no pasa nada, de momento… Tras una semana y poco, casi dos esta chica no puede continuar trabajando con nosotros ya que no daba el nivel para el puesto. Y ahora, si que si, ahora ya estoy solo definitivamente, con todos mis compañeros en remoto de apoyo pero en este proyecto trabajando en el día a día estaba yo. Aún así poco a poco y con paciencia fuimos sacando el proyecto y quedó terminado justo antes de estar yo de regreso. Y al llegar a casa con redoble de tambores 🥁🥁🥁 nos comunican que lo que habíamos previsto para un año de trabajo, tendrá que quedarse en este mes y esta puesta en marcha del nuevo proyecto. La empresa no les está yendo como ellos esperaban y no podríamos continuar con el contacto profesional. Pero lo que si me he llevado de allí han sido grandes compañeros y amistades con las que continuar el contacto. Gracias a todos por ese tiempo ahí, no los voy a nombrar para no dejarme a nadie :) Puntuación: ⭐⭐⭐⭐✴ me hubiese gustado seguir con el proyecto ahora que estaba caminando y ya estaba cogiendo el ritmo. Pero no por ello, dejo de estar contento del buen trabajo que hicimos. Hackaton Ahora volvemos un poco atrás en la línea del tiempo, esto ocurrió por Marzo. No voy a entretenerme mucho en contar sobre este tema, ya que en su momento hice un resumen de toda mi experiencia. Pero en resumidas cuentas, no sabía si ir a este hackatón. Finalmente fui con un compañero y amigo de clase y nos llevamos grandes conocidos y proyectos muy bonitos, de los cuales algunos han podido seguir adelante y les sigo la pista. Una gran experiencia que me gustó desde el principio al final, para durar apenas tres días. Puntuación: ⭐⭐⭐⭐✴ Deporte El deporte este año ha sido uno de mis grandes retos. Hacía mas de dos años que no entrenaba a diario y conseguía estar serio con ello. Pero este año he logrado, a ratos, conseguir volver un poco a esas rutinas. Es verdad, que al inicio del año al estar en casa la mayor parte del tiempo y gestionar yo mismo mis horarios, era más libre y conseguí ser más constante. Una vez en Tenerife de becario gracias a Rubén que me animó a apuntarnos a spinning, logré ir al menos tres o incluso cuatro veces por semana. Pero ya por último este tramo final he perdido ese ritmo que llevaba, en estos tres meses finales del año no he hecho mucho, por no decir que no he hecho nada. No sé cual ha sido el motivo exacto, al principio me excusaba con estar en un país extranjero, luego con llegar cansado después del trabajo, luego porque era de noche desde las 4 de la tarde y al llegar a casa porque estaba aún desorientado y tenía que centrarme. Pero lo cierto es que me he dejado ir. Ahora ya en 2019 y con el apoyo de mi hermana estoy volviendo a coger el ritmo poco a poco, pero bueno esto no es algo que venga a cuento ahora. En términos generales, no me quejo. He logrado medio volver al entreno y he conseguido mantenerme. Puntuación: ⭐⭐⭐✴✴ Centrarme y conseguir objetivos En este apartado voy a ser bastante breve. Con el inicio del año y el trabajo final de grado, no sabía ni como organizarme. Tenía una pila de tareas y cosas que hacer apuntadas por todos lados y no terminaba de hacer ninguna. Saltaba de una a otra (que creía que era más importante) para luego dentro de un rato saltar a otra de nuevo y así todo el rato. Pasé de esto a tener las tareas centralizadas y al menos “priorizarlas” de alguna forma, pero no fue muy efectivo, me seguía yendo por las ramas. La diferencia era que esta vez contabilizaba cuanto tiempo invertía realmente en cada tarea, ya que además de esto hacía un seguimiento de mi tiempo a ver en que lo invertía. Y esto fue lo que me hizo cambiar. Me hizo cambiar al ver que de todas las horas del día que podía invertir en ser productivo apenas un 30% las usaba realmente para hacer lo que “tenía que hacer”. Me hizo replantearme las cosas y comencé el cambio, comencé a ser más estricto y poco a poco fui dedicando el tiempo que realmente si necesitaba cada tarea. Para nada soy una máquina perfecta, ni mucho menos. Ahora mismo he mejorado y soy capaz de centrarme mejor en cada cosa que tengo que hacer, pero como todo el mundo tengo mis buenos y mis no tan buenos días. Por cierto, aún sigo buscando como organizar todas las tareas de manera conjunta (personales, del trabajo, de side project). He probado trello, google keep, libreta, kanban y ahora estoy con un excel jajaja cualquier consejo será bienvenido. Puntuación: ⭐⭐⭐✴✴ Libros Objetivo libros cumplido! Mi meta para el 2018 había sido leer tres libros de los cuales al menos 1 fuera técnico. Y eso he hecho me he leído tres libros, que han sido: “Como ganar amigos e influir sobre las personas”, “Comunicación no violenta” y “Clean Code”. Nunca habría dicho que me leería dos libros de auto ayuda, comunicación, relaciones sociales, no es algo que se me pasara por la cabeza. Pero ahí están y me gustaron muy mucho, los encontré de casualidad por recomendaciones. En este apartado no tengo mucho que decir, es fácil de contabilizar si he cumplido o no jajaja. Puntuación: ⭐⭐⭐⭐⭐ Blog Para terminar, como no, tengo que hablar de este, mi blog. Quería arrancarlo desde hace tiempo y este año he logrado lanzarme. Como todos los inicios con bastante ilusión, preparé tanto material al comienzo que en el primer mes hice 10 o 12 publicaciones, el triple de mi objetivo!! A medida que fue pasando a ser una obligación ya lo miraba con desgana y pasé algún mes sin publicar nada. Y aquí estamos, casi un año después y cumpliendo (con un poco de trampa) mi objetivo de un post semanal. Digo trampas porque aún no hace un año que está, y un año tiene 52 semanas. Y en este 2018 van ya 54 publicaciones así que dos de regalo y todo jajaja. He llegado por los pelos pero lo he logrado, espero este año poder ser más constante y lograr tener preparado todo una semana antes al menos. Y esto lo digo escribiendo este post 20 días después de comenzar el año, si es que no aprendo 😂😂 Puntuación: ⭐⭐⭐⭐✴ A pesar de todo lo bueno y lo malo de este año, ha sido muy bonito y me ha encantado. Por cierto dije que publicaría una aplicación de algún tipo y lo he hecho 🙄🙄 que se descargó una persona por compromiso 😂 bueno ya les contaré esa historia otro día que no me quiero alargar más. Gracias por leerme!! Y a por todas 💪💪 a comerse el 2019 🦄

Expresar agradecimiento mediante la comunicacion no violenta

“Tu informe es muy bueno.” “Eres una persona muy sensible.” “Anoche fuiste muy amable al ofrecerte a acompañarme a casa.” Estas son frases típicas de agradecimiento en nuestras vidas, y por sorprendente que parezca alienan de la vida. Si nos fijamos no revela nada de la persona que lo está emitiendo al mismo tiempo que emite juicios. Además de esto existen personas los usan porque “funcionan”, porque según los estudios demuestran que los empleados trabajan más si los elogias. Y es cierto, en primera instancia. Lo malo es que estos elogios van dirigidos sin pensar porque funcionan la persona que los recibe antes o después termina por darse cuenta y hace justamente el efecto contrario y su productividad cae en picado, pasando a sentirse manipulado. Cuando expresemos agradecimiento tenemos que hacerlo simplemente para eso, expresar agradecimiento, sin esperar nada a cambio. La única intención es celebrar la manera en la que la otra persona ha mejorado nuestra vida. Los tres componentes del agradecimiento La manera en la que tenemos que expresar un agradecimiento se puede dividir en tres componentes claros: 1. Las acciones que contribuyeron a nuestro bienestar 2. Las necesidades concretas que han quedado resueltas o satisfechas 3. Los sentimientos que tenemos como resultado de la satisfacción A la hora de usar estos tres componentes no tiene porque ser en este orden específico incluso aveces, basta con un simple “Gracias” o una sonrisa. Sin embargo, si queremos estar seguros de que nuestro agradecimiento lo han recibido por completo, es valioso que lo desarrollemos con la elocuencia necesaria. La recepción de las expresiones de agradecimiento A muchos, entre los que me incluyo, nos resulta difícil recibir de corazón las expresiones de agradecimiento. Nos inquieta el pensamiento de que no nos merezcamos tales elogios, nos preocupa no estar a la altura de tales palabras. Acostumbrados a vivir en una cultura en la que comprar, ganar y merecer es la manera normal de intercambiar, el simple hecho de dar y recibir nos incomoda. El hambre de elogios Por mucho que lo negamos la mayoría de las personas queremos que se nos reconozca y se nos aprecie cuando hacemos algo bien. Es común que por mucho que hagamos de buena fe y actuemos de la mejor manera posible para ayudar se nos recrimine esa vez que nos equivocamos o no actuamos de manera correcta. Y más importante aún, es bastante común que como no se nos reconoce estamos más acostumbrados a prestarle mayor atención a los juicios negativos y reprimendas en contra de nuestra persona. Una vez alguien me hizo un símil que representa esto y decía así. “Después de un día andando por la calle 98 personas te dan un abrazo por haberles ayudado en sus vidas. Y por otro lado 2 te dan un bofetón en la cara porque esas acciones les han hecho mal. Al final del día en tú cabeza recuerdas esos dos últimos, les prestas toda la importancia y olvidarás los 98 anteriores.” Y suena duro y feo pero es la realidad en la mayoría de casos.

Como liberarnos nosotros y asesorar a los demas

En ciertas situaciones no somos conscientes ni nosotros mismos del “mal”, estámos tan acostumbrado a verlo en el día a día que lo consideramos parte de la “rutina” diaria. Para darnos cuenta de ellos hace falta una gran cantidad de energía y un alto nivel de conciencia. Esto requiere saber identificar las diversas necesidades y tener la capacidad de conectarse consigo mismo. En nuestra cultura ambas cosas son difíciles, no se nos ha enseñado nunca a conocer cuáles son nuestras necesidades y además están mal vistas. Cuando una persona dice que tienen una necesidad se la está viendo como una persona inmadura o inadaptada. Además estas personas son consideradas muchas veces como egoístas. Centrarnos en lo que queremos hacer y no en lo que salió mal. Tener la capacidad de distinguir nuestros propios sentimientos y necesidades y empatizar con ellos nos puede salvar de la depresión. Los profesionales que ejercen la psicoterapia y otras áreas similares también pueden usar la Comunicación No Violenta para establecer vínculos recíprocos y auténticos con las personas que consultan, en lugar de interpretar todo lo que dicen. … Cuanto mejor conozcas la gratitud, menos víctima serás del resentimiento, la depresión y la desesperación. La gratitud actuará como un elixir que irá disolviendo gradualmente esa dura corteza que envuelve tu ego —tu necesidad de posesión y de control— y hará de ti una persona generosa. El sentimiento de gratitud pone en marcha una auténtica alquimia espiritual, nos hace magnánimos— engrandece nuestra alma. SAM KEEN

El uso protector de la fuerza

Existen situaciones, excepcionales, em las cuales el uso de la fuerza es inevitable. Como por ejemplo cuando la otra persona no está dispuesta a comunicarse y el peligro es inminente. Son situaciones en las que el uso de la fuerza es necesario, pero para ello tendremos que diferenciar entre los usos protectores y los usos primitivos. Al usar la fuerza de manera protectora buscamos impedir daños o injusticias. La intención del uso de la fuerza de manera primitiva es la de hacer sufrir a la otra persona las consecuencias de sus malos actos. Cuando un niño trata de cruzar la calle corriendo sin mirar y vemos un coche, para evitar el daño ponemos en juego la fuerza protectora. Este puede ser ejercido tanto de manera física (sugetándo la mano para que no cruce) como psíquico (diciéndole “¡Estás loco como ibas a cruzar sin mirar!”) El uso protector de la fuerza se basa en el supuesto de que hay personas que se comportan de una forma que puede resultar perjudicial para ellas o para los demás debido a la ignorancia. Por ello el proceso para corregir estas acciones debe consistir en educar, no en castigar. La ignorancia presupone: 1. No tener conciencia de las consecuencias de nuestras acciones. 2. Ser incapaces de ver cómo satisfacer nuestras necesidades sin perjudicar a los demás. 3. Creer que tenemos “derecho” a castigar o herir a otras personas porque “se lo merecen”. 4. Tener alguna idea delirante, como por ejemplo que “una voz” nos ordenó que matemos a una persona. EL precio del castigo Cuando decidimos hacer algo con el único objetivo de evitar el castigo, estamos desviando la atención del valor que tiene esta acción en si misma. En cambio, nos estamos centrando en las consecuencias de vendrán si no realizamos lo que se nos pide. Siempre que se recure a la fuerza punitiva, se deteriora la autoestima. Existen dos cuestiones que nos ayudaran a entender por qué es poco probable conseguir lo que queremos ayudándonos de un castigo para cambiar el comportamiento de los demás. La primera de ellas es la siguiente: “¿Qué quiero que haga esta persona de manera diferente de lo que hace ahora?” Si sólo nos planteamos esta pregunta puede parecer que el castigo es una buena vía para conseguir nuestro objetivo, conseguir influir en el comportamiento de la persona. Sin embargo, la segunda pregunta nos demuestra que es improbable que el castigo tenga efecto: “¿Qué razones quiero que tenga esta persona para hacer lo que le pido?” Con la segunda pregunta vemos que una vez la persona haga lo que le pedimos no tendrá razón para volver a hacerlo pues el miedo no estará siempre presente. No tendrá una recompensa por la que sienta que quiere hacer esto en otra ocasión.

La expresion plena de la ira

Para expresar la ira con la comunicación no violenta tenemos que desconectar de los demás la responsabilidad de que nosotros estemos furiosos. Es decir tenemos que eliminar ideas de nuestra cabeza tales como: “Él o ella me hizo que me pusiera furioso cuando hizo esto otro” Nunca nos enfadamos por lo que hizo la otra persona, lo que la persona haya hecho es solo un estímulo no la causa de nuestro enfado. La ira surge cuando nos enfadamos y buscamos culpables, es decir, cuando optamos por hacer el papel de Dios y juzgar o culpar a otra persona por haberse equivocado. Para que quede más claro el ejemplo anterior veremos como una misma conducta puede generar ira, frustración o agradecimiento en una persona. Supongamos que hemos quedado a una cierta hora con otra persona y esta llega tarde. Dependiendo de como nos encontremos podremos sentir estos tres sentimientos. - Sentiremos ira si consideramos que llegar tarde a este tipo de encuentros es una falta de respeto y no nos está tomando en serio. - Sentiremos frustración si necesitamos usar nuestro tiempo de la mejor manera posible y ahora hemos estado esperando por ella sin hacer nada. - Sentiremos agradecimiento si nos hacía falta en ese día un tiempo de descanso y reflexión que nos ha venido dado por el estar esperando. Como podemos ver, tal como hemos dicho antes, nuestros sentimientos dependen de nosotros no de los actos del resto. El retraso en la cita con la persona es tan solo un estímulo. Por todo esto en lugar de indignarnos, es mejor pensar en nosotros mismos porque estamos así. Pensar en nuestras propias necesidades empatizar con ellas o con las de los demás. Esto es complicado y exige mucha práctica, durante la cual sustituiremos de manera sistemática el “Estoy enfadado porque ellos…” por “Estoy enfadado porque necesito…” Pasos para expresar la ira: 1. Detenerse. Respirar profundamente. 2. Identificar los pensamientos que contienen juicio. 3. Conectarse con las propias necesidades. 4. Expresar nuestros sentimientos y nuestras necesidades no satisfechas. A veces, entre los pasos 3 y 4 podemos optar por brindar empatía a la otra persona a fin de que esté en mejores condiciones para oírnos cuando nos expresemos en el paso 4.

La conexion con uno mismo a traves de la compasion

Quizás, la comunicación no violenta tenga su aplicación más importante en uno mismo. Como somos interiormente, si nos tratamos de manera violenta es difícil que seamos compasivos con los demás. Ante nuestras propias equivocaciones, solemos enredarnos en un sentimiento de odio hacia nosotros mismos en lugar de beneficiarnos de conocer una nueva limitación que tenemos con la que poder avanzar en nuestro crecimiento personal. Si la manera con la que nos evaluamos nos lleva a sentir vergüenza y con ello cambiamos nuestra conducta, hacemos que nuestro crecimiento y aprendizaje esté guiado por el odio que nos damos a nosotros mismos ¿cómo podemos pretender actuar de distinta forma con el resto de personas? Existe una expresión en nuestro idioma que tiene el gran poder de generar sentimiento de vergüenza y culpa. Es una expresión que parece inofensiva y usamos bastante para evaluarnos, está tan arraigada en nuestra conciencia que nos parece imposible prescindir de ella. Se trata de la expresión “debería“. Cuando la usamos en la mayoría de las ocasiones nos estamos resistiendo a aprender ya que esta expresión implica que no había otra opción. Los seres humanos tenemos la necesidad de tener que elegir y cuando escuchamos una exigencia del tipo que sea solemos resistirla, incluso una exigencia camuflada con un debería. Recompensas Cuando actuamos estamos buscando de alguna forma una recompensa, bien puede ser dinero o simplemente conseguir la aprobación. En el segundo caso ocurre porque en nuestra cultura nos han inculcado que con cada “buena” acción obtendremos una “recompensa”. De niños nos han ensñado que después de una acción “buena” nos responderán con un “eres un buen niño” y una sonrisa, esto hace que de adultos seamos adictos a ello y nos engañemos creyendo que la vida consiste en obtener recompensas por cada cosa que hagamos. Con ello hacemos todo lo que haga bien para otras personas y evitamos todo lo que podría provocar desagrado o castigo. De esta manera lo único que hemos hecho ha sido vivir “comprando” el amor de otros en lugar de enriquecer la nuestra propia vida. Y lo más importante, si logramos optar por esta segunda parte y enriquecer la vida veremos que los demás nos lo agradecen. Este agradecimiento nos dará la auténtica alegría que ninguna aprobación de los demás conseguirá jamás. Al igual que este sentimiento de buscar aprobación existen otros como: - Escapar del castigo (miedo) - Evitar vergüenza - Evitar sentimiento de culpa - Porque es un deber

La recepcion empatica

Parafrasear lo que nos ha dicho el interlocutor puede ser de ayuda ya que al hacer esto estámos comprendiendo y empatizando con el mensaje que trata de darnos. Es por ello que en ocasiones sea una buena práctica parafrasear el mensaje que acaba de transmitirnos. ¿Cuando debo parafrasear y cuando no? Esto no se puede medir o dar una regla exacta, tenemos que saber en cada situación como actuar en función de distintos factores. Al final quien nos está dando el mensaje lo que quiere es que le prestemos atención y lo comprendamos, es por ello que confirmar que entendemos sus motivaciones y sentimientos al dar este mensaje ayuda a conectar mejor con él/ella antes de lanzarnos como locos a dar consejos o soluciones. Primero comprender después aconsejar. No empecemos con consejos rápidos, mensajes tranquilizadores, tratar de explicar cual es nuestra opinión o cómo nos sentimos antes de empatizar. Para dar empatía necesitamos tenerla Puede que esta frase suene absurda pero la realidad es esa, si queremos mostrar empatía hacia una persona primeramente necesitamos tener empatía. En caso de que no tengamos o no la suficiente podemos simplemente decirlo, podemos decirle que no podemos empatizar con él/ella y de esa forma la persona ve que queremos comprender su mensaje. Esto puede ser más que suficiente para que nos ofrezca lo que necesitamos.

Lo que pedimos a los demas para enriquecer nuestra vida

¿Cómo hago lo que no hay que hacer? Lo único que sé es que no quiero hacer lo que no hay que hacer La letra de esta canción pone de relieve los dos problemas que surgen cuando alguien pide una cosa en forma negativa. La gente se confunde y no sabe qué se le pide en realidad. Además, lo más probable es que las peticiones negativas provoquen resistencia en la persona que las recibe. ¿Cómo expresamos los que queremos pedir para conseguir que los demás respondan a nuestras necesidades de manera compasiva? La clave es expresar peticiones de manera positiva, es decir, evitar decir lo que no queremos. Porque en cierta medida el ser humano es vago y no queremos tener que estar pensando que solución darle a la petición de una persona que nos ha dicho que es lo que no quiere. Mejor si nos dice que es lo que quiere realmente y nosotros actuamos en consecuencia. ¿No es así? De todas formas ya no solo por vagueza, no existen dos personas que piensen de igual forma. Por ello es muy fácil que nosotros demos una idea ambigua o no exacta, como puede ser decir lo que no queremos y que la otra persona interprete una manera que no se nos había pasado por la cabeza. En el libro cuenta como una de sus alumnas en un taller estaba disgustada porque su marido pasaba mucho tiempo trabajando. Para mostrarle este sentimiento le dijo: “Me gustaría que pasaras menos tiempo con el trabajo”. Lo que ocurrió fue que su marido a la semana siguiente haciendo caso de su petición se apuntó a un torneo de golf. Esta chica cuenta como no era lo que esperaba, es más, era aún peor ya que lo que ella quería realmente era que su marido pasara más tiempo con ella y su hija. Tras el curso fue nuevamente a hablar con él y esta vez de manera afirmativa le dijo: “Pasas demasiado tiempo en el trabajo y eso me deprime, me gustaría que al menos una vez por semana pasaras una tarde con los niños y conmigo”. Esta vez tuvo un resultado mejor. De igual manera un lenguaje vago hace que la otra persona interprete lo que mejor le parece a él y casi nunca coincide con lo que nosotros queríamos en realidad. Es por eso que frases como: “Me gustaría que fueras más responsable”, “Quiero que dejes ser quien soy”, etc. sean tan vagas que no sabemos realmente porque lo dicen. Si en lugar de eso dijésemos otras frases como: “Quiero que cuando te diga que hagas algo lo hagas sin rechistar y con una sonrisa” en lugar de simplemente “ser mas obediente” no habría lugar para la interpretación. O “Quiero que cuando hago las cosas bien me digas con una sonrisa lo bien que lo hago” en lugar de “quiero que me dejes ser quien soy” al igual que en el ejemplo anterior dejamos claro cual es nuestro deseo y como queremos que se cumpla. De esta manera la otra persona no puede hacer una mala interpretación y aún mejor no tendrá que pensar como interpretarlo o que hacer al respecto. Un lenguaje vago aumenta la confusión interna. La depresión es el premio que obtenemos por ser ‘buenos’. Por otro lado es igual de peligroso pedir algo sin mostrar sentimientos, en ese caso las peticiones pueden sonar como exigencias. Esto hace que hacia quien va dirigida la petición no la reciba de la mejor forma, por ejemplo unos padres que dicen a su hijo: “¿Y si te cortas el pelo?” suena a exigencia por parte de ellos, pero si cambiasen el mensaje a algo similar a: “Me preocupa que tengas el pelo tan largo y no veas bien, sobre todo cuando vas en bicicleta. ¿Y si te cortas el pelo?” Solicitemos la confirmación de nuestras palabras Por raro que pueda parecer, sobre todo al principio debemos pedir a nuestro interlocutor que nos confirme el mensaje que le hemos dado. En muchos casos veremos que con la confirmación hemos expresado mal algunas partes del mensaje y ha interpretado lo que ha creído conveniente. También es importante que agradezcamos cuando se nos confirma el mensaje, además de brindarle empatía a quien sabemos que no quiere confirmar el mensaje. Las peticiones a un grupo En ocasiones empezamos un debate de grupo y la misma persona que lo inicia no sabe como acabarlo. Esto ocurre porque muchas veces ni la misma persona que lo inicia sabe cuando acabarlo porque ni el mismo sabe que esperar de esa conversación que se ha iniciado. Es por esto que es importante que antes de comenzar un debate pensemos que queremos obtener de hacer esta pregunta o comentario, el resto de personas no lo saben, así que al menos debemos saberlo nosotros mismos y debemos dejarlo claro al comenzar. Y, en el mejor de los casos, cuando realmente si conoce que quiere conseguir del tema que él/ella mismo ha sacado no sabemos como parar. En la india existe un término llamado “bas” que se usa para esto, cuando la persona que ha iniciado un tema ya está conforme con la respuesta recibida por parte del grupo dice “bas” y se puede dar por concluida para poder continuar con otro tema. En nuestra lengua no existe tal término pero podemos indicar de otra forma que ya estamos conformes. Las peticiones versus las exigencias Es difícil saber diferenciar entre una exigencia y un petición, para ello tendremos que observar que hace el interlocutor si no se cumple la petición. Por ejemplo, supongamos que Pepe quiere que María se quede con él esta tarde porque se siente solo. Veamos la situación de dos formas distintas. Situación 1: Pepe: Me siento solo y me gustaría que pasaras la tarde conmigo María: Mira, Pepe, estoy muy cansada. Si quieres compañía, ¿que te parece si se lo pides a otra persona? Pepe: (enfadado) ¡Tan egoísta como siempre! Situación 2: Pepe: Me siento solo y me gustaría que pasaras la tarde conmigo María: Mira, Pepe, estoy muy cansada. Si quieres compañía, ¿que te parece si se lo pides a otra persona? Pepe: (se queda sin saber que responder) María: (nota que está disgustado) ¿Estás enfadado? Pepe: No María: Vamos, Pepe, ¿que te pasa? Pepe: Sabes perfectamente que me siento muy solo. Si me quisieras de verdad, esta tarde te quedarías conmigo. Como podemos ver en ambas situaciones Pepe intenta hacer sentir mal a María de manera que trata de exigir que pase con ella la tarde. Sin embargo si Pepe respondiese, por ejemplo: “María, ¿estás agotada y necesitas descansar esta tarde?” Escuchando la respuesta y preocupándose por ella, estaríamos viendo una petición. Es una exigencia si nuestro interlocutor intenta que nos sintamos culpables

Asumir la responsabilidad de nuestros sentimientos

Asumir la responsabilidad de nuestros sentimientos Otro de los componentes de la comunicación no violenta es reconocer el origen de nuestros sentimientos. Nuestros sentiemientos son el resultado de cómo elegimos tomarnos lo que dicen y hacen los demás. Cuando alguien nos transmite un mensaje negativo, tenemos cuatro opciones con respecto a la manera de recibirlo. Una es tomárselo de manera personal, captando en él acusaciones y críticas. Por ejemplo, alguien nos dice: “¡Eres la persona más egocéntrica que he conocido en mi vida!“ Nos lo podemos tomar de las siguientes formas y responder de estas formas: Personal: “Sí, debería ser más sensible con los demás”, aceptamos el punto de vista del otro y nos echamos la culpa. Lo más probable es que en ese momento nos sintamos culpables, avergonzados y deprimidos. Culpa del otro: “No tienes derecho a decirme esto. Siempre tengo en cuenta tus necesidades. ¡Tú eres el egocéntrico!”, lo más probable es que sintamos rabia en este momento. Nuestros sentimientos: “Cuando me dices que soy la persona más egocéntrica que conociste en tu vida, me siento herido, porque yo querría que reconocieras los esfuerzos que hago para tener en cuenta tus preferencias”, al centrarnos en nuestros sentimientos y necesidades, tomamos conciencia de que sentirnos heridos en esta circunstancia viene dado de la necesidad de que nos reconozcan los esfuerzos que hacemos. Sentimientos del otro: si tenemos en cuenta los sentimientos que puede sentir la otra persona y tratamos de comprenderlos, podríamos preguntarle, por ejemplo: “¿Te sientes herida porque necesitas que se tomen en cuenta tus preferencias?“ Distinguir entre dar desde el corazón y estar motivados por la culpa Existen algunas expresiones de uso común que tienden a enmascarar la responsabilidad por nuestros propios sentimientos. Como pueden ser las de carácter impersonal: “Me indigna descubrir faltas de ortografía en los folletos destinados al público.” “Este tipo de cosas me saca de quicio”. Afirmaciones que sólo hacen referencia a lo que hacen los demás: “Me duele que no me felicites el día de mi cumpleaños”. “Si no te terminas la comida, mamá se pondrá muy triste.“. Usar la expresión “me siento … X porque … Y” siendo ´*Y*´ algo distinto de “yo“: “Me siento triste porque dijiste que no me quieres”. “Estoy enojado porque la supervisora no cumplió lo prometido” En todos estos ejemplos podemos ahondar más en la conciencia de nuestra responsabilidad utilizando la expresión: “Me siento … porque yo …“ “Me indigna descubrir faltas de ortografía en los folletos destinados al público porque me gustaría que nuestra empresa proyectase una imagen profesional.” “Si no te terminas la comida, mamá se siente decepcionada, porque quiero que crezcas fuerte y sano.” “Estoy enojado al ver que la supervisora no cumplió su promesa, porque yo había contado con ese fin de semana largo para ir a visitar a mi hermano.”

Dar desde el corazón

Dar desde el corazón Los 4 componentes de la CNV: 1. Observación 2. Sentimiento 3. Necesidades 4. Petición Por ejemplo una madre podría manifestar esos tres aspectos del proceso diciéndole a su hijo adolescente: “Félix, me molesta (sentimiento) ver dos calcetines sucios hechos una bola debajo de la mesita del café (observación) y otros tres al lado del televisor, porque estoy necesitando (necesidad) más orden en las habitaciones de la casa que compartimos” Acto seguido la madre abordaría el componente número cuatro, que es una petición muy específica: “¿Estarías dispuesto a recoger los calcetines y llevártlos a tu habitación o meterlos en la lavadora?” Con este método nos limitamos a decir hechos concretos y como nos sentimos, respecto a estos. De esta forma no estamos dando pié a la discursión, pues nuestros sentimientos son irrevatibles, nadie puede saber como nos sentimos para entrar en una discursión. Además de esto añadimos una solución, de manera que le damos a la otra persona la solución del problema para que no tenga que pensar si quiera. Actos concretos que observamos que están afectando nuestro bienestar. Cómo nos sentimos en relación con lo que observamos. Las necesidades, los valores, los deseos, etc., que dan origen a nuestros sentimientos. Las acciones concretas que pedimos para enriquecer nuestra vida. Es importante tener en cuenta que la comunicación no violenta, no consiste únicamente en como nos expresamos respecto a los demás, también tiene un importante segundo componente. Tenemos que escuchar de manera empática a la segunda parte de la conversación. La CNV tiene dos partes: 1. Expresión honesta mediante los cuatro componentes. 2. Recepción empática mediante los cuatro componentes. De nada sirve comunicar lo que queremos si luego no tenemos en cuenta la opinión del otro, no solo escuhándola, si no teniendo en cuenta lo que dice y empatizando con él. Pudiendo llegar a entender su razonamiento y porque siente que tiene más validez que el nuestro.

Identificar y expresar los sentimientos

Identificar y expresar los sentimientos En la actualidad se nos educa para orientarnos hacia los demás más que para estar en contacto con nosotros mismos. Tenemos metida en la cabeza la siguiente pregunta: “¿Qué quieren los demás que yo diga y haga?” Una vez tenía un compañero de habitación que ponía la música a un volumen tan alto que no me dejaba dormir, y me preguntaron que era lo que sentía con eso. A lo que respondí: “Siento que por la noche no habría que poner la música tan alta”. Al decir la palabra “siento” seguida de “que”, la oración, pese a incluir el verbo “sentir”, en realidad no expresa mis verdaderos sentimientos, no tan solo opinión. Al tratar de expresar nuevamente mis sentimientos, dije: “Siento que si una persona se comporta de esta manera es porque sufre un transtorno de personalidad”. Pero con esto no mejoraba la cosa seguía siendo una opinión y no un sentimiento. Tras reflexionar un rato, me di cuenta de algo y respondí: “No siento absolutamente nada”. Era obvio que si tenía sentimientos al respecto, pero lamentablemente no sabía cómo darme cuenta de ellos, y mucho menos, cómo expresarlos. Los sentimientos versus la ausencia de sentimientos El lenguaje suele dar pie a confusiones, como cuando utilizamos el verbo “sentir” cuando en realidad no estamos expresando un sentimiento. Por ejemplo, en la frase: “Siento que no he hecho un buen trato”, sería más adecuado decir “creo” que “siento”. En general, no expresamos claramente nuestros sentimientos cuando en una oración, después de “siento”, utilizamos palabras como las siguientes: que: “Siento que tú deberías haberlo sabido”. como: “Me siento como un fracasado” como si: “Siento como si viviera con una pared” Por otro lado, no es indispensable usar la palabra “sentir”. Podemos decir en lugar de “me siento irritado”, “estoy irritado”. Distingamos entre lo que sentimos y lo que pensamos de la reacción o comportamiento de los otros hacia nosotros También es importante diferenciar cuando describimos lo que creemos que otras personas perciben de nosotros y la realidad. “Siento que soy insignificante para mis compañeros en el trabajo” La palabra “insignificante” describe cómo creo que los demás me evalúan, en lugar de ser un sentimiento verdadero, como podría ser: “me siento triste” o “me siento desalentado”. incomprendido, indica la valoración que hago del nivel de comprensión de la otra persona en lugar de expresar un sentimiento real. Sería mejor decir “ansioso”, “molesto” o cualquier otra emoción. ignorado, otra vez más es como creo que los demás actúan sobre mi en lugar de como me siento. Cómo estos ejemplos existen otras palabras, a continuación dejaré una muestra de unas cuantas de ellas. abandonado despojado manipulado acorralado despreciado obligado amenazado estafado olvidado atacado excluido presionado atrapado explotado rebajado coaccionado forzado rechazado degradado humillado subvalorado desamparado incomprendido superfluo desatendido intimidado traicionado desdeñado invisible ultrajado desfavorecido maltratado utilizado Cualquiera de estas palabras en la frase anterior podría encajar pero ninguna de ellas expresan realmente un sentimiento de que sentimos en ese momento.

La comunicación que bloquea la compasión

La comunicación que bloquea la compasión La comunicación que nos aliena de la vida surge de las sociedades jerárquicas o de dominación, y las sustenta. Cuando los pueblos están controlados por un número pequeño de individuos que buscan el beneficio propio, a los reyes, zares, nobles, etc., les resulta muy útil que las masas se eduquen con una mentalidad de esclavos. A tal efecto, el lenguaje de lo incorrecto y de expresiones como “deberías” y “tienes que” es totalmente adecuado para ese propósito: cuanto más acostumbramos a las personas a pensar en términos de juicios moralistas que implican lo que está mal o incorrecto, tanto más aprenden a mirar hacia fuera de sí mismos. la comunicación que aliena de la vida tiene profundas raíces filosóficas y políticas Los juicios moralistas Los juicios moralistas, ciertas formas de comunicación nos alienan de nuestro estado natural de compasión o solidaridad. Estos juicios se reflejan en comentarios como: “Tu problema es que eres muy egoísta”, “Eres una perezosa”, “Están llenos de prejuicios”, “Esto es inapropiado”. Echar la culpa a laguien, insultarlo rebajarlo, ponerle etiquetas criticarlo, establecer comparacion y emitir diagnósticos son distintas maneras de formular juicios. Es importante no confundir los juicios de valor con los juicios moralistas. Todos hacemos juicios de valor con respecto a las cosas de la vida que estimamos. Podemos valorar, por ejemplo, la honradez, la libertad o la paz. Los juicios de valor reflejan nuestras creencias con respecto a cómo podría mejorar la vida. En cuanto a los juicios moralistas, los hacemos en relación con las personas y conductas cuando no concuerdan con nuestros juicios de valor. Decimos, por ejemplo, “La violencia es mala. Quien mata a otro ser humano es malvado”. En lugar de usar “Me asusta el uso de la violencia para resolver conflictos; yo valoro el empleo de otros medios en la resolución de los conflictos humanos” Clasificar y juzgar a las personas promueve la violencia Las comparaciones son una forma de juicio Negación de la responsabilidad El lenguaje que solemos usar oscurece la conciencia de nuestra responsabilidad personal El uso de una expresión tan abitual “tener que”, como en el caso de la afirmación: “Te guste o no, tienes que hacerlo”, ilustra hasta qué punto nuestra responsabilidad personal por nuestras acciones se ve oscurecida por esta manera de haber. En cuanto a la expresión “hacer sentir”, como en el caso de “Me haces sentir culpable”, constituye otro ejemplo más de cómo el lenguaje nos allana el camino para que podamos negar nuestra responsabilidad personal con respecto a lo que sentimos y a lo que pensamos. Negamos la responsabilidad de nuestros actos cuando atribuimos su causa a: - Fuerzas difusas e impersonales: “Limpié mi habitación porque tenía que hacerlo“ - Nuestro estado de salud, un diagnóstico o nuestra historia personal o psicológica: “*Bebo porque soy alcohólico*” - Lo que hacen los demás: “Le pegué a mi hijo porque cruzó la calle corriendo“ - Órdenes de la autoridad: “Mentí al cliente porque mi jefe me dijo que lo hiciera“ - Presiones de grupo: “Empecé a fumar porque todos mis amigos lo hacían“ - Políticas, normas y reglas institucionales: “Tengo que expulsarte por esta infracción porque es la política de la escuela“ - Los roles asignados según sexo, posición social o edad: “Me fastidia ir a trabajar, pero tengo que hacerlo porque soy marido y padre”. - Impulsos irrefrenables: “Me superaron las ganas de comer bombones y me los comí“ Cuando pensamos que “alguien merece algo” bloqueamos la comunicación compasiva

Observar sin evaluar

Observar si envaluar Si combinamos la observación y la evaluación seguramente la otra persona escuchará una crítica La comunicación no violenta, no nos dice que seamos totalmente objetivos ni tampoco que nos abstengamos de hacer evaluaciones. Lo único que nos dice es que mantengamos una separación entre nuestras observaciones y nuestras evaluaciones. | Comunicación | Ejemplo observación con evaluación | Ejemplo observación sin evaluación | | :———– | :————————————- | :————————————- | | Uso del verbo “ser” sin indicar si la persona que evalúa acepta o no la responsabilidad de la evaluación | Eres demasiado generoso | Cuando te veo darle a alguien el direno para tu almuerzo, creo que eres demasiado generoso | | Uso de verbos con connotaciones evaluativos | Pepe siempre posterga las cosas | Pepe sólo estudia para los exámenes la noche anterior | | Dar por sentado que las inferencias que uno hace de las ideas, los sentimientos, los proyectos y los deseos de otra persona son las únicas posibles | No terminará el trabajo a tiempo | No creo que termine el trabajo a tiempo | | Confundir una predicción con una certeza | Si tu alimentación no es equilibrada, vas a enfermarte | Si tu alimentación no es equilibrada, temo que te enfermes | | No ser específico al citar ejemplos | Las minorías no cuidan su vivienda | No he visto que la familia que vive en el número 123 de la calle Pepe retire la nieve de la acera de su casa | | Usar palabras que implican habilidad sin precisar que se hace una evaluación | Pepe Pérez juega mal al fútbol | Pepe Pérez no ha marcado un gol en veinte partidos | | Usar adverbios y adjetivos de maneras que no indiquen que se hace una evaluación | Pepito es feo | No encuentro a Pepito físicamente atractivo | Palabras como “a menudo” o “rara vez” contribuyen también a confundir la observación con la evaluación. | Evaluaciones | Observaciones | | :————- | :————- | | Raras veces haces lo que quiero | Las tres últimas veces que empecé una actividad, me dijiste que no querías hacerla | | A menudo viene sin avisar | Viene sin avisar al menos tres veces por semana |

Feeders

Este proyecto comenzó a inicios de 2016, un amigo (Pablo) y yo queríamos tener dónde poder ver los partidos oficiales de League of Legends de las distintas ligas. Porque hasta ese día si queríamos ver algo era ir uno a la casa del otro y no siempre teníamos la casa disponible. Nuestra idea era hablar con algún local que nos quedase cerca a ambos si nos dejaba los Jueves, que por aquel entonces eran los días de los partidos de la liga española (LVP), poner en una tele estos partidos e ir allí con más gente. Este plan era simplemente tener dónde ir y llegar a un acuerdo con el local, ya que le llevaríamos más clientes. Comenzamos haciendo encuestas masivas, para ver si éramos los únicos que teníamos ese problema y vimos que no. Cuando estábamos decididos a comenzar a hablar, ya teníamos buscados un par de locales a los que preguntar aparece un tercer conocido común (Chema). El cual estaba paralelamente planeando algo similar, pero más grande sin nosotros saberlo. Él quería montar un local especializado en deportes electrónicos dónde ver los partidos mientras te tomas algo, pero además tener una zona del mismo local dedicada al entrenamiendo, para que los equipos locales puedan tener donde realizar sus entrenamientos. Además de esto tener también merchandising y demás cosas. Nos reunimos los tres y Chema nos cuenta que está en una aceleradora de proyectos (El laboratorio FEET) en la Universidad de Las Palmas de Gran Canaria, dónde yo también estaba estudiando el grado en informática, con este proyecto y que si nos interesaba podría hablar con quienes llevan esta acelaradora y entrar en ella con él como compañeros del mismo proyecto, y así lo hicimos. Con esto empieza nuestra aventura en El laboratorio FEET. Una vez en el laboratorio éramos varios grupos con grandes ideas las cuales se irian formando y moldeando con el tiempo con la ayuda de las profesoras que lo llevan Rosa y Pino. Además de las distintas visitas que fuimos teniendo de profesionales que nos contaban su experiencia y nos daban su punto de vista para ayudarnos a mejorar aún más las ideas que teníamos. Lo que yo no esperaba es que después de estos meses las de las ideas con las que llegamos, solo quedaran el recuerdo. Habíamos dado tantas vueltas mejorado, cambiado, quitado tanto que era ya otro proyecto diferente. Habíamos hecho nuestro análisis DAFO, nuestros business canvas model, ya teníamos un plan de acción, un análisis de gastos, todo estába a punto para el demo day. Fueron unos meses muy intensos 🥵 salir de clases a las 14:30 e ir corriendo al laboratorio porque a las 15 o 16 empezaba la sesión, recuerdo que aunque de alguna forma todos estábamos compitiendo por conseguir la financiación final todos eramos compañeros en esta aventura y nos apoyábamos en todo lo que podíamos. Y tras estos meses llegó el gran día, el demo day. Ese día tendríamos que presentar lo que teníamos, no dejarnos nada en el tintero. Explicar a los empresarios que habían venido a vernos todo y convencerlos de que nuestra idea tenía futuro para que nos concedieran el dinero con el que comenzarla. Y así hicimos, cada grupo dio todo lo que tenía y no ganamos el premio… Se lo llevó una de nuestras compañeras, pero hubo una sorpresa. El grupo dinosol decidió darnos el capital semilla que habíamos solicitado para arrancar el proyecto, ¡¡nos habían dado 15.000€!! no nos lo creíamos 🎉🎉 Tuvimos varias reuniones con Olivia Llorca Afonso y Javier Puga Santana quienes nos habían concedido el premio, yo aún no me lo creía, esto fue a principios del verano de ese mismo año. Y habíamos aclarado con ellos que nuestro siguiente paso era constituirnos como empresa y comenzar a hacer eventos y organizar ligas regionales, ellos nos dejarían el local y nos ayudarían con todo el material que nos hiciera falta. Y así empezamos a hacer, comenzamos cada uno a trabajar en su parte. Pablo, redactó toda la normativa de las distintas ligas que tendríamos, como serían los torneos, el sistema de puntuaciones, como gestionaríamos todo. Por mi parte monté una web con PHP en la que se podrían registrar los equipos que quisieran participar y todos sus integrantes, gestionar los puntos de las ligas, hacer torneos y gestionar los enfrentamientos y un apartado de noticias. Y por otro lado… Chema, estaría gestionando los papeles para darnos de alta como empresa, o mejor dicho debería de haberlo hecho… 😔 Este fue el punto en el dejamos escapar la oportunidad. Todo esto que he contado que fuimos haciendo Pablo y yo fue durante todo el verano, verano en el que Chema estuvo de vacaciones de viajes dándonos largas. “Si, lo estoy mirando”, “Aún no podemos, en verano van más lentos”, “Ahora es que no estoy en el país”, etc. Y pasaron los días y los meses y llegó septiembre cuando habíamos acordado con Olivia y Javier tener todo para abrir cara al público el proyecto. Pero no pudimos, no teníamos la empresa y no nos podían ayudar. Le contamos lo que nos había pasado y nos dieron un mes más, en el que Pablo y yo estábamos convencidos de que Chema, al fin, terminaría con este papeleo. Pero sorpresa, nos enteramos de que se va de erasmus a Italia y se va en los primeros días del mes. No nos lo creíamos, le llamamos, le mandamos mensajes de todo y nunca nos respondió. Y el proyecto se quedó ahí, se quedó de lado y a día de hoy grupo dinosol lo ha sacado por su cuenta con la ayuda de unos chicos de tenerife con los que nosotros estuvimos hablando para empezar a operar en ambas islas el proyecto. Y me alegro, al menos la idea no quedó en nada y han conseguido seguir adelante con ella. Ciertamente, me da pena no voy a mentir. Me da pena no haber sido capaz de sacar este proyecto adelante y todo por no saber tomar una desición tan “fácil” como “Chema si sigues así no podemos seguir trabajando juntos”. En aquel momento no fui capaz de apartarlo sabiendo desde la primera reunión con el Olivia y Javier que Chema no quería seguir con el proyecto. Recuerdo como si fuera hoy al salir de la reunión nos quedamos Pablo y yo y le dije: “Pablo, Chema se quiere ir del proyecto. No va a continuarlo, algo le pasa” y aún sabiendo esto, no fui capaz de dar el paso. Puede que en parte porque él había sido quien nos “dió” de alguna forma todo, fue quien nos llevó al laboratorio y era quien conocía toda la parte de empresa de alguna manera no me veía en la posición de “hecharlo”. Lo cosideraba por todo esto alguien “superior” en el grupo que formábamos. Pero bueno me da pena pero por otra parte me alegra bastante, porque he visto como he sido capaz con ayuda de sacar una idea y en menos de un año montar algo con lo que conseguir 15.000€. Además saber que la idea ha sido buena y está funcionando. Y ya puestos aprendí que aveces hay que saber decir “hasta aquí” y cortar la relación de trabajo. La verdad que estoy contento en cierto modo, quien sabe igual si hubiese reaccionado bien y estuviera ahora con ese proyecto no estaría a día de hoy donde estoy. Siendo este uno de los mejores años de mi vida, ¡¡me han ocurrido grandes cosas!! Que ya les contaré. 😜

Ver recursos del sistema en tiempo real

Instalar indicator-multiload sudo apt install indicator-multiload Iniciar el programa. Por defecto, te mostrara un grafico en la barra superior del sistema. Si pulsamos sobre él y nos dirigimos a preferencias podremos seleccionar que quiere que se muestre en el gráfico, personalmente no uso esta parte. Pero si nos vamos a Indicator items... aqui esta la de lo que se esta mostrando en mi barra. – CAPTURA DEL ESCRITORIO INDICANDO QUE BARRA HABLO Si nos fijamos hay varios elementos de ejemplo. El que se muestra en la barra es el primero de todos. Y este es el que editaremos a nuestro gusto. – CAPTURA PROGRAMA En mi caso quiero que se muestre todo CPU, RAM, descarga y subudaen, es por eso que lo edito y pongo lo siguiente 🐐 $(percent(cpu.inuse)) | $(size(mem.user)) | $(speed(net.down))⬇ $(speed(net.up))⬆ Con esto se verá 🐐 9% | Men 5,5 GB | Net 12kb/s⬇15kb/s⬆ en todo momento en nuestra barra del sistema y podremos saber como se encuentran nuestros recursos. Toda la información de como configurar e instalar lo he sacado de este post

Cambiar mensaje del bash

Cambiar el mensaje del bash Típicamente tu bash en linux tendrá la siguiente extructura nombre-de-usuario@nombre-de-maquina/host:directorio-actual~$ cristian-suaver@maquina-linux-de-criskrus:/Descargas~$ Esto puede llegar a se mucho y taparnos la pantalla casi por completo es por ello que lo podemos cambiar a nuestro gusto. Para hacer esto cambio tenemos que modificar una variable llamada PS1, si queremos hacerlo de manera que persista en el sistema lo haremos desde el archivo .bashrc en nuestro directorio home (~). Una vez abierto buscamos donde declara la variable y lo modificamos a nuestro gusto. En mi caso ha pasado a quedar da la siguiente forma if [ "$color_prompt" = yes ]; then PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]👉criskrus@\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' #PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' Simplemente he borrado el nombre de usuario que tenia en la maquina \u y he puesto 👉criskrus y tambien he borrado el nombre del host \h, de tal forma que ahora al abrir mi terminal me aparecera de la siguiente forma. 👉criskrus@:~/Descargas$ Mas limpio y con menos ruido Referencias Más información sobre las variables y opciones disponibles en la documentación

Absence validator

Validador de ausencia (absence) En Ruby on Rails existe un validador con el nombre de absence, este la primera vez que lo vi pensé “¿Para que quiere comprobar que un valor no existe?” Pero luego leyendo este post me ha parecido útil a la hora de definir campos en el modelo que se encuentran en la base de datos y esta los genera automáticamente. Como por ejemplo un ID class User include ActiveModel::Validations attr_accessor :id, :name validates :id, absence: true end De esta forma modelamos que el usuario tiene un ID pero no puede ser impuesto desde el la aplicación, lo dejamos para la base de datos. De tal manera que si comprobamos si el objeto que hemos creado es válido o no, uno de los puntos que tendrá en cuenta será que tiene que tener el ID en este caso vacío. user = User.new user.valid? # => true user.id = 1 user.valid? # => false Este ejemplo no es bueno ya que el ID lo genera rails automáticamente y debe de estar presente siempre en la base de datos. Esto generará un problema si hacemos lo siguiente. user = User.new user.name = 'Pepe' user.save # save on database user = User.last # id: 1 name: Pepe user.name = 'Antonio' user.save # error id should be blank user.id = nil # error from database, id can't be null Al tener la restricción en el modelo de que el ID tiene que ser vacío en el momento de la creación y el primer guardado en la base de datos no hay problema pero si cargamos un usuario para modificarlo nos obliga la aplicación a que sea vacío el ID y al guardarlo en la base de datos no puede ejecutar la sentencia SQL. Referencias Documentación oficial sobre absence Post aclarativo en español.

Ejecutar un solo test

Ejecutar solo un test En nuestros test podemos definir etiquetas, si queremos ejecutar un solo test podemos definir una que sea :focus de manera que en el test que queramos que se ejecute hacemos :focus => true describe "group with tagged specs" do it "example I'm working now", :focus => true do; end it "special example", :type => 'special' do; end it "slow example", :skip => true do; end it "ordinary example", :speed => 'slow' do; end it "untagged example" do; end end Si ahora cuando ejecutamos nuestros test hacemos rspec . --tag focus nos ejecutara solamente el test con el :focus => true en nuestro caso el primero solamente. Si hacemos rspec . --tag type:special en ese caso solo nos ejecutara el segundo test. Para este ejemplo estamos usando rspec toda la documentación sobre etiquetas aquí

Test en ruby

Test en Ruby Artículo original en rspec-info Instalar la gema Primero creamos un archivo Gemfile en la raiz de nuestro proyecto con al menos el siguiente contenido source "https://rubygems.org" gem 'rspec', '~> 3.0' Luego instalamos las gemas que hemos indicado en nuestro fichero Gemfile mediante bundle install --binstubs Con todo instalado iniciamos la suite de test bin/rspec --init Creamos nuestro primer test require 'rspec' describe 'My first test' do it 'should do works fine' do expect(true).to be_truthy expect(true).to be true end end Y lo ejecutamos desde la raiz del proyecto añadiendo la opción --format doc hacemos que sea mas amigable, mostrando los nombres de los test que han pasado en verde y los que no en rojo. Si no ponemos esta opción se mostrará una línea de puntos verdes y rojos para indicar los test que han pasado y los que no. bin/rspec --format doc

Resumen segundo trimestre

Hola voy a intentar ponerte al día lo más rápido que pueda de estos cuatro meses, pero te aviso han dado para mucho y han sido muy moviditos jajaja Grado Primero lo primero, he acabado el grado en ingeniería informática. Ya he presentado el trabajo fin de grado y todo correcto me he orlado y ya puedo decir adiós a la universidad, bueno casi… Aún tengo que ir a solicitar el título que no he tenido lugar y ahora entenderás porque. Trabajo/beca CEDEI El porque es el siguiente, el CEDEI sacó una beca para participar en un programa de aprendices con algunas empresas, entre ellas LeanMind donde había realizado mis prácticas univeritarias y continuaba en contacto, fueron ellos quienes me avisaron. Solicité la beca y nos seleccionaron a los tres compañeros de prácticas en ese momento (Rubén y Jorge) con la “sorpresa” de tener que mudarnos a Tenerife por 6 meses. Lo se no está lejos y, lo se no es mucho tiempo, pero dudé un poco al principio por tener que enfrentarme a una vida “solo” lejos de la familia pero aún así acepté y aquí estoy muy contento de ello, casi al final de esta beca (me quedan apenas dos meses). Cuerpo y mente En cuanto a los objetivos marcados para este año van “bien”, aunque como digo siempre podrían ir mejor. El ejercicio diario está un poco raro, voy al gimnasio con Rubén dos o tres veces por semana pero hay ocasiones en las que nos es imposible, este apartado está un poco de lado… Lo que si es cierto, con mis más y mis menos, es que he logrado mejorar mi concentración a la hora de realizar las tareas del día a día. Me centro con mucha más facilidad, el problema que tengo ahora es poder organizar todo lo que tengo que hacer. He pasado por muchas fases, todo en trello, todo en google keep, en papel, mezcla de todo… Y lo único cierto es que no termino de encontrar el punto, espero encontrar la manera. Blog Pero no todo es malo, estoy logrando publicar semanalmente en mi blog artículos escritos por mí que considero interesantes. Incluso dos veces por semana. Aún no termina de gustarme como está estéticamente y como está organizado todo pero creo que eso no o lograré nunca siempre cambiaré de gustos por ahora funciona y me vale. Comentar que he comprado dominio y hosting (compartido) para tener el blog. Algo que por mucho que te informes y preguntes siempre hay alguien que te cuente malas experiencias sobre el que estábas interesado y nunca terminas de estar seguro del todo ajaja Proyectos Y por último el pasado mes de marzo se celebró un Hackatón en Las Palmas en el cual nuestro equipo consiguió con WikUp un tercer premio en la categoría general y otro en menciones especiales (cuento toda mi experiencia en un post sobre ello). Además en este evento hubo otro proyecto por el que me vi interesado Hack4Gluten, el cual también se llevó un premio. Y a que viene todo esto, te estarás preguntando. Pues todo esto viene porque por las tardes, fines de semana y cualquier rato que saco libre estoy desarrollando estos dos proyectos. Y la verdad que con bastante ilusión en ambos y ganas han ido progresando yç parece que van por buen camino, hemos pasado las primeras fases y ahora nos han dado una financiación para poder empezar a lanzar lo que tenemos y ver que tal. Veremos en Noviembre cuando sea el día de la demo el resultado. Pero pase lo que pase contento de poder participar en todo esto. Quiero hacer una publicación para cada proyecto explicando como surgió la idea que busca y como se está haciendo en cuanto pueda. Objetivos ⭐⭐⭐⭐⭐ Graduarme en Ingeniería Informática ⭐⭐✴✴✴ Hacer ejercicio 4 veces por semana ⭐⭐⭐⭐✴ Lanzar una aplicación ⭐⭐⭐✴✴ Escribir semanalmente un blog ⭐⭐⭐✴✴ Centrarme en las tareas del día a día

Yield

Yield Artículo completo en ruby-doc.org Una sentencia yield es similar a un callback en JavaScript. Defines un bloque de código y lo pasas como argumento de un método, este segundo que lo recibe lo ejecuta internamente donde quiere mediante el uso de yield. Como se verá en el ejemplo siguiente defines tu método y luego a este le envías un bloque de código que dentro ejecutas dos veces. def twice yield yield end twice {puts("Hello World!")} # Hello World! # Hello World! Además se le pueden pasar parámetros, si lo quisiéramos. En este segundo ejemplo como se trata de un bloque de código de varias líneas lo hacemos con do end en lugar de usar las llaves { }. Para indicarle los parámetros usamos las barras verticales | parametro |. def names yield("Joe") yield("Sandy") yield("Melissa") end names do |name| puts "Hello " + name + ", how are you?" end # Hello Joe, how are you? # Hello Sandy, how are you? # Hello Melissa, how are you?

Blocks procs lambda

Diferencia entre Block, Proc y Lambda Los proc se crean de la siguiente forma: p = Proc.new {|bla| puts "I'm a proc that says #{bla}!" } p = proc {|bla| puts "I'm a proc that says #{bla}!" } Las lambda se crean de la siguiente forma: lmb = lambda {|bla| "I'm also a proc, and I say #{bla}" } also_lmb = ->(bla) { "I'm also a proc, and I say #{bla}" } Diferencias Procs son como blocks, lambdas son como métodos anónimos. Lambdas son estrictas en sus argumentos, si le pasas muchos o pocos argumentos devuelve una excepción. En el caso de blocks y procs si pasas muchos los ignora y si pasas pocos pone los restantes a nil Los return y break en procs y lambdas se comportan diferentes. El return en un proc se ejecuta en el scope donde se definió el bloque. En lambdas return y break devuelven el control al que lo ha llamado. next se comporta igual en los tres casos. Usar la abreviatura de invocación de proc cuando el método invocado es la única operación de un bloque. Ejemplo sacado de la guía de estilos Rubocop names = ["Pepe", "Juan", "Rosa", "Violeta"] # BAD upper_names = names.map { |name| name.upcase } # ["PEPE", "JUAN", "ROSA", "VIOLETA"] # GOOD upper_names = names.map(&:upcase) # ["PEPE", "JUAN", "ROSA", "VIOLETA"]

Filtros

Cuando estamos implementando nuestro controlador en Ruby es posible que en algunos casos tengamos que comprobar antes de cada acción (método) algo como por el ejemplo el caso de la sesión. Es decir, comprobar que el usuario está logeado para poder realizar esa acción concreta o ver las credenciales del mismo para comprobar si tiene permisos para ello. Esto se puede hacer en Ruby de manera sencilla con los filtros (filters). Siguiendo con el ejemplo anterior tendríamos que hacer algo parecido a lo siguiente. class ApplicationController < ActionController::Base before_action :require_login, only: :createItem def index # end def createItem # end private def require_login unless logged_in? flash[:error] = "You must be logged in to access this section" redirect_to new_login_url # halts request cycle end end end De esta forma cada vez que que la aplicación acceda a createItem antes pasará por require_login comprobando que el usuario ha iniciado sesión antes de hacer la acción. Como se ve en el ejemplo también existen modificadores si quieres que solo sea en una acción concreta como en el ejemplo con only:, para que NO se ejecute en ciertas acciones solamente except:. Además del before_action existen también el after_action y el arround_action y como es de esperar en el after se ejecutará después de cada acción. El caso del around es más especial y este se ejecuta entre el before y el after, según he podido leer en este post de stackoverflow.

Constantes

Constantes Las constantes se definen escribiendo la primera letra en mayúsculas, pero normalmente se escriben todas ellas. Si despues de definir esta constante tratas de modificarla te salta un aviso de que estás modificando una constante. MAX_SPEED = 100 MAX_SPEED = 1000 # warning: already initialized constant MAX_SPEED # warning: previous definition of MAX_SPEED was here En cambio si la constante es un objeto y tratas de modificarlo no dice nada (dado que está guardando la referencia a memoria), para poder evitar que nos modifiquen una constante objeto existe el método freeze. De tal manera que si creamos un array por ejemplo podemos inicializarlo y evitar que lo modifiquen la consante pero no el objeto que tenga dentro. TYPES = [] TYPES << "freighter" # can be modified TYPES.freeze TYPES << "freighter" # RuntimeError: can't modify frozen Array TYPES[0].upcase! # FREIGHTER

Variables

Variables en las clases de Ruby Ruby permite cuatro tipos de variables: Variable local, las variables locales son variables que están definidas en un método. Estas variables no están disponible fuera del método. Las variables locales empiezan con letra minúscula o guión bajo (_) variable_name def method foo = Array.new end # foo no esta definida aqui, no existe Variables de instancia, están disponible en todos los métodos de cualquier de cualquier instancia u objeto. Esto quiere decir que las variables de instancia cambian de un objeto a otro. Estas variables están precedidas por un @ seguidas del nombre de la variable @variable_name def method @foo = "Hola!" end puts @foo Variables de clase, están disponible en los diferentes objetos. Una variable de clase pertenece a la clase y es una característica de la clase. Están precedidas por @@ seguidas del nombre de la variable @@variable_name class Foo @@variable = 0 def counter @@variable += 1 puts("Count: #@@variable") end end foo1 = Foo.new foo2 = Foo.new foo1.counter # 1 foo1.counter # 2 foo2.counter # 3 Variables globales, están disponible en todas las diferentes clases. Se definen con $ seguido del nombre de la variable $variable_name class Foo $global_variable = "Global" end class Any def method puts($global_variable) end end any = Any.new any.method # "Global" Las variables creadas con @ crean instancias de variables en cada objeto, esto puede generar problemas si creamos dos módulos que tengan el mismo nombre en algunas variables, estas entrarán en colisión. module MajorScales def majorNum @numNotes = 7 if @numNotes.nil? @numNotes # Return 7 end end module PentatonicScales def pentaNum @numNotes = 5 if @numNotes.nil? @numNotes # Return 5? end end class ScaleDemo include MajorScales include PentatonicScales def initialize puts majorNum # Should be 7 puts pentaNum # Should be 5 end end ScaleDemo.new # output # 7 # 7 Como podemos ver tanto en MajorScales como en PentatonicScales hemos creado una variable con el mismo nombre lo que hace que en nuestra nueva clase ScaleDemo compartan esta variable en ambos, de manera que tienen el mismo valor ambas. Ejemplo completo en phrogz Más infromación sobre variables en tutorialspoint Más información sobre clases en tutorialspoint

Usar PUG en Firebase

Usar PUG en Firebase Si ya tenemos nuestra aplicación funcionando con firebase hosting y firebase functions podemos añadir el sistema de plantillas PUG, por ejemplo, para mostrar las pantallas dinámicamente de manera fácil. Si no tienes tu aplicación ya en funcionamiento con estas tecnologías, en este post muestro como se hace y se automatiza el despliegue. Primero que nada tendremos que añadir la dependencia de PUG npm install pug --save Ahora en nuestro index.js podremos indicarle que estamos usando este sistema de plantillas y posteriormente usarlas. En nuestro caso se encuentra dentro del directorio /functions. app.set('view engine', 'pug'); app.set('views', 'views'); // ... app.get('/', (request, response) => { response.render('index'); }); Y como le hemos indicado en el paso anterior que las vistas están en el directorio /view es ahí donde tenemos que crear nuestro index.pug doctype html html(lang="es") head meta(charset='UTF-8') title= 'Saber y Ganar' link(href='/css/styles.css', rel='stylesheet') script(src='https://www.gstatic.com/firebasejs/5.0.4/firebase-app.js') script(src='https://www.gstatic.com/firebasejs/5.0.4/firebase-database.js') body p Hello World! Si hemos seguido todos los pasos y configurado todo correctamete al abrir nuestra aplicación se nos mostrará nuestra plantilla. Ejemplo Tengo un proyecto de ejemplo de preguntas y respuestas donde uso este sistema de plantillas. Está en el siguiente repo de GitHub.

Despliegue automático en Firebase hosting desde Github

Despliegue automático en Firebase hosting desde Github Para desplegar automáticamente nuestra aplicación en el sistema de hosting de Firebase usaremos una herramienta llamada travis. Crear un fichero en la raiz de nuestro proyecto .travis.yml donde configurar todo lo referente al despliegue. En este primer ejemplo indicamos el lenguaje con el que trabajamos y su versión. Además, le indicamos que tiene que instalar las herramientas de firebase y que si todo sale bien lo despliegue con el token que le indicaremos a continuación. De momento no hacemos nada para decidir si debemos desplegar o no. language: node_js node_js: - "6.11.5" sript: - echo "Deploying!" install: - npm install -g firebase-tools after_success: - firebase deploy --token $FIREBASE_TOKEN Obtener token de firebase hosting Para obtener este token ejecutamos firebase login:ci Con este token vamos a travis, lo vinculamos con nuestra cuenta de github, entramos en el proyecto que queremos hacer despliegue automatico y lo añadimos a los respositorios de travis. Ahora dentro de este proyecto en el apartado de opciones >> ajustes y en la parte de abajo tenemos un apartado de variables. En él añadimos una variable con el nombre que puesto en el apartado after_success anteriormente, en nuestro caso FIREBASE_TOKEN como nombre y como valor ponemos el token que hemos obtenido al ejecutar firebase login:ci Ahora si nos dirigimos a nuestro proyecto en una ruta de este estilo travis-ci.org/USUARIO/NOMBRE-PROYECTO veremos como se ejecutan las instrucciones que hemos indicado en el fichero .travis.yml. Configurar .travis.yml Ahora mismo este fichero no tiene gran cosa, solo instala las herramientas de firebase y muestra un mensaje por consola. Tendremos que sustituir el mensaje por consola por las instrucciones que nos hagan falta, en mi caso ha quedado de la siguiente forma. language: node_js node_js: - "6.11.5" sript: - yarn install install: - yarn add pug - yarn global add firebase-tools - yarn add -D jest - yarn add -D babel-plugin-transform-es2015-modules-commonjs - yarn install after_success: - firebase deploy --token $FIREBASE_TOKEN En la parte de instalación le he añadido todas las dependencias de mi proyecto, pug, firebase-tools, jest, babel-plugin-transform-es2015-modules-commonjs y después de todo esto le he indicado que tiene que instalarlas. Llegados a este punto es importante que tengas un script test dentro de tu package.json. Porque este es el que se ejecutará al final de todo el proceso de instalación y, dependiendo del resultado el sistema lo considerará success o no. En mi caso el script que tengo es de la siguiente forma. "scripts": { "test": "yarn jest", [...] Al tener todo esto configurado cuando hagamos un nuevo push a nuestro respositorio travis lo detectará y ejecutará nuestro script, si todo sale bien desplegará nuestra nueva versión. Además nos mantendrá informado de el estado de la aplicación. Referencias Articulo original Toda la información referente a firebase aquí Documentación oficial de Travis aquí Este proyecto se encuentra en Github

Node.js apps en Firebase

Node.js apps en Firebase Si queremos podemos montar nuestra aplicación Node.js en firebase hosting usando firebase functions de manera gratuita y sin muchos problemas. A continuación muestro como inicial el proyecto. Instalación y configuración Primer paso instalar firebase-tools en caso de no tenerlo instalado. Basta con ejecutar: npm install -g firebase-tools A continuación dentro de nuestro directorio donde usaremos para la aplicación iniciamos un hosting con firebase: firebase init hosting Durante la configuración nos hará varias preguntas, donde se alojará la parte pública de la aplicación y si esta será SPA o no, decidiremos en nuestro caso concreto y continuamos. Seguimos iniciando el módulo de funciones. firebase init functions De igual manera que en el caso anterior preguntará que queremos usar, lo cofiguramos a nuestro gusto sin mayor problema y seguimos. En nuestro caso como usaremos express vamos dentro del directorio functions y le indicamos esta dependencia npm i express --save Primera función Ahora abrimos nuestro proyecto con nuestro editor, vamos al fichero /functions/index.js y descomentamos las líneas que vienen por defecto quedando tal que así. const functions = require('firebase-functions'); // Create and Deploy Your First Cloud Functions // https://firebase.google.com/docs/functions/write-firebase-functions exports.helloWorld = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!"); }); Desde este fichero podemos crear nuestra primera función, en este caso haremos una ruta en la que nos mostrará el el resultado de la función Date.now(). Para ello tendremos que importar express y crear nuestro objeto app. Además cambiaremos el objeto que exporta nuestra aplicación sustituyendo exports.helloWorld por exports.app. const functions = require('firebase-functions'); const express = require('express'); const app = express(); app.get('/timestamp', (request, response) => { response.send(`${Date.now()}`); }); exports.app = functions.https.onRequest(app); Tras esto estamos casi listos para desplegar nuestra primera aplicación con firebase, ahora tenemos que ir a la raíz del proyecto y abrir firebase.json y en él añadir la ruta dentro del hosting. He eliminado el resto de información que tiene el objeto para evitar confusiones. { "hosting": { //some other things "rewrites": [{ "source": "/timestamp", "function": "app" }] } } Si queremos añadir más rutas en nuestro objeto app en firebase.json tenemos que cambiar "source": "/timestamp" por "source": "**" de esta forma todas las rutas que configuremos se redigirán a app. Ya tenemos todo listo para probar nuestra primera ruta en la aplicación. Esto lo haremos de manera local, para ello ejecutamos el siguiente comando y todo debería funcionar. firebase serve --only functions,hosting Si todo sale bien tendríamos que ver en nuestra terminal algo parecido a esto user@user:~/$ firebase serve --only functions,hosting === Serving from '/home/user/Documents/project-directory'... i functions: Preparing to emulate functions. i hosting: Serving hosting files from: public ✔ hosting: Local server: http://localhost:5000 ✔ functions: app: http://localhost:5001/project-name/us-central1/app Y ya podremos usar sin problema si vamos a http://localhost:5000/timestamp veremos el resultado de nuestra función. Si queremos desplegar nuestra aplicación en el hosting por defecto que nos presta firebase lo podremos hacer ejecutando el siguiente comando. Esto funcionará si hemos vinvulado en el los primeros pasos nuestro proyecto con firebase. firebase deploy Una vez termine te indicará la url por defecto que se te ha asignado. El resultado debe ser similar a este. user@user:~/$ firebase deploy === Deploying to 'project-name'... i deploying functions, hosting i functions: ensuring necessary APIs are enabled... ✔ functions: all necessary APIs are enabled i functions: preparing functions directory for uploading... i functions: packaged functions (936 B) for uploading ✔ functions: functions folder uploaded successfully i hosting: preparing public directory for upload... ✔ hosting: 2 files uploaded successfully i functions: updating function app... ✔ functions[app]: Successful update operation. ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/project-name/overview Hosting URL: https://project-name.firebaseapp.com Problemática En mi caso he tenido varios problemas. No me reconoce la ruta / Si queremos que nuestra aplicación funcione la ruta app.get('/', (request, response) => {}); y haga lo que le hemos indicado en nuestra aplicación hay que tener cuidado con lo que genera firebase por defecto. Si nos fijamos en nuestro directorio public tenemos un fichero index.html si no lo borramos el hosting por defecto mostrará este fichero HTML ignorando nuestra configuración de la aplicación. Permisos El primero de ellos con los permisos de los directorios donde estaba instalado firebase globalmente. En los log del sistema me decía que no podía acceder/crear un nuevo directorio en la ruta. Lo que hice para solucionar ha sido cambiar los permisos de esta ruta para que mi usuario pueda acceder a ella de la siguiente forma. sudo chown -R $(whoami) /usr/local/lib/node_modules/ Versión de Node El segundo de ellos ha sido la versión de node instalada en el sistema. Yo tenía la 6.11.4 y firebase era compatible a partir de la 6.11.5 según decía el mensaje de error. Para ello instalé el nvm, ya que tengo otro poryecto que usa esta versión anterior y no quiero que le pase nada. En el propio repositorio explica paso a paso como instalarlo. Con él instalado hice nvm install node con lo que me descargué la última versión 10.4.0 en este momento. Volví a tratar de ejecutar la aplicación en local y me decía que la versión seguía siendo la antigua. Esto es porque había cambiado de versión en otro terminal. Para que los cambios surtieran efecto tendria que volver a cambiar la version de node en el terminal donde estaba trabajando con firebase o pasarme a usar este que ahora tengo con la version buena. En cualquier caso cada vez que iniciemos un nuevo terminal este tendrá por defecto la última versión de node siempre. Pero todo no acaba aquí, resulta que la versión de firebase en la que estoy solo acepta node 6.11.5 justo la versión siguiente en la que me encontraba. Así que una vez más node install 6.11.5 y con todo esto por fin pude ver en local mi proyecto. Para cambiar a la versión en la que me encontraba usé node use 6.11.4 y en caso de querer usar la última existe un alias que puedes usar node use default con el que pasas a la 10.4.0 la más actual que dispongo. Referencias Video original de google en inglés. Firebase Hosting, documentación oficial. Node, documentación oficial. SPA Single Page Application o Aplicación de Página Única. Repositorio donde enconrar las instrucciones de instalacion de nvm Java Script es el lenguaje que uso en los ficheros que muestro.

Curiosidades de ruby

Manejo de array Para acceder a los arrays podemos hacerlo de varias formas, una de ellas es mediante los corchetes. Estos son muy flexibles, podemos hacer muchas cosas con ellos. Podemos acceder con - [0..2] elementos desde 0 hasta 2 [0], [1], [2] - [0, 2] 2 elementos desde el 0 [0], [1] - [-1] el último elemento - [0..2] = 'A' elementos desde el 0 hasta el 2 sustituye por ‘A’ deja un solo elemento, une los tres seleccionados en uno solo a = Array.new a[4] = "4"; #=> [nil, nil, nil, nil, "4"] a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] a[0, 2] = "?" #=> ["?", 2, nil, "4"] a[0..2] = "A" #=> ["A", "4"] a[-1] = "Z" #=> ["A", "Z"] a[1..-1] = nil #=> ["A", nil] a[1..-1] = [] #=> ["A"] a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"] a[3, 0] = "B" #=> [1, 2, "A", "B"] Diferencia entre dup & clone Articulo completo en coderwall. Ambos métodos crean una copia superficial (shalow copy), lo que quiere decir que hace una copia directa de los campos que el objeto tenga dentro. Por lo tanto si tiene primitivos los copia y si tiene objetos copia la referencia a la zona de memoria, con lo que si modificamos un campo de la copia el cual es un objeto modificara el valor del original. items = [ Item.new('irrelevant'), Item.new('irrelevant'), Item.new('irrelevant') ] cart = ShoppingCart.new(items) clone = cart.clone puts cart #<ShoppingCart:0x000055d2f0d41bb0> puts clone #<ShoppingCart:0x000055d2f0a421d0> puts cart.getList.size #3 clone.delete 1 puts clone.getList.size #2 puts cart.getList.size #2 La diferencia entre ambos es que clone hace dos cosas que no hace dup estas son: Copia la clase singleton del objeto copiado Mantiene el estado congelado del objeto copiado a = Object.new # creamos la instacia del objeto def a.foo; :foo end # le incluimos un método nuevo p a.foo # => :foo # vemos que el método existe b = a.dup # hacemos la copia p b.foo # => undefined method `foo' for #<Object:0x007f8bc395ff00> (NoMethodError) # el método nuevo no existe en la copia con dup b = a.clone p b.foo # => :foo # el método nuevo si existe en la copia con clone Estado congelado: a = Object.new a.freeze p a.frozen? # => true b = a.dup p b.frozen? # => false c = a.clone p c.frozen? # => true Como podemos ver el estado se mantiene si usamos el clone. for VS each Artículo completo en freecodecamp-leandrotk por LeandroTK en el apartado de Looping/Iterator. En el ejemplo siguiente veremos porque nos recomiendan que usemos each en lugar de for, esto es debido a que el bucle foor una vez termina su ejecución mantiene la variable que usa para iterar. En cambio esto no ocurre si usamos un iterador each. # for looping for num in 1...5 puts num end puts num # => 5 # each iterator [1, 2, 3, 4, 5].each do |num| puts num end puts num # => undefined local variable or method `n' for main:Object (NameError) Objetos En ruby no tenemos porque declarar los campos de un objeto, basta con incluirlos con un @. Estos campos por defecto no son accesibles desde fuera, para decirdir el nivel de acceso que le queremos dar existen tres etiquetas: attr_accesor, permites lectura y escritura en el campo attr_reader, permites lectura en el campo attr_writter, permites escritura en el campo Para usarlo sería de la siguiente forma. class Item attr_reader :price attr_accessor :amount attr_writer :name def initialize(name, amount, price, foo) @name = name @amount = amount @price = price @foo = foo end end De esta forma price solo se puede leer, amount permite lectura y escritura, name solo escritura y al campo foo no se puede acceder. item = Item.new('raton', 15, 40, 'foo') puts item.price # 40 puts item.amount # 15 puts item.name # Error puts item.foo # Error item.amount = 10 puts item.amount # 10 item.name = 'teclado' # se modifica el valor a teclado Múltiples campos en un método Articulo completo en codementor punto 3 En caso de tener un método con una cantidad de campos indefinidos existe un operador * def mood(name, *feelings) feelings.each do |feeling| puts "#{name} is feeling #{feeling} today." end end feeling("Suzie", "happy", "excited", "nervous") # Suzie is feeling happy today. # Suzie is feeling excited today. # Suzie is feeling nervous today. Usar doble exclamación (!!) para comprobar si existe un valor Articulo completo en codementor punto 5 Puede sonar raro, usar una doble negación? No se supone que se anulan al usarla. Esto es un truco que se usa en ruby para evitar que nos devuelvan nil. Si devolvemos directamente un valor que esperamos que sea verdadero o falso, puede darse el caso de que este no esté inicializado. Es por ello que si devolvemos una doble negación, con la primera obtenemos verdadero en caso de que no exista y en la seguda negación falso, lo que nosotros queríamos. class Name attr_reader :middle_name def initialize(first_name, last_name, middle_name=nil) @first_name = first_name @middle_name = middle_name @last_name = last_name end def has_middle_name? !!@middle_name end end george = Name.new("George", "Washington") george.has_middle_name? #=> false puts george.middle_name # nil puts !george.middle_name # true puts !!george.middle_name # false obama = Name.new("Barack", "Obama", "Hussein") obama.has_middle_name? #=> true puts obama.middle_name #=> "Hussein" puts !obama.middle_name #=> false puts !!obama.@middle_name #=> true Legibilidad Métodos true false Articulo completo en sitepoint Por convenio en Ruby los métodos que devuelvan verdadero o faso, como puede ser is_empty se terminan con un signo de interrogación para dar más semántica al código. De tal manera que quedan is_empty?. Condiciones if !true Articulo completo en sitepoint Existe una cláusula que hace lo mismo y queda más claro (a mi parecer), además te recomiendan que la uses. La cláusula es unless if !true do_this end unless true do_this end Y nunca debe de tener un else, se le puede poner pero no tiene sentido. Si tienes un else en un unless con cambiar el sentido de la condición queda igual. # NEVER do this unless success #throw error else #return success end # This is the correct form if success #return success else #throw error end

Inyección de dependencia

Inyección de dependencia La inyección de dependencia consiste, como su nombre indica, en añadir, insertar dependencia a un objeto. Esto no es recomendable porque creas una dependencia grande entre objetos. Pero en ocasiones es la mejor solución que tenemos para asegurarnos de que el objeto B que usamos dentro del objeto A es el que queremos y como lo queremos, en lugar de uno que importemos. Una interfaz puede ser algo como IDisposable, IEnumerable o IPrintable. Una clase es una implementación real de una o más de estas interfaces: List o Map pueden ser implementaciones de IEnumerable. Para entendernos: a menudo tus clases dependen unas de otras. P.ej. puedes tener una clase database que acceda a su base de datos, pero también deseas que esta clase inicie sesión para acceder a la base de datos. Supongamos que tiene un registrador (logger) entonces la base de datos tiene una dependencia al registrador. Hasta aquí todo bien. Puede modelar esta dependencia dentro de su clase de Base de Datos con la siguiente línea: var logger = new Logger (); y todo está bien Está bien hasta el día en que se da cuenta de que necesita un grupo de registradores: a veces desea iniciar sesión en la consola, a veces en el sistema de archivos, a veces usando TCP / IP y un servidor de registro remoto, y así sucesivamente… Y, por supuesto, NO quieres cambiar todo tu código (mientras tanto, tienes miles de millones) y reemplazar todas las líneas var logger = new Logger (); por: var logger = new TcpLogger (); Primero, esto no es divertido. En segundo lugar, esto es propenso a errores. Tercero, este es un trabajo estúpido y repetitivo para un mono entrenado. Entonces, ¿Qué haces? Obviamente, es una buena idea introducir una interfaz ICanLog (o similar) que implementan todos los registradores. Así que el paso 1 en tu código es lo que haces: ICanLog logger = new Logger (); Ahora que la inferencia de tipo ya no cambia de tipo, siempre tienes una única interfaz contra la cual desarrollar. El siguiente paso es que no desee tener un nuevo Logger() una y otra vez. Así que pones la fiabilidad para crear instancias nuevas en una única clase central de fábrica y obtienes un código como: ICanLog logger = LoggerFactory.Create (); La fábrica misma decide qué tipo de registrador crear. A su código ya no le importa, y si desea cambiar el tipo de registrador que está utilizando, cámbielo una vez: Dentro de la fábrica. De esta manera con la interfaz te aseguras que todas las implementaciones de registrador, sea cual sea actúen de la misma forma ya que implementan la misma interfaz. Esta última último tramo de la publicación es una traducción de una respuesta en StackOverflow la respuesta completa aquí.

Olores del código: test

Olores del código: test Existen muchas cosas que no huelen bien en el código y a continuación vas a poder ver las referentes a los test. Esta es una publicación parte de una serie de publicaciones que haré referentes a olores del código. Test Test insuficientes, ¿cuantos test debe tener mi suite? Siento tener que decírtelo pero no hay una métrica para ello. Tienes que tener tantos test como para probar todo lo que puede fallar. Aunque he escuchado decir que normalmente con tener un 80% de la aplicación testada puede valer. Usa una herramientas de cobertura, existen herramientas que te hacen análisis sobre la cobertura de tus test (a veces los propios IDE´s lo tienen integrado). Estas te analizan que partes están cubiertas con un test y cuales no, te hacen tener una visión de como está el proyecto. No te saltes los test triviales, en ocasiones puedes pensar que este test es demasiado obvio como para comprobarlo. Pero la verdad es que este tipo de test tienen más valor como documentación que como comprobación en sí mismo. Hazlos y deja reflejado ese comportamiento. Un test ignorado es una pregunta sobre ambigüedad, a veces no tenemos claro el comportamiento de algún detalle de comportamiento. Esto lo podemos dejar reflejado como un test comentado o ignorado, de esta manera esta cuestión queda reflejada en el código y será más fácil de solucionar. Testea condiciones del entorno, a menudo nos podemos encontrar delante de una solución conrrecta pero ser el entorno quien nos genera errores. Es por ello que hay que tener en cuenta las condiciones del entorno. Testear exhaustivamente cerca de los bugs, cuando encontramos un bug en una función es muy probable que no se encuentre solo. Es por ello que una vez encuentras un bug es recomendable testar esa función a fondo, probablemente encuentres más errores. Los test deben de ser rápidos, un test lento es un test que no se va a ejecutar. Cuando estás ejecutando la suite de test constantemente el test más lento es el test que terminarás por saltar. Este hará que tu ritmo de trabajo baje y te cansarás, por eso es mejor que los test sean lo más rápido posible. Referencias Libro original sobre Clean Code, esta publicación corresponde con el capítulo 17.

Objeto Página (*pageObject*)

Objeto Página (pageObject) Cuando te encuentras haciendo test para una aplicación que no tiene módulos testables y tienes que hacerlo contra la interfaz directamente, es muy probable que te encuentres ante esta situación: // scoreBoard.spec.js // [...] it('should save score after answer a question', function () { startGame(); selectOption(0); submitAnswer(); addPlayerName(players[1]); saveScore(); expectScoreToContainsMinusOne(); }); function startGame() { let startButton = document.getElementById('start-button'); startButton.click(); } function selectOption(optionId) { let answer = document.getElementById(optionId); answer.click(); expect(answer.checked).toBeTruthy(); } // [...] Y luego en otro test lo siguiente: // game.spec.js // [...] it('should start the game, answer a question and change the score', function () { startGame(); selectOption(0); submitAnswer(); expectScoreToBeDifferentFromTheBeginning(); }); function startGame() { let startButton = document.getElementById('start-button'); startButton.click(); } function selectOption(optionId) { let answer = document.getElementById(optionId); answer.click(); expect(answer.checked).toBeTruthy(); } // [...] Estámos repitiendo los mismos métodos startGame() y selectOption() y eso es algo que no nos gusta en el código limpio. Por ello es que existe el PageObject model. Este está formado por funciones que trabajan directamente con la interfaz. De esta manera podemos usar todos las funciones manera unificada con el mismo comportamiento. Extraer al PageObject Creamos nuestro nuevo fichero donde guardar este modulo que hemos decidido extraer y ponemos las funcionalidades que queremos. // pageObject.js export default function pageObject() { function startGame() { let startButton = document.getElementById('start-button'); startButton.click(); } function selectOption(optionId) { let answer = document.getElementById(optionId); answer.click(); expect(answer.checked).toBeTruthy(); } // [...] return { startGame, selectOption }; }; Ahora solo tendremos que importarlo donde lo usábamos anteriormente // game.spec.js import gamePageObject from '../src/pageObject'; // [...] let pageObject = gamepageObject(); it('should start the game, answer a question and change the score', function () { pageObject.startGame(); pageObject.selectOption(0); submitAnswer(); expectScoreToBeDifferentFromTheBeginning(); }); function startGame() { let startButton = document.getElementById('start-button'); startButton.click(); } function selectOption(optionId) { let answer = document.getElementById(optionId); answer.click(); expect(answer.checked).toBeTruthy(); } // [...] De igual forma podemos sacar submitAnswer() si lo vemos necesario. References Martin Fowler post Publicaciones mías sobre Código limpio

Extrer objeto en JavaScript

Extrer objeto en JavaScript Vamos a ver como extraer un objeto en JavaScript en su versión de ECMAScript 5. Partimos de un fichero donde tenemos todas las funcionalidades y vemos que una de ellas es claramente un objeto que podemos extraer. // main.js function application(){ // {...} let actualQuestionIndex = 0; function isThereMoreQuestions() { return actualQuestionIndex < questions.length -1; } function getQuestion() { return questions[actualQuestionIndex]; } function goToNextQuestion() { if (isThereMoreQuestions()) { actualQuestionIndex++; } } function resetQuestions() { actualQuestionIndex = 0; } // {...} } Como podemos ver esta parte de nuestro código se encarga de la navegación en nuestras preguntas, por ello vamos a sacarlo a un nuevo fichero. Haciendo que nuestra aplicación sea más modular y acepte cualquier tipo de navegación que cumpla estos requisitos mínimos. //questionNavigator.js var saberganar = saberganar || {}; saberganar.questionNavigator = function (questions){ let actualQuestionIndex = 0; function isThereMoreQuestions() { return actualQuestionIndex < questions.length -1; } function getQuestion() { return questions[actualQuestionIndex]; } function goToNextQuestion() { if (isThereMoreQuestions()) { actualQuestionIndex++; } } function resetQuestions() { actualQuestionIndex = 0; } return { isThereMoreQuestions, getNextQuestion }; }; //main.js var saberganar = saberganar || {}; saberganar.game = function(questionNavigator){ // {...} let question = questionNavigator.getQuestion(); // {...} } De esta forma también evitamos ensuciar el namespace de nuestra aplicación dejando por separado cada módulo namespace.game y namespace.questionNavigator Referencias Video original

Olores del código: funciones y nombres

Olores del código: funciones y nombres Existen muchas cosas que no huelen bien en el código y a continuación vas a poder ver las referentes a funciones y nombres. Esta es una publicación parte de una serie de publicaciones que haré referentes a olores del código. Funciones 1.Muchos argumentos, las funciones son mejores cuantos menos argumentos tengan. Lo ideal es que no tengan ninguno y como máximo tres, a partir de tres deberías cuestionarte si los argumentos no son realmente un objeto con esas características, por ejemplo. 2.Argumentos de salida, los argumentos como salida no son intuitivos. Al usar una función se entiende que los argumentos son parámetros de entrada y la salida la devolveremos al terminar la ejecución con el return. 3.Argumentos bandera, los argumentos boolean para indicar a la función el tipo de comportamiento que tiene que hacer son confusos. Si una función puede hacer más de una cosa, sepárala en dos funciones independientes. 4.Funciones muertas, funciones que nunca son usadas no tienes porqué tenerlas. No guardes código viejo inútil, bórralo. Tu sistema de control de versiones lo recordará por ti! Nombres 1.Escoge nombres descriptivos, los buenos nombres son el 90% de la legibilidad del código. Darle un buen nombre a una función, a una variable o a una clase no es algo trivial. Esta es de las partes más difíciles, los buenos nombres requieren de pensarlos con calma. No dejes nombres como i, j o aux. Los dos primeros casos si lo usas en un índice de un bucle, se podría pero porque esta variable i no abarca más de las tres líneas del bucle. Si se tratara de un índice global, no lo hagas! 2.Escoge nombres con un nivel de abstracción adecuado, al escoger nombre ten en cuenta en el nivel en el que te encuentras y mantén siempre el mismo. Si estás, por ejemplo, con una interfaz módem con varias funciones como la siguiente. public interface Modem { boolean dial(String phoneNumber); boolean disconect(); boolean send(); String getConnectedPhoneNumber(); } De primeras puede parecer que está correcta pero realmente ¿nos interesa saber que está implementada con un número de teléfono? Dentro de un tiempo si queremos usar otro tipo de localizador ¿como lo hacemos? Es por ello que sería mejor si lo hiciéramos de la siguiente forma. public interface Modem { boolean connect(String connectionLocator); boolean disconect(); boolean send(); String getConnectedLocator(); } 3.Usa la nomenclatura standar, donde sea posible, cuando estás implementando un patrón es probable que este tenga una nomenclatura estandarizada para dar nombres a ciertas partes de la aplicación como pueden ser las funciones, métodos o las clases. Sigue el estándar todo lo posible de esta forma no generas confusiones. Por ejemplo si tienes un método que transforma tu objeto a String, en Java, se usa toString(). Otro ejemplo son las funciones que puedes ver en los condicionales if(), estas suelen empezar por is. Quedando de esta forma if(isFromSpain(phoneNumber)). 4.Nombres no ambiguos, tienes que escoger un nombre que no se quede corto y deje esconder parte de la tarea que realiza una función. Si tienes una función que se llama rename() e internamente, cambia el nombre y añade un prefijo, te estás quedando corto. Un mejor nombre sería renameAndAddPrefix(). 5.Usa nombres largos para ámbitos largos, después tanto hablar de dar nombres descriptivos puedes llegar al extremo de dar nombres largos a variables contadoras en los iteradores. Como te he dicho en el apartado anterior si el ámbito del índice es solamente dentro del iterador for no hace falta que lo llames numberCounter si tienes dos líneas de código dentro y se ve que solamente se usa como iterador. 6.Evita codificar, a día de hoy los IDEs que tenemos son potentes, nos ayudan con los nombres, nos avisan si hay duplicados, si no usamos una variable. No tenemos que comenzar por l_ para indicar que la variable es local, ni nada parecido los IDEs nos lo indican. 7.Los nombres deben ser descriptivos, sin efectos secundarios, los nombres tienen que ser sinceros. No puedes tener una función public getTimer(){ if(localTimer == null){ localTimer = new Timer(): } return localTimer; } Si nos fijamos esta función no devuelve un timer lo que hace realmente es crear uno si no existe ya y luego lo devuelve. Un posible nombre más correcto para la función podría ser createIfNotExistsAndReturnTimer. Referencias Libro original sobre Clean Code, esta publicación corresponde con el capítulo 17.

Olores del código: comentarios y entorno

Olores del código: comentarios y entorno Existen muchas cosas que no huelen bien en el código y a continuación vas a poder ver las referentes a comentarios y al entorno. Esta es una publicación parte de una serie de publicaciones que haré referentes a olores del código. Comentarios Se que lo hemos hablado en varias ocasiones pero una vez más, los comentarios no son una mejora del código. Al contrario, suelen entorpecer más de lo que ayudan por las siguientes razones: 1.Información inapropiada, no hace falta añadir meta-datos a un fichero con comentarios. Cosas como fecha de última modificación, autor, etc no son necesarios y sólo generan ruido. 2.Comentarios obsoletos, tener comentarios explicando una parte de código es algo común (no he dicho que esté bien, es común solo eso). Pero la realidad es que esos comentarios nadie se encarga de mantenerlos y quedan rápidamente desfasados con el código al que está atado. Porque si tu cambias el funcionamiento de un método porque no está correcto, no te acuerdas de modificar el comentario que lo explicaba. 3.Comentarios redundantes, ya que no me vas a hacer caso y vas a poner un comentario, al menos que no diga lo mismo que puedo ver i++; //increment i no lo hagas. 4.Comentarios pobres, si sigues pensando que hace falta poner un comentario, al menos escribe el mejor que puedas. Usa la terminología del contexto en el que estás y no generes más confusión de la que ya hay. 5.Código comentado, por favor esto nunca. No dejes código comentado suelto por tu clase, este código nadie lo borra porque nadie sabe porqué está ahí. Y la realidad es que con cada día que pasa se vuelve más irrelevante, la variables que usa dejan de existir, lo métodos a los que llama cambian la firma, etc. Si está comentado, bórralo no es necesario. Si alguien lo necesita puede volver a la versión anterior de tu sistema de control de versiones y verlo. Entorno 1.Desplegar requiere más de un paso, si tienes una aplicación esta tiene que ser fácil de desplegar un paso, dos como mucho. No debes tener que comprobar decenas de ficheros, ver que dependencias falta y complicarte la vida a ti y a quien quiera usarlo. Tendrías que ser capaz de hacer algo parecido a esto get application, cd application, deploy application, run application. 2.Test requiere más de un paso, al igual que el caso anterior esta tiene que ser fácilmente testable. Tiene que poder ser testada con un simple comando que ejecute todos los test. Referencias Libro original sobre Clean Code, esta publicación corresponde con el capítulo 17. Sistema de control de veriones

JUnit Internos

JUnit Internos JUnit es de los frameworks open source más famosos de testing en Java. Es simple en su concepto, preciso y elegante de implementar. Además al tener tanta fama y tantos seguidores podemos encontrar documentación de sobra. Ejemplo básico Supongamos que tenemos una calculadora con la siguiente implementación. No es importante la implementación en si, pero si quieres reproducir un test te lo dejo, por aquí ;) public class Calculator { public int evaluate(String expression) { int sum = 0; for (String summand: expression.split("\\+")) sum += Integer.valueOf(summand); return sum; } } Y como es normal queremos hacer un test de nuestra calculadora porque como ya sabemos tener una buena batería de test es importante. Entonces implementamos el siguiente. import static org.junit.Assert.assertEquals; import org.junit.Test; public class CalculatorTest { @Test public void evaluatesExpression() { Calculator calculator = new Calculator(); int sum = calculator.evaluate("1+2+3"); assertEquals(6, sum); } } Como vemos es bastante fácil y limpio, es por lo que es tan famoso. Y como este podemos hacer todos los test que queramos, buscando hacer que falle la calculadora. Porque si conseguimos que falle es buena señal, ya hemos descubierto un hueco que tapar, una funcionalidad rota o que no existe. Y claro, mejor encontrarla nosotros que un usuario frustrado. Pasar los test no significa que no falle Y es aquí donde quería llegar, como he dicho puedes hacer todos los test que se te ocurran. Y ahí está el punto, los que se te ocurran. En este caso sólo tenemos uno y parece claro que esta calculadora no está completa ¿verdad? Claro, eso lo vemos nosotros que conocemos más operaciones aritméticas que no sea la suma. Pero, quien ha implementado esta calculadora no conocía más. Para él parece que está completa y testeada contra todos los problemas y casos existentes. No se si ves lo que trato de decirte. Tener una gran batería de test no significa que tu aplicación esté correcta o perfecta porque pasa todos los test. Eso solo significa que aún no has encontrado/escrito los suficientes test para encontrar un fallo. Y antes o después alguien se le ocurrirá otro caso que haga que la aplicación explote. Es por eso que los test si son importantes, es un gran apoyo contra los cambio que puedas hacer al código. Pero no significa que vaya a funcionar en todos los casos que se den. Haces test para probar todo, sabiendo que todo no puede ser probado. Esto es lo que le pasa al autor en este capítulo. Nos muestra un código de primeras bastante correcto y limpio, con una batería de test bastante completa. Y va haciendo varios cambios en el código, nombres de variables, encapsula algunos métodos, etc y finalmente descubre un fallo. Este fallo no estaba visible por como se redactaban las condiciones. Y tras un poco más de cambios en el código, descubre como dos condiciones más sobran. En un primer momento, miras y ves fSuffix > 0 puede parecer que tiene sentido. ... if (fSuffix > 0) result = result + computeCommonSuffix(); return result; ... Pero después de varias vueltas al código con paciencia, llegamos a esto otro que al verlo choca. ... if (suffixLength >= 0) result = result + computeCommonSuffix(); return result; ... Ya que al tratarse de una aplicación que comprueba si dos strings tienen algún carácter distinto. Si nos ponemos a ver el código, llegamos a la conclusión de como esa comparación no tiene sentido. Puesto que si ha llegado a este punto es porque existe un sufijo, es decir es imposible que sea 0 y nunca entrará al else. Una vez sabemos esto podemos borrar ambas comparaciones dejando más claro aún el código. Y todo esto porque el autor quiso darle una vuelta a un aplicación que parecía, de primeras testeada a prueba de misiles nucleares. Pero a pesar de ello se podía mejorar aún más. Siempre se puede mejorar Referencias Libro original sobre Clean Code. Página oficial de JUnit. Wiki primeros pasos con JUnit.

Refinamiento Sucesivo

Refinamiento Sucesivo Si te mostrara ahora un trozo de código y te dijese: “Mira que limpio está” podrías pensar que esto ha salido a la primera de mi cabeza porque soy muy bueno pero no es así. Cuando escribes un código la mayoría de veces te pasará que empiezas a hacer cosas “porque funcionan” sin pensar mucho en las buenas prácticas sobretodo cuando estás empezando con ellas. Pero no por eso debes dejarlo pasar. El código que escribas debes considerarlo como un borrador que siempre tiene mejoras pendientes, cada vez que hagas una funcionalidad antes de considerarla hecha dale una vuelta, revísala como si fuera de tu amigo o compañero y trata de mejorarla (siempre se puede). Es por ello que en este capítulo del libro muestra una clase que implementa y como al verla en su “forma final” gusta pero cuando te enseña las primeras versiones ves que realmente no era tan bonita en sus inicio que se ha llevado varios refinamientos para dejarla en el punto en el que se encuentra ahora. En Incrementalismo La mejor forma de romper un programa que funciona es hacer cambios de nombres “para mejorar” la legibilidad en masa esto no suele llegar a buen puerto nunca… Suele generar problemas, conflictos con otros nombres, y un largo etcétera. Para poder hacer estos cambios tranquilos hay una solución TDD, esto junto con hacer muchos cambios pequeños que vamos comprobando. En este caso las comprobaciones las podremos hacer de manera automáticas con los test que tendremos hechos para nuestro programa. De manera que después de cada cambio ejecutamos los test y si estos fallan volveremos al estado anterior donde todo funcionaba, no haremos cambios que rompan el programa. O en caso de que lo que se rompa sea poca cosa y veamos que se puede solucionar porque no era así como debería funcionar, podemos solucionarlo pero como estábamos hablando de cambios de nombres a variables y métodos normalmente si estos generan un problema no suele ser tan trivial. Conclusión Es por todo ello que el código que hacemos no se considera terminado cuando funciona, porque hacer que funcione algo puede hacerlo cualquiera pero los buenos programadores además lo hacen de manera limpia y lo dejan con pruebas para evitar que los cambios lo rompan. Referencias Libro original TDD, Test Driven Development o Desarrollo Dirigido por Test. Notas Durante todo el capítulo el autor hace cambios en su primera versión del código y va explicando cada uno de ellos la línea que sigue de pensamiento. Subiré estos cambios explicados de manera calmada en el futuro.

Concurrencia

Concurrencia Concurrencia… Aaaah mi amiga la concurrencia, como te quiero y te odio a la vez. Tú junto con la recursividad has sido, de momento, los dos conceptos que más me ha costado entender en la informática. Es de esas cosas que o entiendes a la primera o tardas tiempo hasta que un día, hace “click” en tú cabeza y todo cobra sentido, vamos a por ti ¿eres realmente tan necesaria? ¿Que es la concurrencia? La concurrencia, resumiendo mucho, consiste en poder hacer mas de una tarea en el mismo instante de tiempo. Llevado al mundo de la programación dos funciones/métodos/ejecuciones están ejecutándose al mismo tiempo. Problemas que surgen Imaginemos que tenemos un simple programa que incrementa un contador, turno para ser atendidos, cada vez que un usuario pulsa un botón, fácil ¿no? Una clase con un campo contador y un método que devuelve el contador y le suma una unidad. Pues si este trabajo lo hacemos concurrente pongamos que con tres usuarios que llegan en el mismo instante de tiempo y lo solicitan a la vez pueden ocurrir muchas cosas si no controlamos este contador como debemos. Puede ser que el primer usuario tenga el número 15, luego el segundo el 16 y quede un 17 para el siguiente, suerte todo ha salido bien. O puede que al ser la petición al mismo tiempo ambos tengan el 15 y quede el 16 para el siguiente. O aún peor puede que el primero tenga el 15 el segundo el 16 pero quede un 16 para el siguiente que venga. En la concurrencia todo es posible. Los hilos deben ser independientes Cuando trabajamos con concurrencia y creamos nuestros hilos para dar solución al problema estos deben ser lo más independientes posibles, porque no te puedes basar en el estado de otro hilo en ningún momento ya que este va a su ritmo al igual que tú, puede que estés esperando algo que aún no ha ocurrido. Ejemplos Productor-Consumidor Uno de los ejemplos típicos en la concurrencia es el del la pila de productos en la que una persona se encarga de añadir objetos a ella y otro de consumir de la misma. El que produce tiene que esperar a tener espacio para añadir el nuevo objeto y el que consume tiene que esperar a que exista algún objeto en la pila para poder consumir. Para lograr estar coordinados el consumidor y el productor mandan señales de aviso el uno al otro, quien produce cada vez que añade un producto avisa que la pila no está vacía y el consumidor cada vez que saca un objeto avisa de que la pila no está llena. Así ambos saben que pueden actuar. Lectores-Escritores Otro ejemplo conocido es el de lectores y escritores. Si tenemos un documento el cual puede ser leído y escrito por múltiples personas no se puede solucionar de igual forma que el anterior, pues si el escritor no puede actuar sobre el documento hasta que el/los lector/es terminen con él puedes tener múltiples lectores, que se acumulen y que nunca llegue a estar libre el documento. Hay que buscar una forma de hacerlo de manera más equilibrada. Conclusión La concurrencia de código no es trivial, cuesta de comprender y mucho más de manejar con correctitud. Si estás pensando en empezar con ella, tómatelo con calma y no esperes que todo cobre sentido en un día. Referencias Libro original

Sistemas

Sistemas En este capítulo el autor empieza con un símil que me gusta mucho y voy a tratar de traducir lo más fielmente que sé. ¿Cómo construirías una ciudad? ¿Podrías administrar todos los detalles por ti mismo? Probablemente no. Incluso administrar una ciudad es mucho para una sola persona. Sin embargo, las ciudades funcionan (la mayor parte del tiempo) Funcionan porque tienen equipos de personas que administran partes de la ciudad, el sistema de agua, etc […] Las ciudades funcionan porque tienen los niveles apropiados de abstracción y modularidad haciendo posible para las personas y “componentes” se las arreglen para trabajar efectivamente, sin entenderla a gran escala. Aunque los equipos de desarrollo a menudo se organizan así también, los sistemas en los que trabajan a menudo no tienen la misma separación de preocupaciones y niveles de abstracción. […] En este capítulo veremos como mantenerse limpio a niveles altos de abstracción, a nivel de sistema. Y me gusta porque realmente veo así una aplicación o producto software como un ente enorme que tienes que separar en pequeñas “responsabilidades” para poder gestionarlo. Separar la construcción de un sistema de usarlo Una de las formas de separar la construcción del uso es mover todos los aspectos de construcción (creación de objetos) a nuestro main. De esta maneja queda un flujo limpio y fácil de comprender, creamos todos los objetos que nos hacen falta en el main y luego los enviamos a la parte de nuestra aplicación donde nos sea necesario. Es decir a nuestro main no le entra información de la aplicación solo envía hacia afuera. Fábricas Esto no siempre es posible, por ejemplo si tenemos una aplicación de pedidos, esta tendrá que modificar la lista de pedidos añadiendo un nuevo objeto alguna que otra vez. Para ello podemos usar el patrón Abstract Factory Como podemos ver nuestro cliente (main) tiene una interfaz genérica para nuestros productos (productoA) y una fábrica (AbstractFactory), de esta manera el conoce que puede crear productos mediante el uso de la fábrica pero desconoce como esta los crea ya que es una interfaz y carece de detalles de implementación. A él lo único que le interesa e importa es que puede crearlos y usarlos. Inyección de dependencia La inyección de dependencia consiste, como su nombre indica, en añadir, insertar dependencia a un objeto. Esto no es recomendable porque creas una dependencia grande entre objetos pero en ocasiones es la mejor solución que tenemos para asegurarnos de que el objeto B que usamos dentro del objeto A es el que queremos y como lo queremos. Una interfaz puede ser algo como IDisposable, IEnumerable o IPrintable. Una clase es una implementación real de una o más de estas interfaces: List o Map pueden ser implementaciones de IEnumerable. Para entendernos: a menudo tus clases dependen unas de otras. P.ej. podría tener una clase database que acceda a su base de datos, pero también desea que esta clase inicie sesión para acceder a la base de datos. Supongamos que tiene un registrador (logger) entonces la base de datos tiene una dependencia al registrador. Hasta aquí todo bien. Puede modelar esta dependencia dentro de su clase de Base de Datos con la siguiente línea: var logger = new Logger (); y todo está bien Está bien hasta el día en que se da cuenta de que necesita un grupo de registradores: a veces desea iniciar sesión en la consola, a veces en el sistema de archivos, a veces usando TCP / IP y un servidor de registro remoto, y así sucesivamente … Y, por supuesto, NO quieres cambiar todo tu código (mientras tanto, tienes miles de millones) y reemplazar todas las líneas var logger = new Logger (); por: var logger = new TcpLogger (); Primero, esto no es divertido. En segundo lugar, esto es propenso a errores. Tercero, este es un trabajo estúpido y repetitivo para un mono entrenado. Entonces, ¿Qué haces? Obviamente, es una buena idea introducir una interfaz ICanLog (o similar) que implementan todos los registradores. Así que el paso 1 en tu código es lo que haces: ICanLog logger = new Logger (); Ahora que la inferencia de tipo ya no cambia de tipo, siempre tienes una única interfaz contra la cual desarrollar. El siguiente paso es que no desee tener un nuevo Logger() una y otra vez. Así que pones la fiabilidad para crear instancias nuevas en una única clase central de fábrica y obtienes un código como: ICanLog logger = LoggerFactory.Create (); La fábrica misma decide qué tipo de registrador crear. A su código ya no le importa, y si desea cambiar el tipo de registrador que está utilizando, cámbielo una vez: Dentro de la fábrica. De esta manera con la interfaz te aseguras que todas las implementaciones de registrador, sea cual sea actúen de la misma forma ya que implementan la misma interfaz. Esta última último tramo de la publicación es una traducción de una respuesta en StackOverflow la respuesta completa aquí. Referencias Libro original Abstract Factory Esquema abstract factory (imagen) Inyección de dependencia StackOverflow Respuesta StakOverflow

Resumen 2018, primer trimestre

Resumen 2018, primer trimestre Empecé este año con unos objetivos claros, pero sin saber como lograr alguno de ellos. Quería hacer ejercicio al menos la mitad de los días de la semana, ya que me paso muchas horas en frente del ordenador y no es muy sano que digamos… Quería tomarme más en serio las lecturas que empiezo y terminar al menos dos libros este año (uno tendría que ser técnico), quería lanzar una aplicación por mi cuenta o en equipo, algo que pueda decir “fui parte de ello”, quería tener un hábito de escritura que me haga aprender a escribir mejor, quería (y casi era una obligación para lograr todos estos) aprender a centrarme en los objetivos que me marque cada día y, el que tenía más claro, acabar el grado en ingeniería informática. Hasta la fecha puedo decir que voy por buen camino en lo referente a acabar el grado, he logrado aprender a usar Ionic para hacer el trabajo fin de grado, otro día explicaré como nace la idea de este trabajo, sobre que trata y porqué me gusta tanto. Como bien decía creo que voy por buen camino con esta parte, ha sido un comienzo con bastantes imprevistos: estar perdido con la tecnología, no comprenderla, no tener a quien consultar y no aclararme con la documentación, además como era mi primera experiencia en trabajar desde casa (día si día también) las primeras semanas me distraía con bastante facilidad y me encontraba difuso la mayor parte del tiempo. Empezaba algo pero recordaba que tenía otra cosa a media y me pasaba a terminar esta otra, pero recordaba otra nueva y volvía a saltar. Haciendo que la lista de tareas pendientes se acumulara y las hechas fueran unas pocas a la semana. Pero con cabeza, paciencia, perseverancia y probando todas las técnicas existentes para ello he llegado a un punto en el que soy capaz de dedicar las mañanas y tardes a ello, con bastante seriedad. Esto me lleva al segundo objetivo que tenía este año, centrarme en las tareas del día a día. Respecto a este he mejorado, es algo obvio avanzo muchísimo más que cuando empecé por Enero pero no lo suficiente me queda mucho por mejorar. La parte buena que me quedan aún 8 meses largos para seguir avanzando con ello y espero que con la práctica cada vez vaya a mejor. Los siguientes dos objetivos van de la mano ya que gracias a mis prácticas externas en LeanMind con Carlos Blé se hicieron una y son, las de lograr un hábito de lectura y escritura. Sabía que quería empezar un blog con el que obligarme a hacer una publicación semanal sin tener claro sobre que. Empecé con una guía/tutorial de como empezar a usar Symfony ya que en ese momento estaba empezando a aprender como usarlo un poco por gusto y el otro poco porque había hablado con mi primo para hacer un proyectito en el que tener todas las fiestas te nuestra isla unificadas. Y digo que se unió porque fue Carlos quien me dijo cuando empecé las prácticas que le parecía una buena idea leer a la semana al menos un capítulo de un libro técnico, discutirlo con el resto de compañeros de prácticas y subir un post al blog a modo de resumen. Y tanto me gustó la idea que llevo ya 19 post en lo que va de año y subiendo!! Hasta tengo un lector habitual con el libro. Por cierto el libro es Clean Code y otro que me estoy leyendo paralelo sobre psicología/auto-ayuda es Como ganar amigos e influir sobre las personas, este más bien en los ratos libres para descansar. Cuando lo termine me gustaría subir un post opinando sobre él al blog. Y cuando digo ratos libes me refiero sobretodo a los 15 o 30 minutos que hago cardio sobre una cinta estática, porque sí también estoy logrando ser constante (con mis altos y bajos) en cuanto al ejercicio se refiere. Semanalmente hago al menos 4 días de “entrenos”, mejor dicho sesiones de ejercicio. No sigo un plan estricto y bien pensado lo acomodo un poco al tiempo que tenga ese día pero he logrado seguir con él. Días que tengo tiempo hago ejercicios de fuerza y un poco de cardio, otros días voy con más prisa hago solo una sesión de cardio más larga, depende. Y para ir terminando que ya he hablado bastante por hoy decir que estoy contento, no conforme, pero contento. No pensaba lograr ni la mitad y mira por donde casi un pleno antes de llegar a mitad de año, fallo en lanzar un aplicación pero tranquilo tengo muuchas ideas y ganas para sacar alguna adelante, aún queda tiempo para ello. Digo que no estoy conforme porque si… siendo realistas voy mejor que cuando empecé, y me están saliendo bien pero… se pueden mejorar mucho más aún y soy capaz de hacerlo mejor, esto no está más que empezando.

Clases

Clases Hasta ahora hemos visto como escribir de la mejor manera posible lineas y bloques de código, pero aun no hemos visto como hacerlo con las clases. Ordenar los métodos A la hora de organizar las clases es importante que si tenemos un método que hace uso de otro que tenemos en nuestra clase, es mejor si lo ponemos a continuación, de manera que si estamos leyendo el código no tengamos que dar saltos en él y sea lo más parecido a un artículo. Modificadores Nuestras variables, métodos o funcionalidades deben tener el modificador más restrictivo posible, si tenemos que acceder a un método en un test y no podemos por ser privado lo pondremos como protected, de manera que será accesible desde el mismo paquete. Principio de responsabilidad única Un punto importante a la hora de desarrollar un clase es el tamaño porque, el tamaño si importa, al igual que con lo métodos las clases tienen que ser lo más pequeñas posibles y en lo que a dimensiones se refiere no hay un máximo de líneas. En su lugar lo que hay que tener en cuenta es el número de responsabilidades, una clase no debe tener más de una responsabilidad si se te pasa por la cabeza añadirle otra (consejo, probablemente esto sea una nueva clase). Y esto el principio más importante en la Orientación a Objetos, el principio de responsabilidad única (SRP) y en resumidas cuentas dice que una clase debe tener un solo cometido, de manera que si cambia sea por una única razón. Evitando entremezclar términos de lógica de negocio, con persistencia, con interfaz de usuario. Cohesión Es importante que las clases tengan el menor número posible de instancias de variables y además, para lograr mayor cohesión estas variables tienen que ser accedidas por el mayor número de métodos de la clase. Se dice que una clase es cohesiva cuando cada método de esta accede a cada variable instanciada. En general es complicado alcanzar estos niveles de cohesión. Por ejemplo la siguiente clase tiene un nivel de cohesión bastante elevado. public class Stack { private int topOfStack = 0; List<Integer> elements = new LinkedList<Integer>(); public int size() { return topOfStack; } public void push(int element) { topOfStack++; elements.add(element); } public int pop() throws PoppedWhenEmpty { if (topOfStack == 0) throw new PoppedWhenEmpty(); int element = elements.get(--topOfStack); elements.remove(topOfStack); return element; } } Como se ve tienen tan solo dos variables instanciadas y sólo en un método no se accede a ambas (size). Libro original

Test Unitarios

Test Unitarios La programación a evolucionado mucho en tan solo 20 años, antes por 1997 cuando estaba empezando no había forma de probar las funciones que creábamos. No era raro crear la función añadirle una parte en la que podamos introducir por teclado caracteres o alguna orden y ver como responde este, pero a día de hoy existen los test unitarios. Test que ejecutarán nuestra función y comprobarán si el resultado obtenido es el que nosotros esperábamos. Tan buenos son estos test y tanto ruido han hecho las metodologías ágiles y el TDD que no es raro a día de hoy que en una entrevista de trabajo te pregunten explícitamente por ellos. Las tres leyes del TDD No escribirás código de producción hasta que exista un test que falle. No escribirás más de un test unitario que falle. No escribirás más código de producción del necesario para pasar el test. Estas reglas se convierten en un ciclo de trabajo que no dura más de 30 segundos. Escribes un test, compruebas que falle, escribes el código para que este no falle. De esta manera escribimos los test y el código que lo pase al mismo tiempo. Si tomamos este hábito será fácil escribir docenas de test diariamente lo que hará que cada mes tu software tenga cientos de test nuevos que lo prueban ¿quien no quiere eso? Mantener los test limpios Puede que pienses que los test son una simple herramienta para llegar a un código de producción sólido y robusto, que realmente el fin es este código de producción. Y por ello no le prestes mucha importancia al como realizar los test, los creas con “prisa” dejas variables o los propios test con malos nombres. El problema vendrá en el futuro cuando creando un nuevo test para una nueva funcionalidad tienes que cambiar algo del código y bum explotan unos cuantos test a parte del nuevo. Comienzas a leer y ves que no tienes idea de que están probando realmente, en ese momento comenzará un viaje el cual (con suerte) si has escrito el test hace poco y tienes buena memoria acaba rápido pero en la mayoría de casos no será así. Con todo esto quiero decir que el código de los test es tan importante como el de producción. Un test limpio ¿Como mantenemos un test limpio? Legibilidad, al igual que hemos hablado de como hacer un código limpio los test funcionan de un modo similar: claridad, simplicidad y densidad. Imagina que te enfrentas a este test: public void testGetPageHieratchyAsXmlDoesntContainSymbolicLinks() throws Exception{ WikiPage pageOne = crawler.addPage(root, PathParser.parse("PageOne")); crawler.addPage(root, PathParser.parse("PageOne.ChildOne")); crawler.addPage(root, PathParser.parse("PageTwo")); PageData data = pageOne.getData(); WikiPageProperties properties = data.getProperties(); WikiPageProperty symLinks = properties.set(SymbolicPage.PROPERTY_NAME); symLinks.set("SymPage", "PageTwo"); pageOne.commit(data); request.setResource("root"); request.addInput("type", "pages"); Responder responder = new SerializedPageResponder(); SimpleResponse response = (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request); String xml = response.getContent(); assertEquals("text/xml", response.getContentType()); assertSubString("<name>PageOne</name>", xml); assertSubString("<name>PageTwo</name>", xml); assertSubString("<name>ChildOne</name>", xml); assertNotSubString("SymPage", xml); } Puede que logres comprenderlo después de un tiempo de análisis, no digo que sea imposible. Incluso puede que no te lleve mucho tiempo, pero enfrentarse a test así diariamente acaba quemando antes nuestra cabeza. Si en lugar de eso tenemos ese mismo test pero de la siguiente forma: public void testSymbolicLinksAreNotInXmlPageHierarchy() throws Exception { WikiPage page = makePage("PageOne"); makePages("PageOne.ChildOne", "PageTwo"); addLinkTo(page, "PageTwo", "SymPage"); submitRequest("root", "type:pages"); assertResponseIsXML(); assertResponseContains( "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>" ); assertResponseDoesNotContain("SymPage"); } Sigue teniendo las mismas tres partes diferenciadas. Una primera parte donde creas los datos para el test, a continuación donde utilizamos estos datos y finalmente comprobamos si el resultado es el esperado. Un solo assert por test Una buena mentalidad es la de un solo assert por test, en la práctica no es tan fácil y veremos que aveces tendremos que poner más de uno. Es una mentalidad que se suele tener en cuenta pero no es posible llevarla a cabo en todos los casos. Por ello mejor el mínimo de asserts por test. Lo que realmente tiene que ser único por test es el concepto a probar, que es lo que queremos poner a prueba y eliminar todo el ruido que pueda generarse a su alrededor. Con esto me refiero, si tienes una función que puede generar tres casos, por ejemplo. Dado un número este puede ser mayor, menor o igual. No haremos un test que pruebe estos tres casos de una sola vez, tenemos que hacer tres test independientes de manera que cada uno de ellos prueba un concepto, un caso. F.I.R.S.T. Las cinco reglas que siguen los test limpios: Fast, rápido: un test tiene que ejecutarse rápido, si tenemos un test que tarda bastante en completarse no nos gustará ejecutarlo con frecuencia y tardaremos más en ver posibles errores. Independent, independiente: los test tienen que ser independientes unos de otros, no podemos tener test que están conectados entre ellos porque en el momento que el primero falle todos los demás irán detrás, y será complicado saber donde empieza el problema. Repeatable, repetible: los test tienen que poder ser ejecutados en cualquier ambiente (enviroment) desde producción hasta tu portátil en casa sin conexión a internet. Self-validating, auto-validados: los test tienen que, por si mismos, dar como resultado un boolean (verdadero o falso) no queremos test que guarden un resultado en un documento de texto que tendremos que mirar a posterior y comprobar si coincide con otro que ya tenemos, Somos humanos y nos equivocamos, podemos no comprobar bien estos resultados y más cuando llevamos ya unas cuantas horas de trabajo encima ese día. Timely, oportuno: los test solo serán escritos antes del código de producción que haga que los pase. Si tratas de escribirlos después acabarás odiando los test, porque habrá partes que son muy complicadas de probar o el código de producción que tienes no está si quiera pensado para ser probado. Libro original Referencias Template method Given When Then

Límites

Límites Cuando consumimos una API de terceros existe un conflicto de ideas entre quien la crea y quien la consume. Los creadores quieren llegar a la mayor cantidad de público posible y, los consumidores de esta, quieren que sea lo más consisa posible y esté lo mejor enfocada con su problema para evitar funcionalidades superfluas. Por ejemplo si usamos un Map es normal ver lo siguiente repetidas veces en nuestro código. Sensor s = (Sensor) sensor.get(sensorId); Si, funciona y si, estámos acostumbrados a verlo con bastante frecuencia pero en términos de clean code es mejorable. Todo porque clase Map devuelve un objeto genérico. ¿Pero por qué no “solucionamos” este problema? Podemos crear una clase en medio que en lugar de devolver object devuelva Sensor directamente public class Sensor { ... public Sensor getById(String id) { return (Sensor) sensors.get(sensorId); } ... } De esta forma evitamos muchos problemas y hacemos que el uso sea más claro, además podremos limitar el uso a las funcionalidades que realmente nos hacen falta evitando el ruido del que hablamos en el comienzo de una API que trata de contentar a un gran público. Aprende con los test Si estamos haciendo uso de una nueva API es normal que al comienzo hagamos algunas funcionalidades pequeñas para comprender el funcionamiento de esta y como trabajar con ella correctamente en un proyecto de pruebas donde no rompas nada. ¿Y si en lugar de eso creamos unos test básico? ¿Qué puede tener de malo? Que cuando cambien la API nos demos cuenta porque los test fallan, si un conpañero está en la misma situación pueda ver las funciones básicas en esta “documentación” hechas con los test, si dentro de un tiempo vamos a volver a usarla tener donde refrescar la memoria. Creo que he logrado dejar claro que es una buena idea que ayuda más de lo que molesta y no te consumirá tanto tiempo como para no merecer la pena. Libro original

Manejo de errores

Manejo de errores Puede sonar raro hablar de errores en un libro que trata sobre código limpio, pero los errores son algo que ocurre porque somos humanos y como tal erramos. Un usuario puede perfectamente introducir un dato no válido y esto tenemos que controlarlo de alguna manera. Usa excepciones en lugar de códigos de error Antes era normal ver en funciones esta función devolverá un número mayor a 0 en caso de que termine correctamente, en caso contrario -1 si no se encontró el fichero, -2 si no se pudo acceder, etc. Esto ocurría porque no existían excepciones, a día de hoy hacer esto solo hace que el código de quien usa tu función se ensucie al tener que controlar si el valor devuelto es válido o no y porque no lo es. En lugar de esto todo queda más limpio si cuando ocurre un error lanzamos una excepción. Usa excepciones sin marcar (Unchecked Exceptions) Las checked exceptions son aquellas que tienen que estar en la firma del método reflejadas como que pueden ocurrir en algún momento. En los inicios cuando Java las añadió parecían una buena idea, pero con el paso de los años se han dado cuenta que no son tan necesarias como pensaban. Esto ocurre porque si, tiene sus ventajas, pero también sus inconvenientes. Uno de ellos es que estas no respetan el principio abierto/cerrado (Open/Closed Principle). Imaginemos, lanzamos un nuevo checked exception en un método y el catch de este está tres niveles por debajo en la implementación. Tendremos que ir nivel por nivel cambiando la firma de todos los métodos para que la aplicación compile nuevamente… Esto no parece una buena idea, y eso que solo son tres niveles si se tratase de una aplicación grande en la que hay muchos más niveles pasaremos una buena tarde cambiando todos los niveles. Esto podemos evitarlo usando unchecked exceptions. Explica el contexto de tu excepción Si creas una excepción, se lo más claro posible explicando el contexto y la localización del error en el mensaje. ¿Porqué ha ocurrido esta excepción? Define el flujo normal No hay porque crear excepciones sin pensar hay casos en los que, con cambiar la lógica de nuestra aplicación solucionamos el problema. Supongamos lo siguiente: try { GastosDeComida gastos = reporteDeGastos.obtenerComida(idEmpleado); gastosTotales += gastos.obtenerTotal(); } catch (GastosDeComidaNoEncontrados e){ gastosTotales += obtenerComidaPorDia(); } En este ejemplo si no encontramos los gastos en comida del empleado lanzamos una excepción y entonces añadimos la comida por días pero, ¿porqué generamos esta excepción? Si en lugar de lanzar la excepción controlamos en el método obtenerTotal(), si no hay total en este empleado devolvamos la comida por día, de manera que pase lo que pase siempre devolveremos un dato con el que poder trabajar y todo se soluciona evitando todo esto. No devuelvas nulos Si has trabajado con Java un tiempo, es normal que en algún momento te encuentres creando algo como esto: if(usuario != null){ Tarea[] tareas = usuario.obtenerTareas(); if(tareas != null){ ... } } Puede incluso que te parezca hasta normal ¡pero no lo es! si estás creando una clase y devuelves un nulo recuerda, te estás dejando más trabajo para el futuro. Puedes pensar que no es para tanto, una comprobación de si es nulo o no y ya está pero veamos, que ves más fácil de comprender y ver de primeras entre estos dos códigos: List<Empleado> empleados = obtenerEmpleados(); if(empleados != null){ for(Empleado e: empleados){ pagoTotal += e.obtenerPago(); } } List<Empleado> empleados = obtenerEmpleados(); for(Empleado e: empleados){ pagoTotal += e.obtenerPago(); } Creo que ves por donde voy, la segunda opción queda más clara. Pero además dejando la claridad y lo bonito que queda supongamos que se te olvida hacer la comprobación de si es nulo o no, ¿ahora que? a buscar en la pila del error porque es nulo y donde y como tengo que hacer para solucionarlo. Y todo porque cuando implementaste la función obtenerEmpleados() no hiciste una comprobación del tipo si no hay empleados devuelvo una lista vacía. No pases nulos como parámetros Si devolver nulos está mal, ¡pasarlos como parámetros es aún peor! Imagina que estás usando una API la cual no espera que le pases un nulo, ¿que va a pasar? Pues lo esperado nuestro “querido” NullPointerException y como lo vas a solucionar si la API es tuya puedes añadirle excepciones que, no deberían estar ahí, ensucian y hacen que sea menos claro su funcionamiento. Así que hazte un favor y evita los nulos de todas las formas que puedas. Libro original TODO Escribe tu bloque Try-Catch-Finally primero

Objetos y estructuras de datos

Objetos y estructuras de datos ¿Porque crear variables privadas si acto seguido creamos los getters y setters? Con esto hacemos que el modificador private pierda el sentido. Los getter y setter solo deben estar cuando sea necesario su existencia porque estamos compartiendo esos datos crudos. Cuando creamos una clase lo hacemos con la intención de mostrar unos datos con un formato. Por ejemplo, si estamos con una clase que sea para control del combustible de un vehículo ¿Cual de las dos clases siguientes esta mejor? public interface Vehiculo{ public double capacidadDelTanqueEnLitros; public double litrosDeGasolinaActuales; } public interface Vehiculo{ void rellenarTanque(double litros); double damePorcentajeDeCombustibleRestante(); } El segundo caso es el aconsejable, en esta clase no importa como guardamos los datos si en litros o galones o centímetros cúbicos. El usuario solo le interesa cuanto le queda respecto del total y poder indicar que ha llenado el deposito, y eso es lo que le damos en la segunda. No tenemos que dar mas datos de los que son necesarios y con el formato correcto. De forma que si en algún momento queremos cambiar la forma en la que implementamos esta clase, el tipo de datos o la forma de calcular, el usuario no le supondrá problema. Ya que en el segundo caso seguiremos devolviendo el mismo resultado, pero calculado de otra manera o en otras unidades pero haciendo la conversión antes de devolver el resultado. En el primer caso el usuario de nuestra clase tendrá que hacer cambios donde use esos campos. Datos estructurados u Objetos Pongamos unos ejemplos para reflejar las diferencias entre estos. En el primero de ellos implementamos una clase por cada figura geométrica y otra en la que calculamos el área. public class Cuadrado { public Punto superiorDerecho; public double lado; } public class Rectangulo { public Punto superiorDerecho; public double altura; public double anchura; } public class Circulo { public Punto centro; public double radio; } public class Geometria { public final double PI = 3.141592653589793; public double area(Object figura) throws FiguraNoSoportada { if (figura instanceof Cuadrado) { Cuadrado cuadrado = (Cuadrado) figura; return cuadrado.lado * cuadrado.lado; }if (figura instanceof Rectacgulo) { Rectangulo rectangulo = (Rectangulo) figura; return rectangulo.altura * rectangulo.anchura; }if (figura instanceof Circulo) { Circulo circulo = (Circulo) figura; return PI * circulo.radio * circulo.radio; } throw new FiguraNoSoportada(); } } Los programadores orientados a objetos estarán arrancándose los pelos de la cabeza al ver esto, pero tiene sus aspectos positivos si tienes que añadir una función perímetro en la clase Geometría las clases de figuras ¡no se ven afectadas! Pero por otro lado si incorporamos una nueva figura todos los métodos de la clase Geometría tendrán que ser actualizados con la nueva. Si por el contrario tenemos el caso siguiente en el que usamos la orientación a objetos en el que el método area() sea polimórfico, la clase Geometría no sera necesaria gracias al uso de las interfaces. Y en caso de tener que añadir una nueva figura ¡no tendremos que hacer nada! public class Cuadrado implements Figura { private Punto superiorDerecho; private double lado; public double area() { return lado * lado; } } public class Rectangulo implements Figura { private Punto superiorDerecho; private double altura; private double anchura; public double area() { return altura * anchura; } } public class Circulo implements Figura { private Punto centro; private double radio; public final double PI = 3.141592653589793; public double area() { return PI * radio * radio; } } Solo para dejarlo claro son dos implementaciones completamente opuestas objetos o estructuras de datos, tu decides. Si prevés que vas a incluir nuevas funciones en un futuro las estructuras de datos son mucho mas fáciles pero, por el otro lado, si tu previsión es que incluirás nuevos “tipos” de datos la orientación a objetos soluciona el problema mucho mas sencillamente. La ley de Demeter Resumiendo mucho esta ley dice que quien modifica (modulo que lo usa) un objeto no debe conocer sus entresijos (como se implementa internamente). Esta ley no es aplicable a las estructuras de datos, ya que como vimos antes el funcionamiento de estas es justamente lo contrario. Esta ley de Demeter es aplicable para la orientación a objetos. Híbridos Si vas a implementar ambos casos a medias terminaras por no implementar ninguno y quedarte con la peor parte de los dos. No hagas un híbrido de objeto y estructura de datos porque acabaras con la peor parte de cada uno de ellos, analiza y decide cual compensa mas implementar, seguramente no uses todas sus ventajas y sufras algunos de sus defectos pero es decisión tuya saber cual compensa una cosa con la otra de mejor forma. Libro original

Formato

Formato ¿Dejar una linea en blanco entre métodos o no? ¿El corchete de inicio de la función en la misma linea o en la siguiente? ¿Los campos de la clase todos en la parte superior o al final? Parecen pequeños detalles insignificantes pero a la hora de leer y comprender lo que hemos hecho es mejor seguir siempre un mismo estilo y mas aun si estamos trabajando en equipo. A veces depende del lenguaje que usemos pero en la gran mayoría de casos es un tema del cada persona. De estos temas no hay hojas de estilo estrictas que sigan todos los programadores, pero existe unas “normas” que todos conocen o van descubriendo con el paso del tiempo. Formato vertical ¿Cuanto debe ocupar un fichero? Como ya dije no hay nada escrito en Java un fichero es una clase, si estamos haciendo lo correcto. Estas si miramos casos de proyectos ya desarrollados vemos que rara vez superan las 500 lineas y que normalmente se extienden 200 lineas, en esta extensión una clase tiene mas que suficiente para realizar todas las funciones que debería. Toda nuestra clase debe de estar estructurada como un articulo empezando por un titulo que nos acerque sobre el tema que trata esta, luego una introducción donde tendremos los campos de la clase y el constructor, con lo que sabremos con una mirada rápida quienes son los “personajes” que tenemos en nuestra historia. Un nudo donde estarán todas las funciones de la clase. Todos estos métodos deben de estar tan cerca unos de otros como de relacionados estén entre si. De manera que si en un primer método usamos un segundo este debería estar justo debajo, como si continuásemos un párrafo. Todo esto es por sentido común, no lo pondremos lejos para estar saltando de arriba a abajo sin parar hasta marearnos. De igual manera las variables no las declararemos muy lejos de donde las usamos, si estas solo tienen razón de ser dentro de un método lo declararemos en este. Si es una variable para controlar un bucle, esta estará justo encima del bucle. Formato horizontal En los inicios de la informática no se escribían mas de 80 caracteres (si no me equivoco) porque los terminales donde se veían no alcanzaban mas. Hoy en día no podemos tener esta guía dada la gran cantidad de monitores en el mercado, panorámicos, etc. de todos los tamaños y colores. Pero como limite se podría decir que unos 120 caracteres es una medida buena. Los espacios entre los parámetros de una función, personalmente me gusta que después de cada coma ‘,’ dejar un espacio y es recomendable para ayudar a ver que se tratan de variables diferentes. De la misma forma podemos dejar espacios entre las variables de una operación si ayuda a que esta se vea de forma mas clara. Cuando declaramos muchas variables seguidas, hay quienes las declaran de la siguiente forma: private Socket socket; private InputStream input; private OutputStream output; private Request request; private Response response; private FitNesseContext context; protected long requestParsingTimeLimit; private long requestProgress; private long requestPars; Puede parecer a primera vista que de esta forma queda mas claro pero no, no es verdad es mas fácil no seguir la linea que toca y terminar leyendo mas arriba de donde empezamos equivocándonos. private Socket socket; private InputStream input; private OutputStream output; private Request request; Reglas de equipo Todas esta reglas que seguimos cada uno de nosotros al escribir no dejan de ser por gusto personal. Por ello si vamos a realizar un proyecto en equipo es mejor usar 10 minutos antes de empezar a hacer nada y llegar a un acuerdo entre todos para que todo este sea de la misma forma y no veamos decenas de estilos diferentes. Sera una ayuda en el futuro si tenemos que leer parte del trabajo hecho por un compañero. Libro original

Comentarios

Comentarios Comentarios, un buen comentario puede ser muy útil, puede ser la mejor ayuda que encontraste ese día pero, por otra parte pueden ser tan dañinos y malvados que ni el mismo autor de ellos sabrá porque están ahí. La mala noticia es que la mayoría de las veces los comentarios entran en este segundo grupo. Malos comentarios No arreglan mal código Se supone que cuando dejamos un comentario lo hacemos para suplir alguna carencia que tenemos en el código así que primer consejo, no maquilles mal código con comentarios. Si unas líneas de tu código no hacen lo que dicen, no están nombradas como deberían o no tienen sentido que estén ahí, no les añadas un comentario aclaratorio no es la solución. No explican que estás haciendo Si estás ante una condición como esta // Comprobar si el empleado puede ser seleccionado para jubilarse if ((empleado.señal & SEÑAL) && (empleado.edad > 65)) ¿Porque no intentas darle la vuelta a esto y hacer un código auto explicativo? if (empleado.estaPreparadoParaLaJubulación()) No son redundantes Si tienes unas líneas que se explican por si solas no dejes un comentario que explican exactamente lo mismo. // Si está cerrado devuelve la hora de apertura if(cerrado){ return new HorarioApertura(); } Tampoco hagas esto, creo que sobra explicar porqué. /** * * @param titulo El titulo del CD * @param autor El autor del CD * @param año El año del CD */ public CD(String titulo, String autor, String año){ ... } No engañan No dejes comentarios que engañan al lector, no escribas diciendo que este método devuelve verdadero cuando la tienda está cerrada y luego si miras la implementación realmente lo que hace es mirar si está cerrada y en caso de que no lo esté espera un tiempo para realizar una segunda comprobación. Porque con esto solo logras que al usar esta función no obtengas los resultados esperados. Deben aportar algo Si escribes un comentario para explicar algo que no tiene razón de ser explicado, sobra no dejes ese comentario no expliques que esta variable que estás declarando es privada si delante de ella tienes puesto private, no dejes un comentario sobre un constructor por defecto diciendo “este es el constructor por defecto”. ¿Que pretendes lograr con todo esto? Yo te digo lo que logras con todo ello, logras generar ruido en el código. Con ruido me refiero a líneas de código que no tienen razón de estar ahí que entorpecen a los demás a la hora de leer. No cierran funciones En los inicios de la programación, estos tenían algo de sentido pero a día de hoy no tienen razón de ser. Si no sabes de lo que te hablo mejor, no los has tenido que ver significa de algún modo todo que estamos por el buen camino. Me refiero a lo siguiente. while(condición()){ if(comprobación()){ for(){ ... } //for ... } //if } //while A día de hoy con los IDEs que tenemos que nos marcan con colores donde cierra cada llave, nos permiten colapsar las líneas que tiene dentro incluso, no hay razón válida para que esto siga existiendo. No son código no válido No comentes código que ha dejado de servir, que ya no funciona o incluso nunca ha llegado a funcionar. Si ese código ya no lo usas ¿para que lo “guardas”? En serio crees que en algún momento haciendo construyendo otra clase, dirás “cónchale voy a rescatar este trozo que dejé comentado, que no tiene nada que ver” porque te aviso ya, que esto no ocurre. Es más si ya se te ha ocurrido la solución una vez, se te ocurrirá una segunda y de manera más rápida, en serio no comentes código porque te costó mucho trabajo llegar a él o porque “me será útil luego”. Buenos comentarios Existen circunstancias, como ya he comentado, donde un comentario puede ser de gran ayuda. Aunque bajo mi punto de vista siguen siendo un arma de doble filo, explicaré al final porqué. Comentarios legales Comentarios que por la razón legal que sea tienen que estar escritos, con el autor, el año, empresa, etc. Comentarios informativos Comentarios que información básica de alguna sentencia compleja. Una expresión regular por ejemplo, puede estar bien explicar de manera rápida y básica que comprueba, aunque siempre se puede extraer a una clase que se encarga de estas comprobaciones, de forma que este comentario no haga falta. Explicar la intención Si has solucionado un problema no de la mejor forma, puedes indicar que lo has hecho así con un porqué. Comentarios aclaratorios En caso de que estés usando una librería que los valores que devuelve no corresponden con resultados legibles, puede estar bien un comentario a final de la línea explicando porque ese 1 que devuelve si es mayor que nuestro valor indica que debemos sumar dos valores. Javadocs Si estás creando una API pública es casi obligatorio dejar estos comentarios siguiendo el formato. TODO Estos comentarios están estandarizados y representan problemas que como programador no puedes solucionar en este momento o porque no quieres dejar lo que estás haciendo y dejas esta nota para volver en un futuro. Además al estar estandarizados los IDEs potentes en la actualidad son capaces de encontrarlos por nuestro proyecto y mostrarnos una lista con todos ellos. Libro original

Hack for good 2018, Canarias

Hack for good 2018, Canarias El pasado 8 de Marzo asistí a mi primer Hackaton junto a mi compañero de batalla Javier Santana. La primera sorpresa fue nada más llegar, íbamos con una idea preconcebida de encontrar una gran cantidad de frikis de la informática allí, realmente si lo pienso ahora en frío no se porqué, pero fue todo lo contrario encontramos personas de muchas disciplinas dirección de empresas, magisterio, diseño, incluso un profesional que venía huyendo del día a día de su trabajo quería desconectar y pensar en nuevos retos. Tras las exposiciones de los retos por lo ponentes, nos quedamos en tierra de nadie ningún reto nos llamaba con mucho interés, no sabíamos que hacer en cual de ellos participar. Observábamos como el resto de participantes ya estaban formando sus equipos y empezando a planear que hacer con su reto mientras nosotros discutíamos a que atacar, dudamos entre el propuesto por Santi Navarro para Wikimedia o, ir a por el reto de ayuda para los celiacos. Necesitábamos un impulso que nos decidiera de una vez. Y así fue, dos chicos se nos acercaron (David y Javier) al escucharnos hablar sobre el reto de Santi y nos comentaron que ellos estaban pensando ir a por el mismo, que podríamos formar un equipo. Hablamos, vimos que teníamos una idea similar, congeniábamos bien, estudian diseño algo que a Javi y a mi no se nos da especialmente bien y lo más importante, teníamos ganas. Así que había nacido un nuevo equipo de trabajo para el Hackaton. Explicación del reto El reto parte de que Wikimedia en muchos de sus artículos no tiene foto y que es una enciclopedia sin fotos en sus artículos. Estas hacen que leer se haga mas ameno y ya lo sabemos todo una imagen vale más que mil palabras. Entonces se propuso el hacer una aplicación con la que partiendo de tu ubicación actual te muestre artículos cercanos sin foto, para que puedas acercarte sacar la foto subirla y ayudar a completar entre todos esta enciclopedia. Había una evolución clara para la segunda fase del proyecto, que sería el poder la aplicación sugerirte rutas para hacer fotos o por otro lado rutas visitando puntos cercanos a modo de turismo. Pero esto último eran funcionalidades para segunda o tercera fase de la aplicación, la principal problemática era añadir fotos. Viernes Al día siguiente al llegar por la mañana vimos que ya ellos habían adelantado trabajo, tenía preparado el canvas con las ideas sobre el problema, soluciones y puntos clave del proyecto. Hablamos, corregimos, detallamos y una vez con todo organizado y dado de alta en el evento comenzamos a desarrollar cada uno nuestras tareas. En el caso de Javi y mío decidimos hacer primero una web en HTML, con datos falsos a modo de prototipo antes de comenzar a darle funcionalidades para que nuestros compañeros pudieran ir probando el material que generaban. Estuvimos con ello casi toda la mañana. Sobre mediodía Javi tuvo que salir para el trabajo y me quedé solo con la web. En ese momento decidí comenzar a jugar con la API de Wikimedia, de primeras fue un poco extraño nunca me había enfrentado a algo así a solas y no estaba muy familiarizado. Pasé esa tarde y parte de la noche, escribiendo query tras query hasta que conseguí obtener los datos que nos hacía falta. Ya con estos datos parecía que el resto sería una cosa rodada pero no iba a ser tan fácil, tocaba el siguiente problema Google Maps. Pero esto sería un problema que abordaría al día siguiente, para mí el día terminaba ahí. Me fui a dormir a casa el Viernes por la noche (me arrepiento, me perdí bastantes buenos momentos). Uno de ellos como mi compañero Javi, decide a las tantas de la madrugada borrar todo el trabajo hecho porque no le gustaba y rehacerlo todo jajaja Me manda un mensaje que leo por la mañana diciendo: “Lo borré todo, ahora está en este repositorio nuevo. Revísalo, te he dejado notas para que soluciones algún problema que tiene” Me tuve que reír. Sábado Un problema con el teníamos la suerte de contar con la ayuda de un mentor que conocía a la perfección el tema, Pablo Fernández. Le pregunté vino nos ayudó estuvo conmigo un rato hasta que logramos pintar aquella nube de puntos en el mapa, toda una victoria, gracias Pablo. Con esta parte finalizada quedaba el siguiente paso, subir las fotos. Y aquí hablamos el grupo durante un tiempo y decidimos que no nos daba tiempo, no era algo que a pocas horas de que cerrara el plazo de entrega nos diese tiempo. Preferimos no arriesgar y comenzar con la parte de la presentación, organizar los entregables y dejar todo listo para que no se nos hiciera tarde luego. Una vez todo presentado sin problemas, estando todo en orden y quedando solo preparar la presentación (en la que no participé, se lo pedí a mis compañeros) dediqué lo que quedaba de mañana a preguntar al resto de equipos que tal con sus retos. Llevaba un día allí y no había hecho lo más importante en este evento ¡conocer gente maravillosa!. Fui equipo por equipo, preguntando que tal el reto y la experiencia en general. GC004 Canarias sin Barreras, me contaban como empezaron queriendo hacer una aplicación pero vieron que había decenas ya sin terminar y cambiaron de idea hacia una base de datos que recopile toda la información de estas que ya existe y unificarlo. Porque cada una mapeaba un trozo cercano a los creadores pero no continuaban más. GC010 Testigos de Fourier, como en la madrugada sobre las 2 am la gran hora consiguieron mejorar el tiempo de la función, con lo que ello suponía y la emoción que supuso. GC002 H4Gluten, como descubrieron la poca información al respecto que hay en la web sobre los celiacos y que pasara lo que pasase querían que este proyecto continuase. Y como no agradecer a todo el equipo de mentores y organizadores, los cuales saben como relajar la tensión que existe en esta contrarreloj. ¡¡Son todo unos profesionales!! Y como estos otros tanto que no te voy a seguir contando porque esto se hará eterno, pero muy contento de conocerlos a todos ellos. Y tan contento estaba que una vez finalizadas las presentaciones, el jurado en proceso de deliberación, y haber hablado con todos de la buena experiencia que esto había supuesto me fui. Me fui contento, con mi premio el haberlos conocido y la experiencia en general. La sorpresa fue al llegar a casa que me llama Javi diciendo que habíamos ganado dos premios, y que yo como capitán no estaba… que vergüenza… Lo dicho, muy muy contento con la experiencia en general. Más información Hack for good Las Palmas Álbum completo Twitter hack for good Canarias

Funciones

Funciones Como ya vimos en el capítulo anterior nombrar coherentemente es importante, pero no es lo único que tenemos que hacer. Las funciones si están bien nombradas pero ocupan 3.000 líneas no son precisamente algo manejable, y buscar donde se produce un error en ellas será un dolor de cabeza. El libro recomienda que cada función ocupe tanto como abarque tu pantalla, siempre y cuando esta no sea extremadamente grande, unas 20 (llegando al límite, pero con la mitad sobra para poder cumplir con su objetivo la función) Haz solo una cosa! Por ello mantener funciones que sean pequeñas en cuanto a líneas de código y funcionalidad es importante. No debes de liarte y hacer en un mismo bloque 100 cosas distintas, haz una sola cosa por cada función, dale un buen nombre y así sarbás a donde acudir cuando falle. Identación Otro punto importante es la identación es algo que si sigues el consejo anterior se soluciona un poco. La cuestión de este punto es que no debes comenzar a encadenar condiciones (por ejemplo, if {} else), aumentándo la identación del código con cada una de ellas, hasta llegar a salir de la pantalla. Esto hace que no sea legible de manera rápida y cómoda y más fácil de liarte a la hora de revisar el trabajo ya hecho. Switch Esta sentencia por como está diseñada es difícil que ocupe poco, ya que con tres opciones solamente se extiende 12 líneas. Además si queremos modificar este porque ha cambiado los requisitos de nuestra aplicación un switch nos dará problemas, no siendo la mejor opción. Para evitar que esto ocurra nos recomienda hacer el uso de la abstracción. Argumentos Cuando declaramos una función a veces nos olvidamos de que los argumentos luego tendremos que completarlos al hacer uso de esa y comenzamos a solicitar una gran cantidad de ellos. Esto hace que luego cuando la llamemos quede un churro en medio de nuestro código con muchos parámetros que nos chocará cuando estemos leyendola. Además de que siempre será más complicado de modificar en un futuro. En el libro nos recomiendan, que cada función como máximo deba tener 3 parámetros. Si hace uso de más de estos la mayoría de veces significa que lo que necesitas no son los parámetros como tal, si no un objeto con todos estos atributos. Por ejemplo si queremos pintar un punto en pantalla, podremos pedir como parámetros: coordenada X, coordenada Y, radio. Pero si nos fijamos realmente las dos primeras pertenecen a lo mismo, un punto, si en lugar de esto simplemente solicitamos punto y radio queda todo más claro. Nombrar correctamente Las funciones deben de ser nombradas como verbos (acciones) de manera que sean ejemplificativas de que están haciendo en su interior sin tener que investigarla. Por esta misma razón es importante que no hagamos dentro de una función más de lo que decimos hacer, si tenemos una función comprobarConección no debemos hacer más que la comprobación, si se nos ocurriera que, una vez comprobado su correcto estado la inicializamos, tendríamos que llamar de mejor manera a esta algo como: comprobarConecciónEIniciar Argumentos de salida Estos argumentos existen por razones historicas, desde la llegada de la Orientación a Objetos (OO) carecen de sentido, es más hacen que las funcuiones no sean tan claras como deberían. Si una función tiene que devolver algo, esto lo hará por su parámetro de salida (su return) no por un argumento pasado que es un objeto que modificamos. Si tenemos que modificar un objeto lo haremos de la siguiente forma: objeto.actualizarParámetro(); Códigos de error De igual forma los códigos de error no son nada aconsejable, si los usamos tendremos que comprobar si el valor entra dentro de uno de los códigos de error que existen para esa función y luego actuar en consecuencia. Esto hace que el código se haga más complejo añadiendo comprobaciones innecesarias que con excepciones sería más fácil dentro de un bloque try {} catch Libro original

Nombres significativos

Nombres significativos En este capítulo se trata un tema el cual me importa especialmente, nombrar variables, métodos, clases o cualquier cosa que requiera ser nombrada. Es algo que en los inicios de mi aprendizaje con la programación nadie me dijo y después de dos años me di cuenta y he ido corrigiendo poco a poco. Evita la desinformación & Usa nombres que revelen infromación Dar un buen nombre puede llevar tiempo, pero ayuda a ahorrar más en el futuro. Supongamos tenemos lo siguiente private void calcula(int numero){ // haz algo largo y lioso ... } Cuando nos encontremos en nuestro programa esto tendremos que mirar primero si hay algún buen comentario que nos diga que hace, con suerte, o pasar una divertida tarde descifrando que es lo que obtenemos de todo esto y si realmente es lo que queremos porque “calcula” pero que es lo que calcula. Esto es un ejemplo extremista pero queda más claro de esta forma. Si en lugar de esto tuviésemos esto otro sería mucho más fácil private void calculaPresiónDelAgua(int metrosCubicos){ // haz algo largo y lioso ... } Nombres pronunciable De igual manera los nombres tienen que ser pronunciables no le demos nombres cortos con siglas que hace que a la hora de nombrarlo sea imposible. Imagina tienes un campo que guarda la fecha con el formato año-mes-dia-hora-minuto-segundo y decides llamar a este private date genamdhms; cuando trates de preguntar a otra persona del equipo por este campo ¿como lo nombrarás? y claro, suerte para que tus compañeros le den el mismo nombre y sean capaces de ponerse de acuerdo. Mejor si lo llamas de la forma private date generateTimestamp Nombres buscables Es bastante normal ver trozos de código como este for (int j=0; j<34; j++){ s += (t[j]*4/5); } Que no está mal cumple su función (cualquiera que sea, supongo) pero si queremos corregir, por ejemplo ese “34” en algún momento prque ha cambiado el límite, para empezar no sabemos que es lo que indica y segundo ¿como lo piensas buscar entre todas las líneas de código? sería mejor si hicieramos esto en su lugar. int realDaysPerIdealDay = 4; const int WORK_DAYS_PER_WEEK = 5; int sum = 0; for (int j=0; j < NUMBER_OF_TASKS; j++) { int realTaskDays = taskEstimate[j] * realDaysPerIdealDay; int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK); sum += realTaskWeeks; } De esta forma en un futuro si queremos cambiar el número de tareas o como se calculan estas por día, solo tendremos que usar el buscador y cambiarlo donde esté la constante. Notación húngara Por bagajes culturales de los primeros lenguajes de programación donde indicaban en el nombre de la propia variable el tipo de esta, entiendo que porque era más cómodo al no tener los lenguajes actuales de tipado fuerte y los potentes IDE que tenemos en la actualidad. Pero a día de hoy estoy no es necesario el propio compilador te avisa si no es compatible la variable (en caso de Java, por ejemplo). No hagas String telefonoString Nombres de clase Estos deben de ser nombres no verbos usar Customer, Wikipage, Account en lugar de Manager, Processor, Data Nombres de métodos De forma contraria los métodos deben de ser nombrados como verbos, identificando una acción postPayment, deletePage, save No seas lindo Nombrar tratando de ser gracioso por alguna broma que tienes con cierto compañero, o porque está relacionado con una serie de televisión famosa que “todo el mundo conoce” o “está claro lo que significa” no es una buena idea, por mucho que te vayas a reir cuando lo leas. Recuerda no todas las personas tienen el mismo nivel de cultura en los mismos temas que tú y no tienen porque saber que significan esas siglas. No añadas contexto gratuitamente Si estás desarrollando una aplicación que se llama “Gas Station Deluxe” es mala idea nombrar todas las clases con el prefijo “GSD”. Te estás añadiendo ruido cada vez que pulses “G” para usar algún método, variable, etc que realmente si empieza por esta letra el IDE te hará sugerencias de todo lo que no necesitas. A parte de estos puntos que he resumido de forma muy simple, hay otros puntos que se tratan en el libro que he decidido no añadir. Puntos no descritos Make Meaningful Distinctions Avoid Encodings Member Prefixes Interfaces and Implementation Avoid Mental Mapping Pick One Word per Concept Don’t Pun Use Solution Domain Names Use Problem Domain Names Add Meaningful Context Libro original

Código limpio

Código limpio Todo este capítulo se dedica a hacernos ver la importancia que tiene el código, que este sea lo más limpio posible y, los diferentes puntos de vista sobre que es código limpio. Además muestra como afecta en gran medida en los proyectos actuales y a los equipos que lo desarrollan. ¿Que es el código limpio? Para algunos de los autores que nombra, el código limpio es un código el cual es placentero de leer, otros nombran que tiene que ser enfocado, sin rodeos, elegante entre otros calificativos con los que estoy bastante de acuerdo. Y si es tan bueno, ¿porque todos no lo hacen de esta forma?. El libro lo deja bastante claro con un símil entre el código y el arte. Una persona es capaz de diferenciar un buen cuadro en el que el artista ha representado bien un objeto (por ejemplo) pero por mucho que esa persona vea cuadros o lea sobre ellos no va a saber pintar uno. Con el código limpio ocurre de igual forma, un programador al verlo lo reconoce pero por mucho que vea y lea sobre patrones y técnicas, sin las horas suficientes de práctica no logrará hacerlo. Y para muchos que ya se encuentran en el sector trabajando reaccionan: “Para que voy a aprender chino, si ya funciona como estoy” y no se molestan en aprender y practicar las nuevas técnicas. Equipos sufren problemas por código lioso/sucio Muestra como ha visto mermar la productividad de equipos profesionales de desarrollo porque empiezan a implementar código lioso excusándose en: “Si no, no llegaremos al deadline. Después lo arreglo” y este “después” es nunca y termina por ser un software lioso y desastroso que ha llegado a tal punto de descontrol que ni los mismos creadores son capaces de manejar. Y cuando el inversor ve lo que está pasando solicita que rescriban el código completo, contratando a un nuevo equipo que se encargará de ello mientras los otros continuan desarrollando paralelamente. Esta situación puede alargarse durante mucho tiempo, ha llegado a ver casos en los que tardan hasta 10 años, con los costes que esto conlleva. ¿Para quien escribimos el código? Un detalle a tener en cuenta cuando escribimos código es saber para quien escribimos este, algunas personas piensan que el código que escriben es para los usuarios finales de la aplicación ¿eres uno de ellos? para unos usuarios finales a los que se le da una aplicación empaquetada, compilada, ofuscada en algunos casos, que solo le importa si le soluciona su problema y en un tiempo comprensible, ¿para ellos? Escribimos código para otros programadores, otros que usaran nuestras librerías, otros que se incorporarán al equipo a mitad, otros que tratarán de ayudarnos a solucionar problemas del que no vemos salida, incluso para nosotros mismos en el futuro. Pasamos más tiempo leyendo código que escribiendo, mejor si lo que escribimos es claro y no requiere de grandes esfuerzos para comprenderlo. Libro original

Prefacio

Prefacio Se resume en una frase: “Las pequeñas cosas si importan” La filosofía de las 5 S: Sort, ordenar, saber dónde están las cosas. Darles nombres significativos para que se más fácil. Systematize, sistematizar. Como dice el dicho: Un lugar para cada cosa y cada cosa en su lugar. Cada trozo de código debe estar donde se espera que esté y, si no lo está, refactor ya!! Shine, limpieza. Mantener el espacio de trabajo limpio no dejes trozos de código comentados porque “Me harán falta en el futuro” eso no ocurre. Standarization, estandarización. El grupo de trabajo tiene que estar de acuerdo sobre como dejar el espacio de trabajo limpio y donde va cada cosa, así todos lo harán de la misma forma. Self-discipline, auto-disciplina. Para mí la más importante, tienes que tener la capacidad de seguir las practicas y ser capaz de ver su trabajo sin creer que es la mejor creación del universo, reflexionar y estar dispuesto a cambiar. Introdución Para lograr escribir código limpio no basta con leer sobre los principios y patrones. Aprender es más que conocer el conocimiento requiere conocerlo y aplicarlo, trabajar con ello, practicar y aprender aún más. Este es el objetivo del libro hacerte trabajar y ¡trabajar duro! Este libro se ha dividido en tres partes fundamentales, la primera más teórica, preparatoria para los dos siguientes donde introducir los principios y patrones que se usarán en los dos siguientes. Por ello tiene menos partes de código ejemplificativas. La segunda parte consta de ejercicios que van evolucionando en cuanto a dificultad. Ejercicios han de tomarse con calma y comprender. El tercero y último es la recompensa, una lista resumiendo lo que hemos aprendido en los ejemplos (ejercicios) anteriores. AVISO: Si lees solamente el primer y tercer capítulo de este libro añadirás uno más a la lista de “sentirse bien” pero si decides detenerte debidamente en el segundo asentarás estos conocimientos mucho mejor.

Udacity Developer Challenge Scholarship

Udacity Developer Challenge Scholarship El pasado 3 de Noviembre me llegó un mensaje al correo que no me esperaba, Google me había seleccionado para Developer Challenge Scholarship, en el apartado de android. Hace dos años intenté hacer este mismo curso con mis compañeros de Google Developer Group Gran Canaria pero por esa época, sentía que no iba a ser capaz no tenía conocimientos. Excusas tontas, miedos sin sentido, pero ahora lo he enfocado de manera distinta y con muchas ganas. Udacity Google Developer Challenge Scholarship 2017⁄2018 El primer día fue un caos total, se abrió el Slack y era incontrolable. Los mismos administradores quedaron perplejos, creando canales como locos, mensajes, mensajes y más mensajes. Era imposible ser capaz de leer o enterarte de nada. Me agobió tanto que volví a entrar en el Slack dos semanas más tarde, aquí la cosa cambió bastante. Ya era todo más controlable y comenzaba una buena experiencia. A partir de este punto las sesiones de AMA (Ask Me Anything) fueron de bastante ayuda. Además los administradores tuvieron la consideración de transcribir las preguntas a un documento en línea, para los que no podíamos estar en todas las sesiones o simplemente quisiéramos consultar algo a posteriori. Y en el canal en español nuestro compañero Felipe Joglar se curró unos apuntes impresionantes los cuales compartió con nosotros. Gracias! Mi ejecución en este curso no ha sido la correcta, en parte porque he tenido exámenes y trabajos que no me han permitido dedicarme todo lo que debiera. Y por otra parte porque han habido lecciones las cuales me han costado bastante más de lo que esperaba, llegando a estar dos semanas con ellas. Pero a pesar de todo he logrado terminar exitosamente, comprendiendo casi casi todo lo que he hecho. Una de las clases que más me gustó por el contenido ha sido la referente a Polishing the UI por varias razones, ya era la recta final y la afrontaba con bastante ilusión. Y en general todo lo referente con interfaz de usuario me llama bastante la atención porque, soy tan malo con este tema, que me fascina ver como de bien se puede llegar a hacer. En términos generales la experiencia ha sido satisfactoria, me queda mucho por delante hasta lograr comprender la magia del desarrollo en Android. Se que seguiré formándome sobre este tema porque es, a día de hoy, el que más me gusta sin duda. Y nada más que agradecer a todos tanto administradores, como a los compañeros de todas partes del mundo por la ayuda que han dado :) Veremos quien pasa a la segunda fase. PD: no he pasado a la segunda fase.

Primera página y controlador

Primera página y controlador Para crear nuestra primera página basta con crear un nuevo documento PHP // src/Controller/LuckyController.php namespace App\Controller; use Symfony\Component\HttpFoundation\Response; class LuckyController { public function number() { $number = mt_rand(0, 100); return new Response( '<html><body>Lucky number: '.$number.'</body></html>' ); } } Y luego añadir en nuestro archivo config/routes.yaml la siguiente línea: app_lucky_number: path: /lucky/number controller: App\Controller\LuckyController::number Si entramos en localhost:8000/lucky/number veremos el resultado. Anotaciones Para evitar tener que poner cada ruta en este archivo manualmente, usaremos las anotaciones. Para instalar estas (añadir la dependencia) desde el directorio del proyecto en la consola ejecutamos: composer require annotations Con esto ya podremos eliminar las líneas que habíamos puesto en routes.yaml // src/Controller/LuckyController.php namespace App\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class LuckyController { /** * @Route("/lucky/number") * @return Response */ public function number() { $number = mt_rand(0, 100); return new Response( '<html><body>Lucky number: '.$number.'</body></html>' ); } } Si entramos en localhost:8000/lucky/number veremos como todo sigue funcionando de la misma manera. Una vez en este punto al intentar desplegar en Heroku me dio problemas: Script cache:clear returned with error code 255 !! PHP Fatal error: Uncaught RuntimeException: APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file. in /tmp/build_28bf132bc15e00a1674b37dec1340dc6/CrisKrus-My-Symfony-Project-f31cfde/bin/console:20 !! Stack trace: !! #0 {main} !! thrown in /tmp/build_28bf132bc15e00a1674b37dec1340dc6/CrisKrus-My-Symfony-Project-f31cfde/bin/console on line 20 !! ! Push rejected, failed to compile PHP app. ! Push failed Para solucionar este problema basta con seguir las instrucciones del error, las cuales nos indica que con añadir la dependencia de “dotenv” con Composer, estando dentro del directorio del proyecto. composer require dotenv Otro problema mas, si nos dirigimos a la aplicación una vez ya tenemos todo en master correctamente, veremos como no hay nada desplegado realmente. Eso es porque no hemos indicado que directorio es el que queremos que use, se lo tenemos que indicar de la siguiente forma. Creando el archivo Procfile indicando al servidor de Heroku que carpeta, public en nuestro caso. web: vendor/bin/heroku-php-apache2 public/ Añadir framework-extra-bundle dependencia. *Esto no se hasta que punto es realmente necesario. composer require sensio/framework-extra-bundle Referencias Symfony, primera página Symfony, extra bundle dependencia Symfony, crear procfile

Configurar el IDE

Configurar el IDE En este caso usaré para el desarrollo el PhpStorm, instalaremos el plug-in de Symfony desde > File > Settings > Plugins > Browse repositories… Donde lo buscaremos e instalaremos. Una vez instalados lo configuraremos para nuestro proyecto > File > Settings > Languages & Frameworks > PHP > Symfony Marcamos Enable Plugin for this Project cerramos el proyecto y lo volvemos a abrir. Tras ello podemos configurar el estilo del código de acuerdo con Symfony, si queremos > File > Settings > Editor > Code Style > PHP > Set from… > Predefined Style > Symfony Referencias Jetbrains, instalar add-ons

Desplegar en Heroku

Desplegar en Heroku Tras subir el proyecto a Github, podremos desplegarlo en Heroku de manera bastante sencilla. Entramos a nuestra cuenta de Heroku y creamos una nueva aplicación. Le damos un nombre y en la parte de desplegar deployment lo conectamos con nuestra cuenta de Github. Buscamos nuestro proyecto y lo seleccionamos. A continuación, para evitarnos tener que desplegar cada versión a mano y dado que trabajaremos desde la rama develop en nuestro repositorio, marcamos la opción de Enable Automatic Deploys haciendo que con cada push a master, se despliegue automáticamente. En caso de que sea necesaria una base de datos, vamos a la pestaña Resources luego en el apartado Add-ons y pulsamos sobre Find more add-ons donde veremos los distintos add-ons disponibles, entre ellos bases de datos como MongoDB y PostgreSQL.

Iniciar el proyecto Symfony

Iniciar el proyecto Symfony Estando en el directorio en el que queremos crear el proyecto ejecutamos composer create-project symfony/skeleton my_project Siendo my_project el nombre del proyecto. Una vez descargado todo, se crea el árbol de directorios y nos indica que debemos entrar en el directorio del proyecto y ejecutar php -S 127.0.0.1:8000 public/index.php Con ello nuestro terminal quedara “inutilizado” ejecutando el proyecto, para cerrarla bastara con pulsar la combinación de teclas Ctrl + c Tras ello si vamos a nuestro explorador web y entramos en http://localhost:8000/ veremos la pagina de inicio de nuestro proyecto. NOTA: Se puede usar cualquier otro puerto si este se encuentra en uso, en mi caso el 8000 no hay problema. Referencias Symfont, configuracion symfony Curso en video, primeros pasos con symfony

Instalar PHP

Instalar PHP Instalar PHP y algún extra sudo apt-get install php sudo apt-get install php-xdebug sudo apt-get install php-intl Comprobar la versión de PHP instalada php -v Instalar los estándares del estilo de código y comentarios que sigue Symfony, estos son los PSR-0 PSR-1 PSR-2 PSR-4 de PHP. Para esto instalaremos php-codesniffer sudo apt-get install php-codesniffer Instalar estándares de código Con esto ya tendremos donde instalar estándares de desarrollo, en mi caso: /usr/share/php/PHP/CodeSniffer/src/Standards Nos moveremos hasta el para posteriormente incluir los estándares. cd /usr/share/php/PHP/CodeSniffer/src/Standards *En caso de no tener instalado git en el sistema lo instalamos para poder clonar el repositorio. sudo apt-get install git Si buscamos “symfony coding standard” en Google, en el primer resultado de Github podremos clonarlo: git clone git://github.com/djoos/Symfony-coding-standard.git AVISO: en el paso tres nos colocamos en el repositorio “/usr/share/php/PHP/CodeSniffer/src/Standards” debemos seguir en él. Añadir el repositorio clonado a las rutas de codesniffer, especificaremos la ruta absoluta. sudo phpcs --config-set installed_paths /usr/share/php/PHP/CodeSniffer/src/Standards/Symfony-coding-standard/ Comprobamos que ha sido instalado correctamente phpcs -i En la consola tendría que aparecer Symfony entre otros. Referencias David Morales, instalacion de PHP David Joos, standares

Me presento

Me presento Mi nombre es Cristian Suárez Vera soy un chico de 22 años, que si todo sale bien este 2018 será Graduado en Ingeniería Informática, residente en las Islas Canarias. Me gusta el mundo de la programación y el emprendimiento. ¿Esto que es? Mi blog personal, o mejor dicho es el lugar donde escribiré mis divagaciones sobre cualquier tema que se me ocurra, por varias razones las cuales te contaré justo ahora. Mejorar mi redacción. En un futuro megustaría también en inglés, pero primero lo primero, mi lengua nativa. Aprender más y mejorar con markdown. Aprender como es tener un sitio web y administrarlo o, en su defecto, una aplicación de cualquier tipo web, móvil, lo que sea realmente. Quiero saber como es y conocer el mundo de lanzar uana aplicación que tu mismo has hecho y enfrentarse a ello. Ser más estricto y centrarme, consiguiendo superar los objetivos del dia a dia. ¿Porque hago esto? Con esto busco empezar con un hábito semanal con el que ir mejorando mi escritura, comunicación y markdown. Supuestamente deberia de estar en una web la cual yo tendría que gestionar y de momento va por buen camino. ¿A ti que te importa como lector todo esto? A ti como lector te importa más bien poco. A ver, no digo que no lo tengas que leer pero, no soy alguien tan relevante (de momento, dame tiempo) como para que esto te importe, ni estoy contando nada tan interesante como para llegar a nadie. Esta publicación es mi manera de obligarme a cumplir todo esto yl, si alguien se anima por un casual a leer a este loco pues me alegro ;D Let’s go 2018 I’ll smash you!