Cómo maquetar HTML con el sistema grid de CSS

11/07/2018
Artículo original

El sistema grid de CSS es el nuevo estándar para estructurar elementos en 2 dimensiones en páginas web. A diferencia del sistema flexbox, grid permite definir con precisión la distribución de los elementos en los ejes horizontal y vertical simultáneamente. Además, soluciona muchos problemas clásicos de estilo y estructura con mucho menos código CSS, y nos puede servir como sustituto de frameworks responsive tipo Bootstrap, si únicamente nos hace falta la funcionalidad de columnas.

En este artículo construiremos estructuras comunes de páginas web de forma simple mediante grid, y veremos lo versátil que llega a ser. ¡Vamos allá!

Una estructura clásica: cabecera, dos columnas y pie

Disposición básica

Nuestro primer ejemplo será una página web con una disposición básica: un título arriba, una columna de contenido y otra lateral, y un pie de página.

Teniendo en mente la disposición, antes de nada diseñaremos la rejilla a la que queremos que se ajuste. Una opción razonable es disponer de 3 filas y 4 columnas, de forma que podamos colocar todos los elementos y tengamos una columna vacía a cada lado del contenido central:

Rejilla para la estructura diseñada

Para comenzar a construir la rejilla, necesitamos un elemento contenedor, el que tendrá definidas las filas y columnas de la misma. Dentro de él incluiremos todos los elementos necesarios para componer nuestra página:

<div class="basic">
  <header>Título</header>
  <nav>
    <h3>Navegación</h3>
  </nav>
  <div class="main">
    <h2>Contenido principal de la página</h2>
  </div>
  <footer>Pie</footer>
</div>

Ahora, activamos el uso de grid en el contenedor mediante la propiedad display: grid. Tras haberlo activado, para definir la rejilla en sí se declara su composición mediante las propiedades grid-template-rows, grid-template-columns y grid-template-areas, que definen respectivamente la distribución de filas, columnas y, dentro de éstas, qué nombre queremos darle a las áreas rectangulares definidas por estas filas y columnas.

Según el diseño, puede que no haga falta definirlas todas, sino que se podrán dejar con sus valores por defecto, que activan el comportamiento automático del navegador para colocar los ítems en la rejilla. Como en nuestro caso queremos un número determinado de filas y columnas y cada ítem ocupará una o varias de las áreas resultantes, las definimos todas como sigue:

.basic {
    display: grid;
    grid-template-rows: 4rem auto 200px;
    grid-template-columns: 1fr 1fr 3fr 1fr;
    grid-template-areas:
        "header header header header"
        ".      nav    main   .     "
        "footer footer footer footer";
}
.basic header {
    grid-area: header;
}
.basic nav {
    grid-area: nav;
}
.basic .main {
    grid-area: main;
}
.basic footer {
    grid-area: footer;
}

En este momento, los elementos se organizarán para cumplir el diseño que hemos definido. En particular, en la propiedad grid-template-rows se especifican 3 filas (de alturas 4 rem, automática y 200 píxeles respectivamente), y en grid-template-columns tendremos 4 columnas, la tercera de las cuales será el triple de ancha que las demás.

La unidad fr permite especificar cuantas "fracciones" sean necesarias y que se repartan el ancho de forma proporcional a los coeficientes que usemos. Por ejemplo, 1fr 2fr 5fr 2fr 1fr crearía una estructura de 5 columnas fraccionando el ancho disponible en 11 partes (la suma de los fr) y repartiendo según el valor de cada una.

La propiedad grid-template-areas sirve para dar nombre a las áreas que quedan definidas por la rejilla (usamos un punto para indicar un área vacía), y después se pueden aplicar a los elementos mediante la propiedad CSS grid-area, como vemos en el ejemplo. Si no se definen estas propiedades, entonces cada elemento se irá asignando a una única área en orden de llegada, de izquierda a derecha y de arriba hacia abajo por defecto.

Como hemos visto, en las filas y columnas se pueden especificar tamaños en distintas unidades. Además, auto no es la única forma de definir un tamaño variable: la función minmax declara un tamaño que puede hallarse en cualquier punto entre el mínimo y máximo que se pasan como argumentos. Vamos a utilizarla para mejorar la repartición del espacio disponible en nuestra página:

.basic {
    display: grid;
    grid-template-rows: 4rem minmax(calc(100vh - 4rem - 200px), max-content) 200px;
    grid-template-columns: minmax(10%, 1fr) 1fr 3fr minmax(10%, 1fr);
    grid-template-areas:
        "header header header header"
        ".      nav    main   .     "
        "footer footer footer footer";
}

Como ves, no he modificado las áreas de la rejilla, pero sí el alto de la fila de contenido y el ancho de las columnas de los extremos. Para las segundas, se define un ancho de entre el 10% del disponible y una fracción completa, de manera que se adapte según el ancho disponible. Para la fila central tomamos el alto del viewport (unidad vh) y le restamos las alturas de las otras dos filas: calc(100vh - 4rem - 200px). Establecemos una altura entre dicho cálculo y la altura máxima del contenido (max-content), de forma que éste ocupe al menos la altura completa del navegador y la exceda cuando sea necesario. Así, el pie de la página nunca subirá del borde inferior del viewport.

El resultado lo puedes ver en la siguiente figura, he aplicado algunos estilos para que se diferencien las secciones:

Página con estructura sencilla en CSS Grid

Una vez distribuidos los elementos principales de la página, la disposición de ítems dentro de cada uno se puede conseguir tanto con grid como con otros sistemas como flexbox, si vamos a usar únicamente un eje. Además, si queremos variar la posición de cada elemento según el tamaño de viewport disponible, podemos usar media queries para redefinir las áreas de la rejilla.

Una rejilla responsive: colección de ítems

Si lo que queremos mostrar en nuestra página es una colección de artículos, publicaciones en redes sociales, imágenes, etc. podemos utilizar grid para adaptar la distribución de los elementos al tamaño del viewport. En particular, podemos evitar proporcionar un número de columnas específico de forma que se muestren tantas como quepan en la página en cada momento. Por ejemplo, supongamos que nuestro elemento contenedor es el siguiente:

<div class="collection" id="collection">
  <article>1</article>
  <article>2</article>
  <article>3</article>
  <article>4</article>
  <article>5</article>
  <article>6</article>
  <article>7</article>
  <article>8</article>
  <article>9</article>
  <article>10</article>
</div>

Para que el número de columnas sea dinámico, utilizaremos la función repeat con las palabras clave auto-fit o auto-fill:

.collection {
    display: grid;
    grid-template-columns: repeat(auto-fit, 250px);
    grid-auto-rows: 300px;
    grid-gap: 1rem;
    justify-content: center;
}

En grid-template-columns definimos columnas de 250 píxeles de ancho. En lugar de definir filas, simplemente especificamos su altura con grid-auto-rows, estableciéndolo a 300px. Si el alto de los elementos fuera variable, usaríamos max-content de forma que todas las filas que se creen automáticamente sean tan altas como el ítem más alto.

Las propiedades grid-gap y justify-content ajustan la posición de los ítems de forma que quede un hueco entre cada dos columnas y entre cada dos filas, y el conjunto de elementos se muestre siempre centrado horizontalmente en el espacio disponible, respectivamente.

El resultado de este código lo vemos en la figura siguiente, donde muestro la misma colección de elementos en una ventana ancha y una estrecha. He aplicado estilos adicionales a los ítems para que se distingan mejor:

Resultado de la colección de ítems

Si queremos añadir algo de asimetría y romper la uniformidad de la rejilla, podemos permitir que algunos ítems ocupen dos columnas o dos filas, mediante los valores de tipo span x:

.collection article.wide {
    grid-column-end: span 2;
}
.collection article.tall {
    grid-row-end: span 2;
}

Aplicando las clases wide y tall a algunos de los elementos que habíamos definido, obtenemos la estructura de la siguiente imagen:

Elementos más anchos y más altos

Aún no hemos explotado todo el potencial de grid, pero espero que estas estructuras básicas te ayuden y te animen a construir tus propios diseños. Te dejo el código con los ejemplos y algunos enlaces para aprender más:

Asegura la disponibilidad de tus aplicaciones Cloud con el patrón Service Messaging

06/07/2018
Artículo original

Queue

La Web ha cambiado mucho desde los tiempos en donde los usuarios soportaban largas esperas mientras navegaban de página a página.

En la actualidad, es inaceptable que una operación lanzada por el usuario se alargue en él tiempo o produzca un error general a causa de una degradación del servicio, o una interrupción de la conexión. Y para ello utilizamos frameworks como VueJs o similares.

Con la llegada de la computación en la nube, los problemas de fallos transitorios relacionados con las comunicaciones o la sobrecarga de los servicios de backend, ganan en criticidad y requieren de patrones de aplicaciones especialmente diseñados para Cloud.

Este artículo describe el patrón que ofrece un incremento de la disponibilidad, confiabilidad y resiliencia de nuestras aplicaciones Cloud.

Service Messaging

El patrón de mensajería es aquel que se enfrenta a los problemas que producen las conexiones permanentes entre servicios remotos como son la dependencia, el acoplamiento y la limitación de la reutilización y escalabilidad de estos.

Para ello propone realizar la comunicación por medio de un bus de mensajes que realizan una conexión asíncrona entre ambos servicios (en este caso el cliente y los servicios de facturación y almacén), tal y como se ve en la figura.

Cola De Múltiples clientes comunicándose por un bus de mensajes con el pool de servicios

Los mensajes son unidades de información alfanumérica de pequeño tamaño (en un rango de Kilobytes) que son introducidos en la cola de mensajería por los clientes, para ser consumidos por los servicios de acuerdo con el flujo de trabajo que mejor resultados ofrezca.

Figura 2. Ejemplo de mensaje

Así, de mano, obtengo varios beneficios muy importantes:

  • Disponibilidad. Puedo añadir tantos clientes como quiera, porque los servicios de bus de mensajes en Cloud son “infinitos “y con un rendimiento que escala según el tamaño de la cola.
    • Cliente. El número de peticiones puede situarse muy por encima del volumen medio o el máximo esperado. El cliente no tendrá ninguna percepción de que la ejecución de la compra vaya más lenta.
    • Servicios. Los servicios pueden hacer una previsión de la carga de trabajo dependiendo del número de mensajes que puedan procesar. Evitando el riesgo de tener una degradación del servicio al recibir más peticiones de las que pueda admitir; o de un escalado automático sin límites que desemboquen en un coste que desborde al previsto y aprobado.
Compensacion De La cola de mensajes estabiliza los picos de peticiones, permitiendo un escalado de los servicios estable y previsible.
  • Escalabilidad. Podremos escalar de forma indistinta cualquiera de los servicios implicados de acuerdo con las necesidades de negocio. Por ejemplo, ante la avalancha de ventas, incrementar las instancias del servicio de facturación, dejando sin modificar el de almacén ya que la gestión del stock es mucho menos compleja. Y utilizando el tamaño de la propia cola de mensajes como indicador en las reglas automáticas de crecimiento y decrecimiento del número de instancias.
  • Resiliencia. Si una vez realizada la operación de venta en la caja y enviado el mensaje, hubiese un corte o degradación de las comunicaciones, el cliente no sufriría ninguna desconexión o fallo, siguiendo trabajando en modo local hasta que volviese la conexión y se envíen los mensajes producidos. En el caso de que una instancia del servicio de facturación se cayera o tuviera algún problema, se podría continuar realizando su trabajo con cualquier otra instancia, sin perder información de estado o mensajes.

Bregando con estados

Desde el nacimiento de la web basada en http, me tengo que “pegar” con un sistema sin estado; y aún más si quiero desplegar en el Cloud en donde el crecimiento horizontal basado en instancias es el modelo de escalado por defecto.

Es cierto que puedo utilizar mecanismos más o menos eficientes como registros en base de datos o memorias caché compartidas, pero solo cuando me veo obligado a ello.

De acuerdo con esta arquitectura, sería más correcto implementar un patrón de mensajes con metadatos, incluyendo un valor de estado en el propio mensaje que se modificaría según el servicio que lo haya procesado.

Otra forma de gestionar estados sería utilizar un patrón de colas prioritarias. En donde el propio cliente decide la urgencia de la operación simplemente ingresando el mensaje en una de las colas disponibles, y que son consumidas por los servicios en un orden temporal establecido. Creándose de forma sencilla múltiples “pipelines” o flujos de procesamiento sin necesidad de persistir ningún estado.

Cola La cola superior es prioritaria a la inferior, descargando al servicio de decidir la urgencia de cada operación

Finalmente podríamos enviar la respuesta al cliente que dio de alta la operación de compra, añadiendo la identificación única de a quien hay que remitirle el resultado del proceso de compra en el propio mensaje.

Latencia y complejidad, los efectos secundarios

Pero todo tiene su lado oscuro, y este patrón de aplicaciones en cloud no iba a ser diferente.

Así, los inconvenientes vienen primero por el aumento de la complejidad de la arquitectura de mis aplicaciones. No solamente por tener que añadir más código para que el cliente interactúe con la cola de mensajes, si no también por construir servicios que sean idempotentes, que soporten múltiples lecturas del mismo mensaje, que sepan cómo gestionar los metadatos, o que breguen con el TTL de los mensajes.

Por otro lado, estoy metiendo latencia a mi sistema y un punto de incertidumbre; ya que la arquitectura no está diseñada a las prestaciones puras, si no a la escalabilidad, disponibilidad y resiliencia.

De forma que, dos operaciones realizadas en el mismo momento serian procesadas en un intervalo de tiempo diferente, con una duración indefinida dentro de un rango temporal aproximado.

En resumen, aún siendo un patrón que se puede aplicar en múltiples escenarios, no es una “bala de plata. Y hay que descartarlo en aquellos en donde las operaciones en tiempo real sean requisito.

Show me the Code

Quiero compartir dos excelentes tutoriales paso a paso publicados por Microsoft, en donde muestran lo sencillo que es implantar este patrón con C#:

Partiendo de ambos tutoriales, lo siguiente a implantar sería el reemplazo del uso de la clave de acceso por un patrón Valet Key, para tener cubiertos mis requisitos de seguridad.

En GenbetaDev | 3 patrones de diseño imprescindibles que deberías conocer para tu sistema en cloud: Retry, Valet Key y Sharding

Imágenes: Azure Interactives

También te recomendamos

¿Cuánto queda para ver un sindicato de eSports?

Google lanza PerfKit ¿Cómo evaluar el rendimiento real de las plataformas en la nube?

Cloud9 Un IDE en la nube

-
La noticia Asegura la disponibilidad de tus aplicaciones Cloud con el patrón Service Messaging fue publicada originalmente en Genbeta Dev por Juan Quijano .

FRIKADAS: Una inteligencia artificial que detecta enfermedades oliéndote el aliento

06/07/2018
Artículo original

La inteligencia artificial ha demostrado su gran capacidad a la hora de utilizar algunos "sentidos", por ejemplo la vista (para conducir coches o analizar resonancias magnéticas), el oído (por ejemplo el asistente de tu móvil o Google Home) o el tacto (a la hora de que un robot agarre y levante una caja midiendo su peso, rozamiento, etc...). Parece ser que ahora también pueden usar el sentido del olfato.

Unos investigadores de la universidad de Loughborough en Inglaterra, el Hospital Western General, la universidad de Edimburgo y el Centro de Cáncer de Edimburgo han desarrollado un método basado en deep-learning que podría analizar compuestos en el aliento humano y detectar enfermedades (incluyendo cáncer) con un promedio de aciertos superior al obtenido por especialistas humanos.

"El sentido del olfato lo usan los animales e incluso las plantas para identificar cientos de sustancias diferentes que flotan en el aire. Pero comparado con el de otros animales, el sentido del olfato humano está mucho menos desarrollado y desde luego no se utiliza para llevar a cabo actividades diarias ", escribió la investigadora Andrea Soltoggio en Smithsonian.com. "Por esta razón, los humanos no somos particularmente conscientes de la riqueza de la información que se puede transmitir a través del aire, y puede ser percibida por un sistema olfativo altamente sensible".

Esta técnica se ha utilizado durante ya algunas décadas por parte de laboratorios en todo el mundo. Los cromatógrafos de gases y espectrógrafos de masas son dispositivos que pueden analizar el aire y extraer su composición, pudiendo identificar miles de moléculas que flotan en el aire denominadas compuestos orgánicos volátiles.


Foto por Gem & Lauris RK en Unsplash, retocada por campusMVP

El problema es la gran cantidad de moléculas detectables y el hecho de que, a veces, la más mínima traza de cierta molécula puede ser crucial en el diagnóstico, por lo que el proceso es largo y tedioso, se necesitan recabar muchísimos datos y analizarlos y es propenso a muchos errores u omisiones.

El equipo de investigación que realizó el trabajo al que hacemos referencia hoy, entrenó a una inteligencia artificial para aprender a reconocer ciertos tipos de compuestos a partir de los datos del aliento de pacientes obtenidos con uno de estos sistemas de cromatografía de gases y espectrografía de masas (GC-MS). Para ello se basaron en los conocidos frameworks de código abierto para deep-learning Keras y TensorFlow, con los que crearon y entrenaron a su red neuronal. Como datos de control y entrenamiento utilizaron muestras del aliento de pacientes que sufrían diferentes tipos de cáncer y estaban recibiendo radioterapia. Para acelerar el proceso de aprendizaje utilizaron GPUs Tesla de NVIDIA, y para mejorar la eficiencia de la red neuronal, el equipo incrementó la cantidad de datos de entrenamiento mediante técnicas de aumento de datos, logrando hasta 100 veces más muestras de datos de base que los que tenían realmente.

Gracias a esta red neuronal, una máquina es capaz de dar resultados muy fiables sobre posibles condiciones médicas, en unos pocos minutos y de manera autónoma. Un experto humano puede tardar muchas horas y en ocasiones ser menos preciso.

"Este es el primer intento exitoso de machine learning para aprender patrones iónicos y detectar compuestos a partir de datos en bruto de cromatografía de gases y espectrografía de masas", ha dicho el equipo detrás del proyecto. La red neuronal convolucional logró el mejor rendimiento cuando se implementó con dos características concretas: filtros unidimensionales para adaptarse a la estructura particular de los datos GC-MS, y una entrada de tres canales para leer señales de alta, media y baja intensidad del espectro de GC-MS altamente variable. Este nuevo enfoque descubrió incluso errores de etiquetado en las muestras proporcionadas por personas con experiencia, lo que sugiere un rendimiento promedio mejor que el humano ", explicaron los investigadores.

Los investigadores también usaron la AI entrenada con los datos anteriores para analizar nuevas muestras de aliento e inferir resultados: "Los computadores equipados con esta tecnología solo tardan unos minutos en analizar de forma autónoma una muestra de aliento que anteriormente requería horas por parte de una persona experta", dijo Soltoggio. La inteligencia artificial está haciendo que todo el proceso sea más barato, pero sobre todo lo está haciendo más confiable.

El trabajo se ha presentado en la Conferencia Internacional sobre Redes Neuronales (IJCNN 2018), en Río de Janeiro, Brasil, este mismo mes de julio de 2018. El documento de investigación fue publicado en Research Gate.

Cómo localizar y eliminar los backups de WhatsApp en Google Drive

27/06/2018
Artículo original

Hoy uno rapidito que ando liado :-)

Si eres como yo y como el 80,96% de la población española, usarás Android en tu móvil:

Y si tienes móvil entonces, con casi total seguridad, usarás también WhatsApp. Y siendo con Android tendrás necesariamente una cuenta de Google.

En ese caso, WhatsApp hace todos los días una copia de seguridad a través de Wifi a tu cuenta de Google Drive. Esta copia puede llegar a ocupar bastante (sobre todo si estás en muchos grupos y te mandan muchos videos y tonterías) y puede comerte mucho espacio en tu Google Drive si es gratuito, ya que "tan solo" tienes 15GB.

Pero si entras en Google Drive no verás la copia de seguridad de WhatsApp por ningún lado. ¿Dónde está esta copia? ¿Cuánto espacio me está ocupando?

Bueno, el caso es que no podrás ver estos datos en ninguna carpeta dentro de Google Drive ya que la API que se utiliza para hacer estos backups oculta las carpetas que usan las aplicaciones para almacenar sus datos privados, y este es el caso de WhatsApp.

Sin embargo si que podrás ver cuánto te está ocupando, y también borrar esos datos para que liberar espacio.

En realidad es muy sencillo y te lo explico en el video a continuación (ponlo a pantalla completa para verlo mejor):

¡Espero que te sea útil!

Da potencia y flexibilidad a tus tests con Jest

27/06/2018
Artículo original

Jest

El testing es uno de los conceptos más core de eXtremme Programming (XP). Ya lo decía el gran Kent Beck:

Any program feature without an automated test simply doesn’t exist

Curiosamente, JavaScript ha sido históricamente uno de los lenguajes con más frameworks de test y menos cultura de testing en su comunidad. Los frameworks han ido apareciendo y desapareciendo a la velocidad del rayo y, por fin hoy, podemos decir que tenemos un magnífico ecosistema para realizar pruebas automáticas que ha venido para quedarse.

En este post vamos a hablar sobre Jest, con él que podemos construir tests unitarios trabajando con matchers personalizados, crear mocks o comprobar snapshots de componentes visuales como algo sencillo y accesible.

Instalación y puesta en marcha

Para completar un ciclo de feedback rápido y con la máxima información en cada momento, debemos de elegir un framework de testing flexible, rápido y con un output sencillo y comprensible. Este es el caso de Jest que, basado en Jasmine, destaca por sus funcionalidades potentes e innovadoras.

Jest ha sido desarrollado por el equipo de Facebook y, aunque nace en el contexto de React, es un framework de testing generalista que podemos utilizar en cualquier situación. Una vez que empecemos con Jest, ya no querréis cambiar :)

Comenzado por el principio, es importante conocer la página principal de Jest , la cual es una de las mejoras guías que encontraréis. En ella podremos encontrar todo tipo de ejemplos y documentación que nos ayudará mucho a profundizar en el framework. En cualquier caso, lo primero es lo primero y para comenzar debemos instalarlo.

Como sucede con cualquier otro paquete JavaScript, podemos añadirlo mediante NPM o Yarn a nuestro proyecto:

npm install --save-dev jest
yarn add --dev jest

Si vamos a utilizar Jest con ES6+, entonces necesitamos algunas dependencias extra (partimos de la suposición de que vamos a usar Babel 6 para este ejemplo):

npm install --save-dev jest babel-jest babel-core regenerator-runtime babel-preset-env
yarn add --dev jest babel-jest babel-core regenerator-runtime babel-preset-env

En el caso de que queramos trabajador con React y procesar JSX, entonces añadiremos también el preset correspondiente (recordad la nueva gestión de presets que babel incorpora en sus últimas versiones):

npm install --save-dev jest babel-jest babel-core regenerator-runtime babel-preset-env babel-preset-react
yarn add --dev jest babel-jest babel-core regenerator-runtime babel-preset-env babel-preset-react

Por último, definiremos en el .babelrc los presets necesarios:

{
  "presets": ["env", "react"]
}

NOTA: En este caso, el preset de React se añade únicamente por si queréis a futuro examinar algunas características avanzadas de Jest como el *snapshot testing, pero no sería realmente obligatorio para el propósito de este artículo.*

Con todo el tooling preparado, que no es poco, si queremos ejecutar los tests desde NPM añadiremos una nueva entrada en la sección de scripts del fichero package.json de nuestro proyecto:

{
  "name": "jest-testing",
  "version": "1.0.0",
  "scripts": {
    "test": "jest"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-jest": "^22.4.3",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "jest": "^22.4.3",
    "regenerator-runtime": "^0.11.1"
  }
}

De esta manera, al ejecutar npm test (o npm t si te van los shortcuts), se invocará Jest y se ejecutarán nuestros tests:

Ejecución de Jest sin tests definidos

Por supuesto, la ejecución falla al no encontrar ningún test todavía en el proyecto, pero esto va a cambiar muy pronto :)

Para ir calentando, vamos a crear un ejemplo un poco chorra que nos permita comprobar que todo funciona correctamente a nivel de configuración del proyecto.

Para ello, crearemos un carpeta test donde almacenar nuestras pruebas automáticas y dentro crearemos un fichero sum.test.js con el siguiente contenido:

test('should sum two numbers', () => {
    let result = 3 + 2;        
    expect(result).toBe(5);
});

No hay demasiado que explicar aquí, verdad??? :)

Vamos pues a ejecutar los tests de nuevo y así nos vamos familiarizando con la salida:

Resultado de la ejecución de nuestros tests

Nuestro primer verde!! Ahora ya no podremos parar.

En los siguientes apartados analizaremos como getionar las distintas fases de definición de un test: Arrange, Act y Assert. En el Arrange estableceremos el estado inicial del que partimos (setup/tearDown), luego ejecutaremos en el Act el código que altera ese estado incial y, por último, realizaremos las comprobaciones necesarias en el Assert utilizando los matchers que Jest nos provee o, incluso, definiendo los nuestro propios.

Definición de contextos

Como estructurar nuestros tests es un aspecto fundamental que condiciona su mantenibilidad a medida que crece el proyecto y el número de tests que tenemos. Generalmente, decimos que los tests se agrupan en suites, que no son más que agrupaciones de tests que están relacionados. Personalmente, prefiero que la causa de agrupación de las pruebas sea un contexto común, un arrange compartido en el contexto de la funcionalidad que abordamos. Es en este tipo de situaciones donde realmente veremos cómo interactuan las funcionalidades que se relacionan cohesivamente.

Para la definición de este tipo de contextos, Jest nos da los siguientes niveles de agrupación:

  • Nivel de fichero. Cada suite puede ir en un fichero distinto siempre y cuando sea detectado por Jest. Esto sucede bajo ciertas condiciones como que se llame *.test.js o que esté dentro de un directorio __test__, por ejemplo.
  • Definición de contextos Podemos agrupar tests mediante la construcción describe bajo un concepto compartido. Ejemplo:
describe('User registration', () => {
   test('....', () => {});
});

Los contextos o describe son anidables, aunque normalmente no se recomienda más de dos niveles de anidamiento ya que complican mucho la legibilidad del conjunto.

Gestión del estado inicial del que partimos: Setup y TearDown

Para la ejecución de una prueba automática es necesario establecer antes de nada un escenario específico que reproduca el estado del que queremos partir. Para ello, podemos tener ya código especializado que inicializa ciertas tablas en una base de datos, crea unos ficheros en el sistema o inicializa ciertas estructuras de datos. Sea como sea, es necesario que nuestro "framework" nos proveea de ciertos métodos que se ejecutarán siempre antes y después de cada test. Es el caso de Jest, estos son beforeEach y afterEach:

beforeEach(() => {
  db.init();
});

afterEach(() => {
  db.clear();
});

test('Car should be present in the catalog', () => {
  expect(db.findCar('BMW')).toBeDefined();
});

Es posible que tanto en beforeEach como en afterEach tengamos que realizar llamadas a código asíncrono (conexión a BD, llamada a servicio o similar). Por este motivo, estas funciones son capaces de recibir promesas como resultado de su invocación, de forma que Jest esperará a que la promesa se complete antes de ejecutar el test. El único punto a tener en cuenta es que la promesa se debe devolver siempre como resultado utilizando return:

beforeEach(() => {
  return db.initAsync();
});

Por último, si la inicialización o limpieza que debemos realizar únicamente se produce al principio de lanzar toda la suite y al final, podremos utilizar las expresiones beforeAll y afterAll:

beforeAll(() => {
  return db.initAsync();
});

afterAll(() => {
  return db.clearAsync();
});

test('Car should be present in the catalog', () => {
  expect(db.findCar('BMW')).toBeDefined();
});

test('Motorbike should be present in the catalog', () => {
  expect(db.findMotorbike('BMW')).toBeDefined();
});

Por otra parte, beforeEach y afterEach se pueden utilizar dentro de un contexo definido con describe y, en ese caso, únicamente se aplicarían a los tests que están definidos en ese contexto:

Con el fin de podamos comprender la secuencia concreta de métodos disponibles y como se van ejecutando en Jest, podemos ver el output de la siguiente secuencia de definiciones:

beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));
test('', () => console.log('1 - test'));
describe('Scoped / Nested block', () => {
  beforeAll(() => console.log('2 - beforeAll'));
  afterAll(() => console.log('2 - afterAll'));
  beforeEach(() => console.log('2 - beforeEach'));
  afterEach(() => console.log('2 - afterEach'));
  test('', () => console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll

Matchers

Los matchers son las funciones utilizadas por el framework de test para comprobar si el valor esperado por la prueba automática coincide realmente con el obtenido. Evidentemente, si el valor no coincide el test fallará (FAIL) y nos mostrará, marcando la salida en rojo, cual ha sido la discrepancia de valores. Por otra parte, si valor esperado coincide con el obtenido, tendremos ese adictivo verde (PASS) que nos llevará a escribir el siguiente test.

Es por esto que, cuando comparamos dos valores al final de un test, es importante conseguir que no se pierda la intención que queremos expresar con esta prueba automática. Recordemos que los tests son la documentación ejecutable de nuestro proyecto y, si esta no es entendible y sencilla, discernir lo que realmente el software será mucho más complicado y, por consiguiente, tendremos que invertir mucho más tiempo en su mantenimiento y seguramente acabaremos cometiendo muchos más errores.

Cuando comparamos dos valores al final de un test, es importante conseguir que no se pierda la intención que queremos expresar con esta prueba automática

Si partimos de la base, nuestro test en su estructura básica de Arrange, Act y Assert deberá ser rápido de leer y sencillo de entender. No hay nada peor que encontrarse con un test en rojo, leerlo y toparse con una definición compleja que no somos capaces de asimilar en pocos segundos.

Para evitar esta situación y conseguir que nuestros tests sean realmente "semánticos", el naming es fundamental. Utilizar buenos nombres de definición de nuestros tests, buenos nombres de variables, aserciones de negocio (ya veremos más adelante que son) y cualquier otra técnica que mejore la expresividad, nos salvará la vida cuando más lo necesitemos.

Pero bueno, todo esto llegará poco a poco y, si realmente interiorizamos estos principios, podremos ir mejorando nuestra base de test ganando así velocidad de cambio en el futuro.

Para ir avanzado, podemos partir de lo básico que nos ofrece Jest por defecto para comparar tipos primitivos como son cadenas, valores numéricos o colecciones, dejando para la siguiente sección la definición de comparaciones o "matchers" personalizados. Comparaciones disponibles:

Igualdad

Ya sabemos que la igualdad en JavaScript es un tema delicado, así que en este comparador lo mejor es aclarar que se aplicará tal y como se define en el estándar para Object.is (si no conocéis esta función, os recomiendo explorar este y otros métodos de Object que muchas veces obviamos a la hora de escribir código):

expect(3 + 2).toBe(5);

Si en lugar de comprobar igualdad queremos verificar lo contrario, podemos seguir utilizando el comparador toBe precedido de not. Personalmente me encanta esta aproximación desde el punto de vista de la semántica extra que añade, ya que creo que se asemeja mucho más al lenguaje natural y facilita su lectura cuando se compara con otras aproximaciones de otros frameworks.

expect(3 + 3).not.toBe(5);

Para finalizar con las comparaciones de igualdad, no podíamos dejarnos fuera de la ecuación los valores que en JavaScript pueden ser truthy o falsy. Es por esto que en Jest tenemos soporte para cualquier comparación del estilo:

expect(null).toBeNull();
expect(null).toBeDefined();
expect(null).not.toBeUndefined();
expect(null).not.toBeTruthy();
expect(null).toBeFalsy();

Objetos y listas

Para el caso de estructuras complejas como son los objetos y las listas, incluidas aquellas que pueden presentar distintos niveles de profundidad, el operador adecuado no es tanto toBe sino toEqual, al permitir una comparación profunda o deep equality que evalua correctamente la estructura completa:

let data = {one: 1};
data['two'] = 2;

expect(data).toEqual({one: 1, two: 2});
expect(data).toEqual({two: 2, one: 1});

En el caso concreto de las listas, algo bastante habitual es el poder comprobar si el resultado de una operación que devuelve algún tipo de colección contiene el valor buscado. Para ello, haremos uso del matcher toContain, que nos permite explorar esta colección sin tener que iterar sobre ella y sin tener que utilizar funciones como indexOf o includes en la colección:

expect(['cat', 'beer', 'dog']).toContain('beer');

Valores numéricos

Ya fuera de los comparadores más básicos de igualdad, en el caso de valores numéricos disponemos de muchas comparaciones que aportan un valor semántico extra a la comprobación que intentamos expresar. Algunos de ellos son:

expect(2+2).toBe(4);
expect(2+2).toEqual(4);
expect(2+2).toBeGreaterThan(3);
expect(2+2).toBeGreaterThanOrEqual(3.5);
expect(2+2).toBeLessThan(5);
expect(2+2).toBeLessThanOrEqual(4.5);

expect(0.1 + 0.2).not.toBe(0.3);
expect(0.1 + 0.2).toBeCloseTo(0.3);

No hay nada más poco expresivo que encontrarse suites de test que únicamente hacen uso de los matchers más básicos, conformándose con su limitado valor expresivo. En este sentido, preferimos expresiones de este tipo:

expect(2+2).toBeGreaterThan(3);

Sobre otras menos específicas como la siguiente, en la que únicamente nos valemos del matcher más básico como es toBe:

expect(2+2 > 3).toBe(true);

Expresiones regulares

Cuando construimos nuestro conjunto de aserciones en los tests que diseñamos, una buena práctica es no acoplarse en exceso a las fixtures utilizadas o a la implementación de los métodos. En este sentido, lo que buscamos es validar más la estructura de la información procesada que los datos en si. Es por ello que es bastante habitual en las aserciones de nuestras tests encontrar expresiones regulares que validen un patrón en los datos, más que un resultado concreto.

Una buena práctica es no acoplarse en exceso a las fixtures utilizadas o a la implementación de los métodos

Para poder expresar este tipo de comparaciones podemos hacer uso del matcher toMatch y pasarle una expresión regular estándar:

expect('Christoph').toMatch(/stop/);

Excepciones

Partir de lo que conocemos como Happy Path en nuestros tests puede ser una buena manera de empezar a razonar sobre la funcionalidad y explorar así cual sería un diseño adecuado para la misma. Es una práctica habitual si estamos haciendo TDD. Siguiendo este proceso y a medida que avanzamos, es posible que surjan situaciones a tener en cuenta y que, por no ser tan habituales o tratarse de situaciones excepcionales, no habíamos tenido en cuenta en un incio. Para este caso, lo más recomendable es anotarlas y abordarlas en próximas iteraciones, de forma que no nos corten el flujo de trabajo que estemos siguiendo en ese momento.

Por consiguiente, si más tarde volvemos a revisitar estas situaciones excepcionales que habíamos anotado, debemos de saber que Jest nos va a poder ayudar de nuevo a evaluarlas, permitiendo recoger excepciones y otros errores "esperados" ante una Arrange concreto.

const computeValue = () => {
    throw new Error('should fail');
};

expect(computeValue).toThrow();
expect(computeValue).toThrow(Error);
expect(computeValue).toThrow('should fail');
expect(computeValue).toThrow(/fail/);

Matchers personalizados

Como hemos podido comprobar en el apartado anterior, Jest cuenta con un conjunto de matchers muy rico y que aportan mucha semántica a las comprobaciones que necesitamos realizar a la hora de escribir tests. En cualquier caso, estos matchers siguen siendo demasiado generalistas en muchos contextos y, si lo queremos es que nuestro código hable de "negocio", necesitamos poder definir nuestros propios "matchers de negocio".

Tal y como comentaba, un "matcher de negocio" es una matcher que usa expresiones de nuestro dominio a la hora de verificar una comprobación. Si trabajamos con una factura, la idea sería tener un matcher que compruebe toBePaid, en lugar de acceder al importe de la misma y comprobar que el importe es superior a 0 con toBeGreaterThan. Nada que ver!!

// Correcto pero poco semántico
expect(invoice.getPaymentDate()).toBeDefined(); 

// Muchísimos más semántico y próximo al lenguaje del dominio!!! ;)
expect(invoice).toBePaid();

Ahora que tenemos un poco más claro el valor de poder definir matchers personalizados y próximos al dominio, vamos a ver como realmente poder utilizarlos en Jest. Para ello, y si queremos que los matchers que definamos estén disponibles en todas mis suites de test, tendremos que declararlos de forma global.

Simplemente editaremos el fichero package.json y añadiremos la propiedad setupTestFrameworkScriptFile dentro de la clave jest apuntando al fichero que contendrá la definición de los matchers (en este caso setup.js):

{
  "name": "basic-sum",
  "version": "1.0.0",
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-jest": "^21.2.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "jest": "^21.2.1",
    "regenerator-runtime": "^0.11.0"
  },
  "jest": {
    "setupTestFrameworkScriptFile": "/test/setup.js"
  }
}

Si ahora creamos el fichero setup.js dentro del directorio test de nuestro proyecto, podremos definir haciendo uso de la función extend los matchers que necesitemos (en este ejemplo la posibilidad de comprobar si un valor numérico concreto está dentro de un rango):

import expect from 'expect';

expect.extend({
  toBeInRange(received, lower, upper) {
    if (received >= lower && received <= upper) {
      return { pass: true };
    }

    return {
      message: () => `expected ${received} to be in range`,
      pass: false,
    };
  }
});

Finalmente, ya puedo hacer uso de los nuevos matchers personalizados que he definido en cualquier de los tests que implemente:

test('should sum', () => {
  expect(100).toBeInRange(90, 120);
});

Watch mode

Una de las funcionalidades que resultan más prácticas es lanzar Jest en watch mode para que así pueda ir ejecutando los tests cada vez que se cambie un fichero. Esto que puede parecer mucho en bases de código con suites muy grandes, es especialmente interesante en Jest ya que ante un cambio buscará los tests a los que afecta este cambio y los relanzará, evitando el procesamiento de toda la suite completa cada vez. De hecho, en el caso en el que se encuentre con un error que detenga la ejecución, lanzará el test fallido el primero cuando se vuelvan a tirar los tests, evitando procesar antes los que ya estaban en verde.

Para ejecutar el modo watch, podemos añadir un nuevo script a nuestro package.json, de forma que lo podamos lanzar de una manera simple ejecutando npm run test:watch:

{
  "name": "jest-testing",
  "version": "1.0.0",
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-jest": "^22.4.3",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "jest": "^22.4.3",
    "regenerator-runtime": "^0.11.1"
  }
}

Una vez lanzado, nos encontraremos con una salida como la siguiente:

Captura De Pantalla 2018 06 19 A Las 17 23 25

Como podemos ver en la parte de usage, tenemos también la opción de filtrar qué tests se van a ejecutar con el fin de centrarnos únicamente en un subconjunto de los tests y no estar lidiando con el set completo que puede ser muy numeroso. Maravilloso!! :)

Cobertura

La cobertura de nuestra suite de test es el tanto por cien del código de producción por el que pasa alguna de nuestras pruebas automáticas.

La cobertura es una de esas métricas que las carga el diablo. Si nos sirve para ir detectando partes del código o hot spots en los que hemos incidido menos y que son importantes porque absorben mucho cambio, entonces la cobertura es nuestra amiga. Si la usamos como un indicador de calidad de cara a negocio o que el propio negocio nos exige para cumplir con ciertos requisitos, entonces puede ser peligroso, ya que podemos subirla rápidamente sin realmente estar prestando atención a la efectividad de las pruebas que incorporamos. En general, huid de buscar un valor concreto o de intentar superar un umbral prefijado. Si la tomamos como una métrica orientativa que, apoyada en las historias de usuario que nos entran, nos permite dirigir el cambio y nuestro empeño cuando hacemos testing, entonces nos aportará mucho valor.

La buena noticia es que obtener un análisis de cobertura en Jest es extremadamente sencillo, ya que puede ser obtenida mediante la opción --coverage, de forma que de nuevo podemos ampliar la sección de scripts de nuestro package.json para lanzar Jest con esta opción añadida a la que únicamente querremos recurrir cuando sea necesario:

{
  "name": "jest-testing",
  "version": "1.0.0",
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-jest": "^22.4.3",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "jest": "^22.4.3",
    "regenerator-runtime": "^0.11.1"
  }
}

Cuando ejecutamos Jest con cobertura, se mostrará una tabla resumen extra al final de la ejecución de nuestros tests que tendrá más o menos este aspecto:

Captura De Pantalla 2018 06 19 A Las 17 42 47

Conclusiones

Como te puedes imaginar, esto es sólo la punta del iceberg y Jest esconde muchas otras sorpresas interesantes. Entre otras muchas, cabe destacar el mocking automático a distintos niveles, el soporte para snapshot testing y la integración por defecto de JSDOM para hacer tests sobre código que necesite tener disponible el DOM del navegador (React, Vue, Angular, etc) sin necesidad de levantar un navegador.

Muchas posibilidades para iniciarse en una de las prácticas de XP que más valor aporta al diseño y a la estabilidad de nuestro software.

También te recomendamos

La evolución de las interfaces de comunicación hombre-máquina: escribir, tocar, hablar… pensar

JavaScript BenchMark con Benchmark.js

Karma.js, el concepto de Test Runner

-
La noticia Da potencia y flexibilidad a tus tests con Jest fue publicada originalmente en Genbeta Dev por Ricardo Borillo .

Ponencias DotNet2018: Docker, Xamarin Forms y posibilidades gráficas de los navegadores

27/06/2018
Artículo original

 

El pasado 29 de mayo se celebró en Madrid la DotNET2018, el evento más importante del año de la comunidad .NET, que fue un auténtico exitazo. Las entradas literalmente volaron y no cabía un alfiler. Así que, desde aquí va una rotunda felicitación para los compañeros de Plain Concepts que se encargaron de organizarlo.

Ya os comentamos por aquí en su día que, aparte de ser orgullosos patrocinadores, tres de nuestros tutores participarían en las charlas, así que allí estuvimos en primera fila. Es los malo de tener tutores especialistas de primer nivel, que cada dos por tres son secuestrados para participar en eventos ;-D. 

Pero esto también tiene su lado bueno, y es que nuestros alumnos pueden contar con instructores de primera categoría sin necesidad de moverse de su casa o lugar de trabajo.

En fin, al grano: el evento de grabó por lo que ahora puedes disfrutar de las ponencias aunque no hayas podido asistir.

Esta fue nuestra alineación "titular" en el evento: 

Eduard Tomás: Containers en .NET

La participación de nuestros tutores la abrió hacia el mediodía Eduard Tomás, nuestro profe de Docker. Un auténtico megacrack (VS & Development Technologies MVP) quien, mano a mano con Ramón Tomás, nos dió una charla magistral de buenas prácticas con .NET Core y .NET Framework usando contenedores. También vimos cómo Docker nos puede ayudar en escenarios Full Framework, lift and shift (migraciones) además de mostrarnos un ejemplo de pipeline de integración y despliegue continuo utilizando Docker con Visual Studio Team Services (VSTS).

Encontrarás los ejemplos del vídeo en GitHub.

Vídeo: Containers en .NET

[youtube:hc2jUraOC7o]


José Manuel Alarcón: Por qué Superman lleva los calzoncillos por fuera y qué tiene que ver esto con tu navegador

A Eduard le siguió Jose Manuel Alarcón , Microsoft MVP desde 2004 (casi nada) e instructor especialista en FrontEnd.

A pesar de tener algunos problemillas técnicos sobrevenidos, los solventó con creces y le pegó un buen repaso a las posibilidades gráficas y de vídeo en los navegadores actuales con CSS y JavaScript. Y es que lo que podemos hacer a nivel de imagen y vídeo actualmente en el frontend casi da vértigo si viviste los espartanos inicios de la web.

Y de propina aprendimos por qué Superman lleva los calzoncillos por fuera, que parece que no, pero viene a cuento. Míratela de arriba abajo, que no tiene desperdicio, con mención especial para los ejemplos de efectos chroma en vídeo en tiempo real en el navegador. Tela marinera.

Por cierto, encontrarás los ejemplos de código de la presentación en su perfil en GitHub.

Vídeo de la presentación de José Manuel

[youtube:b0a_aWEBrWA]


Javier Suárez: Xamarin.Forms Everywhere

Y ya por la tarde, cerró la participación de nuestros tutores nada menos que Javier Suárez, otro monstruo (de los buenos) que es Xamarin MVP, Microsoft MVP, además de autor y tutor de nuestro solicitadísimo curso de Xamarin.

En su charla, Javier nos cuenta cómo con Xamarin.Forms ya no nos limitaremos solamente a crear una interfaz para exportar nuestras apps para móviles iOS y Android, sino para un montón de sistemas y dispositivos presentes y futuros. Javier nos enseña también cómo realizar aplicaciones nativas Web, WPF, Tizen o apps para Linux con Xamarin.Forms; y descubriremos cómo combinar elementos 3D en interfaces complejas y cómo gestionar dispositivos IoT con Xamarin.Forms y Xamarin.IoT.

Vídeo de la presentación de Javier sobre Xamarin.Forms

[youtube:Ha6F1dCrEag]

Si te interesan tanto la presentación como los ejemplos de código de Javier, los encontrarás todos en este post de su blog.

Hasta aquí las ponencias de nuestros tutores, pero si te quedas con ganas de ver más no te preocupes: tienes todas las ponencias del evento en el canal de de YouTube de nuestros amigos de Plain Concepts, quienes se encargaron de organizar este auténtico eventazo.

Conceptos esenciales sobre compilación e interpretación

26/06/2018
Artículo original

Si programas habitualmente en unos pocos lenguajes, tendrás la costumbre de seguir siempre los mismos pasos para ejecutar los programas que escribas. Según el lenguaje, eso involucrará utilizar un intérprete, transformar tu código a ejecutable mediante un compilador, o incluso utilizar herramientas de automatización para pasar de un punto a otro. En este artículo nos interesa estudiar cuál es realmente la tarea que cumplen todos estos sistemas y qué los diferencia.

Antecedentes

Primero, no debemos considerar la compilación y la interpretación como la única forma de producir y tratar programas.

Por un lado, existen lenguajes y programas teóricos, es decir, que no llegan a ejecutarse en un ordenador pero también tienen su importancia, al poder razonar sobre ellos de forma lógica: el cálculo lambda de Church es un claro ejemplo. Se trata de un lenguaje diseñado con el razonamiento lógico como propósito, que incorpora reglas sencillas de cálculo (sustituciones y reducciones) para resolver las expresiones. Así, para este lenguaje existen tanto intérpretes como teoremas matemáticos.

Por otro lado, otro tipo de programas que no se interpretan ni compilan son los que se componen directamente en el lenguaje de la máquina que los va a ejecutar. En particular, los primeros programas de ordenador, escritos por Ada Lovelace entre 1842 y 1843, consistían en unas notas especificando en tablas y diagramas cómo realizar el cálculo de números de Bernoulli en la máquina analítica de Charles Babbage. Esta máquina, que podía realizar procesamiento de propósito general, era meramente un diseño y no se pudo construir hasta un siglo después (pese a ello, se pudo comprobar que los programas de Lovelace eran correctos). El equivalente actual sería escribir un programa en ensamblador, que se puede convertir directamente a código máquina transformando cada instrucción en su equivalente en binario.

Por tanto, la compilación y la interpretación son necesarias cuando la intención es ejecutar nuestro programa en una máquina, y el lenguaje de nuestro programa es una abstracción, es decir, no se corresponde directamente con el lenguaje máquina. En este sentido, podemos verlas como tareas de traducción, de un lenguaje origen de alto nivel (el de programación) a un lenguaje objetivo de bajo nivel.

El primer lenguaje de programación de alto nivel fue Plankalkül, diseñado entre 1942 y 1945, pero el primero con una implementación real y de uso común fue Fortran en 1954.

Compilación

La compilación es la conversión de código en un lenguaje en otro, en un paso previo a su ejecución. Normalmente cuando pensamos en compilación hablamos de su versión más tangible, aquella que nos da un binario ejecutable como salida. Se suele denominar a este tipo de compilación ahead-of-time (AOT). Muchos de los lenguajes clásicos y más rápidos se utilizan con compiladores AOT: desde Fortran hasta Rust, pasando por C y C++. Este tipo de compilación permite realizar optimizaciones complejas, por muy costosas que sean, y adaptar el ejecutable final a la máquina donde se va a ejecutar (el proceso de compilar para una plataforma distinta de la de compilación se llama compilación cruzada).

Si la compilación no es de este tipo, puede ser porque no veamos el binario generado, sino que directamente se pase a su ejecución, estrategia conocida como compilación just-in-time o JIT. Esta permite aplicar ciertas optimizaciones que aprovechen la información disponible en tiempo de ejecución sobre la máquina, pero no puede realizar todas las optimizaciones AOT, ya que esto generaría un retraso muy perceptible en la ejecución. Para optimizar al máximo el rendimiento al usar compilación JIT, algunos lenguajes realizan un paso de pre-compilación intermedia que no genera código máquina sino bytecode. En ese paso se trata de conseguir producir un código resultante de bajo nivel independiente de la plataforma de ejecución, de forma que la compilación JIT sea más rápida. Conocidos lenguajes que suelen utilizar compilación a bytecode y JIT son Java y C#. La mayoría de implementaciones de JavaScript son también compiladores JIT.

Por otro lado, cuando la compilación no genera un binario en código máquina sino un resultado en otro lenguaje de programación, se trata de una compilación fuente-a-fuente (source-to-source), en ocasiones conocida como transpilación. Los casos más comunes de transpilación los encontramos en tecnologías web, ya que los navegadores principalmente permiten ejecutar código JavaScript (aunque WebAssembly está llegando), por lo que si queremos programar en el front-end con otro lenguaje debemos compilar a JS. El interés de esto reside en que el lenguaje origen puede proveer características que no estén disponibles en el objetivo, como tipado estático, programación funcional, otras orientaciones a objetos, etc. Algunos lenguajes que se transpilan son TypeScript, PureScript o Dart.

Interpretación y lenguajes interpretados

La forma alternativa de ejecutar un programa a partir del código en un lenguaje de programación es no generar una traducción a código máquina, sino analizar el código y realizar los cómputos que éste indique, bien directamente o bien a partir algún tipo de representación intermedia que no constituya un programa en código máquina. En este caso se dice que el programa es interpretado. Mientras se produce la interpretación, debe ejecutarse en el sistema el programa que la realiza, es decir, el intérprete del lenguaje. Este puede estar interpretado también, en cuyo caso hace falta un programa al final de la cadena de intérpretes que sí esté en código máquina.

Las representaciones intermedias que puede generar el intérprete son generalmente de dos tipos: un código de bajo nivel o una estructura de datos. El código de bajo nivel puede ser bytecode como en compilación JIT, o código enhebrado que consiste únicamente en llamadas a subrutinas del intérprete. Las estructuras de datos, por otro lado, suelen ser árboles de sintaxis abstracta (AST) que se van recorriendo para obtener los resultados de la ejecución. Estas últimas no son tan utilizadas ya que producen mayores sobrecostes que los códigos de bajo nivel. Ejemplos de lenguajes típicamente interpretados son Python (con bytecode), Ruby (usaba ASTs hasta la versión 1.8), PHP (con bytecode) y Perl (utiliza ASTs).

La interpretación de bytecode y la compilación JIT son muy similares, y las diversas implementaciones que existen forman más un espectro que dos categorías diferenciadas. Podríamos considerar que un bytecode es interpretado cuando su traducción a código máquina y su ejecución están totalmente entrelazadas, mientras que es compilado cuando primero se genera la traducción y posteriormente se ejecuta. Sin embargo, muchos intérpretes JIT (conocidos como máquinas virtuales) traducen el bytecode a trozos, en los momentos en que se prevé que son necesarios, desdibujando así la línea entre ambas estrategias.

Fuentes y más información:

Desarrollando microservicios reactivos con Micronaut

25/06/2018
Artículo original

Micronaut

Micronaut es un nuevo framework para la JVM que soporta Java, Groovy y Kotlin y que está diseñado para construir microservicios cloud native.

La versión actual de Micronaut permite construir un JAR con un Hello World que ocupa 10MB y que se puede ejecutar con un heap de tan sólo 8MB siendo el tiempo de arranque de menos de un segundo cuando programamos con Java. Además el tiempo de arranque de Micronaut y el consumo de memoria no crece linealmente con el número de líneas de código porque toda la inyección de dependencias, AOP y generación de proxies ocurre en tiempo de compilación.

El proyecto ha sido creado por OCI y el mismo equipo que desarrolla el framework Grails. Fue oficialmente presentado por Graeme Rocher en Greach 2018 el pasado Marzo en Madrid, liberado con licencia Apache 2.0 el 23 de Mayo y publicada la primera version milestone el 30 de Mayo.

Entre los objetivos con los que se ha diseñado Micronaut se encuentran:

  • Arranque prácticamente instantáneo
  • Muy bajo consumo de memoria
  • Fácilmente testeable
  • Inyección de dependencias y AOP
  • JAR generados de muy poco tamaño
  • Prácticamente sin dependencies externas
  • 12 factor

Micronaut proporciona un servidor y cliente HTTP además de soportar microservicios reactive y non-blocking al estar basado en Netty. En la parte del cliente HTTP, es posible construir uno declarativamente con una simple interfaz Java y anotaciones. Este cliente es implementado, a diferencia de la mayoría de herramientas y bibliotecas existentes, en tiempo de compilatión, lo que hace que se reduzca el consumo de memoria.

Como no podía ser de otra forma, Micronaut también soporta GORM - el ORM de Grails - que proporciona un conjunto de APIs para acceder de una manera sencilla y potente a todo tipo de bases de datos SQL, así como Neo4J, Redis, MongoDB, GraphQL y Cassandra.

Micronaut está desarrollado por los creadores del framework Grails y se inspira en las lecciones aprendidas después de crear muchas aplicaciones, tanto monolitos como microservicios, durante los años utilizando Spring, Spring Boot y Grails.

Empecemos

Micronaut proporciona un CLI para poder crear aplicaciones de manera sencilla. Para instalarlo usaremos SDKMan!, del que ya hablamos hace tiempo.

$ sdk install micronaut 1.0.0.M1

Una vez instalado el cliente podemos crear una aplicación ejecutando:

$ mn create-app my-app

Por defecto Micronaut crea una aplicación Java y utiliza Gradle como sistema de build pero es muy sencillo, por ejemplo crear una aplicación Kotlin y usar Maven:

mn create-app -build maven -features kotlin my-app

Además, si ejecutamos únicamente mn entraremos en el modo interactivo en el que tenemos autocompletado usando el tabulador y podremos ver todas las opciones y comando existentes:

 mn
| Starting interactive mode...
| Enter a command name to run. Use TAB for completion:
mn> 

create-app   create-federation   create-function   create-profile   help   list-profiles   profile-info

mn> create-app -

-inplace     -build       -lang        -profile     -features    

mn> list-profiles 
| Available Profiles
--------------------
* base - The base profile
* federation - The federation profile
* function - The function profile
* function-aws - The function profile for AWS Lambda
* service - The service profile


mn> profile-info base
Profile: base
--------------------
The base profile

Provided Commands:
--------------------
* create-bean - Creates a singleton bean
* create-job - Creates a job with scheduled method
* help - Prints help information for a specific command

Provided Features:
--------------------
* annotation-api - Adds Java annotation API
* config-consul - Adds support for Distributed Configuration with Consul (https://www.consul.io)
* discovery-consul - Adds support for Service Discovery with Consul (https://www.consul.io)
* discovery-eureka - Adds support for Service Discovery with Eureka
* groovy - Creates a Groovy application
* hibernate-gorm - Adds support for GORM persistence framework
* hibernate-jpa - Adds support for Hibernate/JPA
* http-client - Adds support for creating HTTP clients
* http-server - Adds support for running a Netty server
* java - Creates a Java application
* jdbc-dbcp - Configures SQL DataSource instances using Commons DBCP
* jdbc-hikari - Configures SQL DataSource instances using Hikari Connection Pool
* jdbc-tomcat - Configures SQL DataSource instances using Tomcat Connection Pool
* junit - Adds support for the JUnit testing framework
* kotlin - Creates a Kotlin application
* mongo-gorm - Configures GORM for MongoDB for Groovy applications
* mongo-reactive - Adds support for the Mongo Reactive Streams Driver
* neo4j-bolt - Adds support for the Neo4j Bolt Driver
* neo4j-gorm - Configures GORM for Neo4j for Groovy applications
* redis-lettuce - Configures the Lettuce driver for Redis
* security-jwt - Adds support for JWT (JSON Web Token) based Authentication
* security-session - Adds support for Session based Authentication
* spek - Adds support for the Spek testing framewokr
* spock - Adds support for the Spock testing framework
* tracing-jaeger - Adds support for distributed tracing with Jaeger (https://www.jaegertracing.io)
* tracing-zipkin - Adds support for distributed tracing with Zipkin (https://zipkin.io)

Nuestro primer servicio

El siguiente código muestra cómo crear un endpoint HTTP en Micronaut:

@Controller("/hello")
public class HelloController {
    @Get("/{name}")
    public String hello(String name) {
        return "Hello " + name;
    }
}

Como se puede ver el modelo de programación es muy similar a Spring Boot o Grails. Los desarrolladores que estén acostumbrados a ambos frameworks van a tener una curva de aprendizaje muy baja. Esto permitirá, por ejemplo una transición rápida y sencilla a Micronaut.

Si arrancamos la aplicación y nos conectamos con un navegador (por defecto el servidor HTTP se ejecuta en un puerto aleatorio pero es posible definir uno por configuración) veremos algo como esto:

$ ./gradlew run
> Task :run 
23:59:44.420 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 502ms. Server Running: http://localhost:43134

Servidor Http

¿Y esto cómo lo testeamos?

Para ello vamos a utilizar Spock aunque es posible utilizar JUnit o Spek si así lo queremos.

En primer lugar vamos a crear un interfaz que nos permitirá comunicarnos con el servidor. Micronaut será el encargado de, en tiempo de compilación, implementar el interfaz con el cliente HTTP por nosotros.

@Client('/hello')
interface HelloClient {
    @Get("/{name}")
    String hello(String name)
}

Ahora ya podemos crear el test:

class HelloControllerSpec extends Specification {

    @Shared @AutoCleanup
    EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer) // 1

    void "test hello world"() {
        given:
            HelloClient client = embeddedServer.applicationContext.getBean(HelloClient) // 2

        expect:
            client.hello("Iván") == "Hello Iván" // 3
    }
}
  1. Creamos una instancia única y compartida por todos los tests de un servidor embebido que ejecuta nuestra aplicación.
  2. Obtenemos el bean del interfaz HelloClient que hemos definido anteriormente y que utilizaremos como cliente HTTP para conectarnos al servidor. Recordemos que Micronaut ha implementado el cliente HTTP.
  3. Ejecutamos la llamada HTTP y comprobamos el resultado.

Una de las premisas con las que se ha construido Micronaut es que sea muy rápido y sencillo de testear. Cuando utilizamos frameworks como Spring y Grails, escribimos principalmente test unitarios en lugar de tests funcionales o de integración no porque prefiramos hacerlo ni porque sea deseable, sino porque los tests funcionales son muy lentos. Tendemos a mockear nuestra infraestructura, dependencias,... simplemente porque arrancar el servidor es lento y pesado. En Micronaut todo ocurre en tiempo de compilación así que la velocidad de ejecución de los tests es muy rápida.

Ejecucion Test

Como podemos ver la ejecución del test es de sólo 185 ms. Esto no es ningún truco. Es la aplicación real comunicandose utilizando HTTP con el servidor. Para comprobarlo podemos incrementar el nivel del log del cliente HTTP añadiendo al archivo logback.xml:

<logger name="io.micronaut.http.client" level="TRACE">

Si ejecutamos el test de nuevo podemos comprobar que se hacen peticiones HTTP reales entre el cliente y el servidor.

Ejecucion Test Trace

Programación reactiva

Micronaut también soporta programación reactiva out-of-the-box al implementar Reactive Streams por lo que es posible utilizar cualquier biblioteca que lo soporte como RxJava2, Reactor o Akka.

Vamos a añadir soporte RxJava2 al ejemplo anterior:

@Get("/{name}")
public Single<String> hello(String name) { // 1
    return Single.just("Hello " + name);  // 2
}
  1. En lugar de devolver un String devolvemos un Single<String>. La clase Single de RxJava2 implementa el Patrón Reactivo para un único valor de respuesta.
  2. Creamos un Single a partir del mensaje con la respuesta.

El cliente quedaría:

@Client('/hello')
interface HelloClient {
    @Get("/{name}")
    Single<String> hello(String name)
}

Y finalmente el test:

void "test hello world"() {
    given:
        HelloClient client = embeddedServer.applicationContext.getBean(HelloClient)

    expect:
        client.hello("Iván").blockingGet() == "Hello Iván" // 1
}
  1. En el test convertimos la llamada en bloqueante

Con un pequeño cambio tenemos una implementación reactive y non-blocking en el servidor y un cliente reactive para testear todo.

Micronaut es más que esto

Hemos visto un ejemplo muy sencillo pero Micronaut ofrece gran cantidad de opciones y características pensadas en Microservicios:

  • Service discovery: Con soporte de Consul y Eureka
  • Circuit Breaker con soporte de Retry
  • Configuración compartida
  • Cliente HTTP con Load-Balancing
  • Tracing con soporte para Zipkin y Jaeger
  • Seguridad
  • Soporte de serverless en AWS Lambda
  • Soporte para Hystrix y Ribbon
  • Micrometer
  • Y muchas más integraciones

Otra característica a destacar es que estas integraciones están construidas sin añadir práctiacemente dependencias adicionales al framework lo que evita que los JARs finales tenga excesivo tamaño.

Más información

Esto es sólo el comienzo. Podeis encontrar el código completo de la apliación hello-world en Github.

Para conocer más sobre el framework recomiendo la de presentación de Graeme: Launching the Micro Future: Groovy, Grails and Project Particle disponible en el canal de Youtube de Greach.

También está disponible la documentación oficial, la documentación del API y guías con distintos ejemplos en dónde se irán añadiendo nuevas en los próximos meses.

Finalmente la versión Milestone 2 estará disponible a final de Junio y se espera la versión final 1.0 antes de fin de año.

También te recomendamos

Trabajar con microservicios: evitando la frustración de las (enormes) aplicaciones monolíticas

Dime cuál es la organización de tu equipo de desarrollo y te diré cómo es la arquitectura de tu software

La evolución de las interfaces de comunicación hombre-máquina: escribir, tocar, hablar… pensar

-
La noticia Desarrollando microservicios reactivos con Micronaut fue publicada originalmente en Genbeta Dev por Iván López .

La receta para organizar un buen evento de desarrollo y los errores que lo harían fracasar

23/06/2018
Artículo original

Events 01

El nivel de los eventos para desarrolladores en España es bastante alto. Un muestreo de lo más selecto lo hicimos aquí mismo hace un par de meses con el artículo titulado 'Los 15 eventos para desarrolladores en España que no te puedes perder en 2018'. Pero, ¿qué tiene que tener un buen evento de desarrollo? Y ya que estamos, ¿qué no debe tener?

En este artículo vamos a intentar responder a estas preguntas y no sólo desde el punto de vista del asistente si no también desde el del ponente. Asociaciones y empresas que estáis pensando en lanzaros a organizar vuestro evento, estad ojo avizor.

Una web verdaderamente útil

Events 02

Los interesados en tu evento van a buscar información del mismo principalmente a través de sus redes sociales (ya hablaremos de ellas y como utilizarlas más adelante) y de la web. Por lo tanto se tiene que tratar de una web clara, sencilla, informativa y en permanente actualización. Sobre tu evento debe quedar meridianamente claro cuándo es, a quién se dirige, cómo conseguir las entradas (y a que te dan derecho), cómo llegar, cómo enviar una charla (y tus ventajas y remuneración como ponente), quiénes son los patrocinadores...

Puedes dejar la venta de entradas (e incluso el Call for Papers) en manos de webs especializadas, sobre todo si se trata de un evento pequeño, pero si la mantienes bajo "tu ala" puedes conseguir una mejor experiencia. Por ejemplo puedes preguntarle al comprador la talla de camiseta que usa o si tiene alguna alergia o intolerancia alimentaria (o si no come algo por temas de religión o conciencia) y así conseguir un evento más inclusivo... aunque de inclusión hablaremos también más tarde.

Si externalizas esta venta de entradas siempre puedes hacer estas comprobaciones a posteriori enviando emails o formularios a los asistentes aunque se corre el riesgo de tener un retorno mucho menor.

Un precio ajustado y asumible

Comparados con los eventos y conferencias en el resto de Europa o Estados Unidos, en España los precios de las entradas a los mismos no son nada caras. Iván López (@ilopmar), desarrollador Groovy y colaborador también de esta casa, nos cuenta lo siguiente al requerimiento de qué le atrae de una conferencia o evento para ser ponente: "Principalmente lo que más me atrae son el resto de ponentes que pueda haber y la posibilidad de ver las charlas de la conferencia de manera gratuita. Hay conferencias que tienen precios asequibles, al menos en España, pero en Europa lo normal suelen ser 400-600€ que, al ser ponente, no hay que pagar. Además, en la mayoría de las conferencias, el día anterior siempre hay una cena de speakers en la que conoces a otros ponentes como tú, hablas con ellos de tecnología, de tu charla, de la suya,... y de muchos temas que van surgiendo.".

De las actividades paralelas en los eventos hablaremos luego pero ahora sigamos hablando de los precios, que si bien no son tan caros como allende los Pirineos, pueden suponer una barrera de entrada: hay gente que no se puede permitir pagar cientos de euros y/o los gastos de desplazarse a otra ciudad durante varios días ni tiene una empresa detrás que se haga cargo. Por eso se puede llegar a alcanzar cierto grado de endogamia o lo que es lo mismo: que en todos los eventos de similares temáticas o características se vean siempre las mismas caras.

Hay que intentar ajustar los precios y proponer ofertas: para los early birds, para grupos, para los colectivos menos representados, para aquellos que sólo quieran asistir online (a través de streaming) o acceder al material a posteriori...

Eso si, si apuestas por un evento gratuito (o incluso de precio simbólico), te puedes encontrar con situaciones desagradables como que la gente luego no acuda, como comentaban recientemente en este hilo de Twitter.

Ayuda a los "extranjeros"

Si tienes la "aspiración" de que desarrolladores de fuera de tu ciudad acudan a tu evento no sería una mala idea intentar llegar a acuerdos con agencias de viaje para conseguir descuentos en trenes (o aviones, aunque esto suele ser más complicado) y hoteles. Venga, sabemos que es un engorro pero si tu primo lo pudo hacer para su boda, vosotros también podéis.

Volvemos a recurrir a Iván López y lo invitamos a recordar el mejor evento en el que jamás ha estado: "SpringOne 2GX 2015, en Washington DC. SpringOne 2GX era LA conferencia de referencia de Spring, Groovy y Grails a nivel mundial y me aceptaron nada menos que tres charlas. Me sorprendieron muchísimas cosas: más de 1000 asistentes en uno de los hoteles más grandes de DC, las keynotes fueron por la noche después de la cena, cuando lo habitual en las charlas en Europa es que las keynotes son por la mañana a primera hora, muchísimos sponsors y mucho swag a repartir. Fue mi primera conferencia en USA por lo que tengo un muy buen recuerdo de ella. Por si eso no fuera poco me pagaron el vuelo desde Madrid, 5 noches de hotel, pude ver muchas charlas muy interesantes y conocer mucha gente nueva. Finalmente también tuve tiempo para ver un poco de la ciudad: La Casa Blanca, el monumento a Washington, el World War II memorial, el Air and Space Museum,...".

Tampoco es que te vayamos a decir que tienes que pagar vuelos transoceánicos y hotelazos pero si que hay que tener en cuenta que los temas de transporte y alojamiento siempre quedan en el recuerdo y ayudan a conformar la opinión sobre un evento.

Un starter pack que haga ilusión

Después de pagar, desplazarte al lugar y seguramente tragarte una buena cola para acreditarte que menos que un starter pack (o un kit de bievenida si estás en contra de los anglicismos innecesarios) que mole, siempre dentro de las posibilidades del evento: una bolsa chula, una camiseta que te quede como un guante, botellitas de agua, barritas energéticas, bolis, un pen drive, algún cachivache curioso o representativo, un plano, el horario... y sí, claro, también se puede meter los panfletos publicitarios de los patrocinadores.

Y ya que hemos hablado del proceso de acreditación, no estaría mal una acreditación cómoda, con información útil (usuario de Twitter, empresa...), identificación por colores... vamos, que no estés deseando quitártela y no te des cuenta de que la sigues llevando puesta hasta que llegues a casa o al hotel.

Ayuda (antes) al speaker

Aunque hay gente que son auténticos profesionales de las charlas y unos animales escénicos, convertir un paper en una charla no siempre es una tarea sencilla y cierta guía y asesoramiento en la preparación del "show" nunca viene mal: tiempo del que dispone, materiales a su disposición, audiencia estimada, trucos que nunca fallan, incluso dresscode (no lleves amarillo que se ve raro en el streaming...) o temas a evitar (política, religión, tortilla de patatas con cebolla sí o no...)

Ayuda (durante) al speaker

Events 03 Cáspita, esto no tira, tenía que haberme traído el Mac

Puede suceder, sobre todo en eventos muy grandes, que el speaker, el ponente se encuentre durante la charla o ponencia en una situación de indefensión, sólo ante los elementos. No se puede permitir, siempre tiene que haber alguien de la organización para ocuparse de la "intendencia": encargarse del micrófono o el proyector, proporcionar agua, marcar los tiempos de la charla, organizar el turno de preguntas...

Ignacio Sánchez (@isholgueras), desarrollador Drupal, lleva años asistiendo tanto de público como de ponente a eventos y lo tiene claro y ante la pregunta de las características y facilidades que deben tener los eventos para con los ponentes (y no siempre tienen en España): "Sé que mucha gente dirá que gastos pagados para los ponentes, pero yo con la entrada me conformo, y a veces hasta ni eso. Me conformo con que la organización indique a los asistentes unas normas básicas de asistencia a las sesiones, una calidad decente en el sonido y proyección y un sistema de grabación de charlas. No faltan en todos los eventos, pero en alguno sí que falla alguna de las tres.".

Eventos nacionales como el futuro Commit Conf ya empiezan a incluir códigos de conducta, imprescindibles para que reine la concordia y el evento no termine convertido en una selva. Que no somos animales, por favor, señores (y señoras).

No me hagas correr

Esto es algo referido sólo a los eventos de cierto tamaño, a aquellos que tienen más de un track simultáneo: un poco de tiempo entre las charlas para poder ir de uno a otro con tranquilidad y que el recinto no se convierta en la San Silvestre vallecana de gente corriendo por todos lados. Diez, quince minutos de margen, aunque sea a costa de reducir un poco la duración de las charlas... y ser estricto con el cumplimiento de estas duraciones.

Actividades paralelas (de trabajo)

A colación de las mejores conferencias a las que ha asistido, Ignacio Sánchez nos cuenta: "Diría dos: Drupalcamp Spain 2013 y DrupalCon Dublin 2016. La primera fue donde di mi primera charla “profesional” y donde encontré realmente la calidad humana de la comunidad de Drupal, a la que he acabado enganchado, incluso he acabado siendo parte de la junta de la Asociación Española de Drupal. La segunda fue donde impartí mi primera charla en inglés a público internacional. Público muy crítico, profesional, preguntas muy rigurosas. Disfruté muchísimo de la charla.".

¿Qué tenían en común? "En los dos eventos hay una característica común, y son las salas de sprint. La organización pone a disposición una sala con enchufes y conexión wifi para contribuir (o lo que quieras). En Cáceres (2013) descubrí en esta sala cómo funcionaba Varnish, y en Dublín, junto con uno de los mejores contribuidores, ayudé a mejorar unas funcionalidades del módulo que estaba creando. Esas salas te dan la posibilidad de hablar con los mejores profesionales de ese área en cuestión. A esto me refiero con conferencia de pasillos. Sé que si no me hubiese sentado con esa persona en 2013 no pertenecería a la AED, ni trabajaría donde trabajo, ni hubiese dado más charlas.".

Es obvio, las actividades paralelas "de curro" a las charlas son siempre un plus de los que da lustre y convierten en referencia a un evento: talleres, katas, demostraciones, stands...

Actividades paralelas (de ocio)

El networking. Los eventos de desarrollo sin el networking no serían nada, eso es así. La anteriormente mencionada cena de speakers, una fiesta fin de evento o alguna actividad para conocer mejor la ciudad ya sea cultural o gastronómicamente (en el vallisoletano LechazoConf agasajan a los asistentes con el típico lechazo (el propio nombre de la conferencia es un spoiler en este caso), por ejemplo).

Es más, puedes apelar al nerd que todo desarrollador tiene dentro y apostar por actividades algo más frikis como en la próxima TarugoConf, donde verán la mítica 'Juegos de Guerra' en pantalla grande en un cine de la Gran Vía madrileña.

Buen uso de las redes sociales

Las redes sociales, en especial Twitter pero también Instagram o Facebook (¿alguien sabe si Google+ sigue vivo?), son una herramienta imprescindible para cualquier evento y los de desarrollo no son una excepción. Antes, durante y después.

Antes: anunciando todas las novedades, formando comunidad, generando hype. Durante: sirviendo casi de "atención al cliente", buscando la tan ansiada viralidad (que los que no están hablen, que se mueran de las ganas). Después: agradeciendo a los presentes, ofreciendo los highlights, no dejando que muera en la memoria (y el corazón) de los presentes.

No lo dejes en manos de novatos, se trata de artefactos primordiales para conseguir una buena imagen de marca de tu evento. Si tienes que escatimar en algo, que no sea en esto. No en 2018.

Cuida la comida y los coffee breaks

Tampoco es que haya que contratar a Ferran Adriá o Jamie Oliver para el catering pero la pausa para la comida es un detalle a cuidar con mimo: calidad y variedad (recuerda lo hablado en el primer epígrafe: ten en cuenta alergias, intolerancias y objeciones de los asistentes).

Aquí tiro de experiencia propia: nunca se me olvidarán los pantagruélicos buffets de la BlackBerry Jam Europe y de los stands con refrescos y chocolatinas (y fruta) que había por todos lados. Han pasado cinco años y sigue en mi recuerdo, ¿será que a un geek se le conquista por el estómago?

También hay que cuidar los coffee breaks, que son indispensables para poder sobrellevar las maratonianas jornadas "eventeras" (perdón por el palabro) a base de cafeína... y buena charla.

Apuesta por la igualdad y la representación

Events 04

Muchos eventos, pido perdón de antemano por la expresión que voy a utilizar, parecen "campos de nabos". Hay que acabar con esto. Las mujeres son minoría en el mundo del desarrollo, de acuerdo, pero no son tan invisibles como parece en muchos eventos. Se retraen de asistir a estos saraos más que los hombres y sin esa parte (sea más grande o más pequeña) del espectro no estamos de verdad representados los developers en nuestro conjunto.

¿Cómo luchamos contra esto? Habrá distintas teorías, claro, pero en el fondo es de cajón: mujeres en la organización que consigan atraer a mujeres encima del estrado que, a su vez, conseguirán atraer a más asistentes femeninas. Hay que conseguir que los eventos de desarrollo sean tan inclusivos y representativos como lo puedan ser los de medicina o cualquier otra disciplina.

Y, ojo, no por lo de moda que pueda estar o dejar de estar el movimiento feminista en los tiempos que corren si no porque es de justicia. Ganamos todos y todas.

Coda: estado del arte

Terminamos hablando con Ivan e Ignacio sobre el estado del arte de los eventos y conferencias de desarrollo en España. Para Iván: "No me muevo demasiado por los eventos en España pero desde mi experiencia creo que están bastante bien y, en general se trata muy bien a los ponentes. Hay que tener en cuenta la inmensa mayoría de ellos están organizados por gente que no se dedica a ello de manera profesional sino que lo hacen en su tiempo porque les gusta y quieren aportar algo a la comunidad. Además, en ocasiones es complicado encontrar suficientes sponsors para cubrir los gastos mínimos por lo que muchos eventos no pueden ayudar mucho a los ponentes. Aún así, cuando hay alguna conferencia de desarrollo en España intento seguirla por twitter y las sensaciones que tengo "desde fuera" es que la gente termina muy contenta.",

Intentamos tirarles de la lengua y que nos hablen de aquellos eventos nacionales que les han defraudado, que alguna habrá. Ignacio nos cuenta que: "Ya sólo por el esfuerzo que cuesta organizar un evento, que lo sé, no voy a decir que sea malo o me haya arrepentido, sobre todo si el que lo organiza lo hace por amor al arte. Si acaso, diría el Codemotion de 2014 o 2015. Abraham, Nacho y compañeros tienen un mérito increíble al conseguir lo que consiguen organizando el Codemotion año tras año con tanta afluencia y les deseo lo mejor con la commit-conf. Sí diría que este fue del evento con el que me fui con un sabor de boca agridulce. Hubo asistentes, sesiones y demos brutales, pero bastantes asistentes hablaban en las charlas, entraban y salían de las salas a cualquier hora, incluso haciendo ruido, hablaban de lo mal organizado que estaba, de que si la comida era una basura (oído en la cafetería). Por otra parte hubo alguna charla que eran ‘poco rigurosas’ o ‘poco contrastadas’ con el tema del que trataban.".

También te recomendamos

La evolución de las interfaces de comunicación hombre-máquina: escribir, tocar, hablar… pensar

Go para Pythonistas: Francesc Campoy participará en varios de los GDGs de toda España explicando más sobre GO

Women Techmakers 2016 y el papel de las mujeres en la tecnología

-
La noticia La receta para organizar un buen evento de desarrollo y los errores que lo harían fracasar fue publicada originalmente en Genbeta Dev por Fernando Siles .

GAMBADAS: imágenes maliciosas subidas a Docker Hub minaban criptomonedas en tu servidor

22/06/2018
Artículo original

Imagen ornamental

Las imágenes de Docker son paquetes que incluyen aplicaciones y bibliotecas pre-configuradas para ejecutarse de manera aislada encima del sistema operativo actual (ver ¿Qué diferencia hay entre Docker (Contenedores) y Máquinas virtuales?). Docker mantiene un repositorio público de imágenes, Docker Hub, con miles de imágenes de contenedores listas para ser utilizadas, ahorrándonos mucho trabajo a la hora de definir las nuestras propias. Cualquiera puede subir sus propias imágenes a Docker Hub, lo cual hace que sea tan rico y útil, pero al mismo tiempo se debe ser cauteloso a la hora de usar algo que alguien haya subido. Pasa un poco como con npm, el repositorio de paquetes de Node.js: es muy útil pero hay que tener cuidado.

Durante los pasados meses de julio y agosto de 2017 alguien (o más de una persona) con el identificador docker123321 subió 3 imágenes a Docker Hub con algunas utilidades interesantes. Y mucha gente las descargó más de 5 millones de veces. El problema es que, junto a las utilidades, estas imágenes contenían también algunas puertas traseras y software para minado de criptomonedas. Cuando las ejecutabas en tu sistema o en uno de tus servidores, se ponían en marcha aprovechándose de tu infraestructura para generar dinero para estos malhechores.

Por supuesto, no deberías descargarte imágenes subidas por organizaciones o personas que no conozcas. Peor lo cierto es mucha gente lo hace y la prueba es que estas se descargaron varios millones de veces. El verdadero problema se dio porque, a pesar de que alguien avisó a Docker ya en Agosto a través de una incidencia en Github, la empresa tardó más de 8 meses en hacer caso y retirar las imágenes comprometidas. Y esto sí que es grave. Tampoco eliminaron la cuenta maliciosa por lo que llegaron a subir con ella otras 14 imágenes contaminadas más, que también fueron denunciadas (en esta ocasión por empresas de seguridad, no por usuarios individuales), sin que Docker hiciera nada al respecto.

Un análisis del contenido de las imágenes comprometidas, reveló que se usaba la dirección de una cartera para recibir las criptomonedas que se habían minado. Gracias a ello se ha averiguado que fueron capaces de mirar 545 Moneros, lo que equivale a unos 90.000 dólares nada menos.

La empresa de seguridad Kromtech tiene un artículo muy detallado sobre todo el incidente. Además incluye una lista de las 17 imágenes infectadas para que lo revises, no vaya a ser que lo estés usando. Seguramente quedan todavía equipos y servidores con ellas en funcionamiento sin que el responsable lo sepa si no ha leído sobre este problema.

Lo que debemos tener claro como desarrolladores y usuarios de Docker es que si descargamos una imagen desde Docker Hub es como si descargásemos un ejecutable a nuestro equipo: no podemos fiarnos salvo que se trate de alguno obtenido de una fuente conocida y fiable o si lo examinamos con detenimiento para ver qué hace.

Página Siguiente