Reflexiones sobre cómo desarrollar software (y no morir en el intento)

Buscando un cielo más azul

Hay algo que está muy extendido entre los desarrolladores de software: la tendencia natural que tenemos a pensar siempre que existe un lugar de trabajo mejor que donde estamos. Sólo tenemos que encontrarlo para ser felices para siempre, al menos, laboralmente.

Lo cual, traducido a nuestras carreras profesionales, tiene su traducción en creer firmemente que la empresa donde trabajamos es una basura, que las condiciones son horribles, que el trabajo se hace mal (y que, en otra parte, existe una empresa donde las cosas se hacen realmente bien, donde nos sentiremos valorados, nos pagarán como nos merecemos y donde las condiciones serán tan buenas que ir a trabajar será mil veces mejor que irnos un mes de vacaciones a las Maldivas).

Normalmente este pensamiento no aparece instantáneamente nada más empezar a trabajar en una empresa. Algunas veces es un hecho concreto lo que lo dispara (una promoción que se nos ha escapado, un aumento de sueldo que no ha llegado, una discusión con nuestro jefe) aunque lo más habitual es que sencillamente se vaya formando en nuestra mente poco a poco, conforme la rutina empieza a impregnar cada aspecto de nuestro trabajo diario y la magia de estar en un sitio nuevo donde descubres algo todos los días se va perdiendo.

A partir de entonces empezamos a disfrutar cada vez menos de nuestro trabajo y, probablemente por ello, nos sentimos menos motivados. Lo que suele resultar, en casi todos los casos, en que nuestros resultados son peores. A veces, mucho peores (he visto a desarrolladores a los que consideraba genios hacer auténticas chapuzas y darme cuenta al final de que el problema estaba en que no se sentían motivados cuando las hicieron). Por supuesto, conforme los resultados son peores, la valoración que otros tienen de nosotros es peor, la promoción que esperábamos no llegará, tendremos cada vez más broncas con nuestro jefe, nuestro sueldo seguirá siendo igual de miserable. Y todo ello hará que cada vez nos sintamos más resentidos con nuestra empresa y más amargados en nuestro trabajo.

Pero no pasa nada, porque sabemos que existe ese otro sitio donde las cosas nos irían mucho mejor instantáneamente por la sencilla razón de que es un lugar mejor. Y entonces empezamos a buscar otro trabajo… de nuevo. Porque no es la primera vez que andamos buscando ese sitio. Es la misma búsqueda que hicimos la última vez que nos pusimos a buscar un nuevo trabajo (y que a lo mejor pensamos que había terminado cuando encontramos el trabajo donde estamos ahora). Si eres sólo un poco más listo que la mayoría de la gente ya te habrás dado cuenta de que tampoco va a ser la última vez que lo hagas.

Y así nos pasamos muchos de nosotros nuestra vida profesional, saltando de una empresa a otra regateando beneficios sin ser feliz en ninguna parte, hasta que llega el momento en el que empezamos a valorar más otras cosas como la estabilidad -por convencimiento o por pura necesidad. Y dejamos de buscar, no porque pensemos que hemos llegado al lugar correcto, sino sencillamente porque sabemos que ya no podemos seguir más adelante. Y terminamos en un lugar que no nos gusta, donde no disfrutamos ni un solo segundo que pasamos en él y de donde sabemos que ya no vamos a salir fácilmente.

Vale, estoy siendo un poco dramático pero quién puede decir que no reconoce un patrón parecido en su carrera o la carrera profesional de otros. Y, ¿realmente tiene sentido que sea así?

Rompiendo el círculo

Lo primero que tenemos que hacer para romper esta espiral de insatisfacción es darnos cuenta de una verdad: no existe la empresa perfecta. Sólo tienes que pensar en que la gente se va de Google porque está absolutamente quemada. Sí, los mismos que trabajan en esas oficinas tan chulas con salas de reuniones super guapas, restaurante propio donde te dan de comer chefs y te prepara el café que quieras un barista profesional, además de cobrar una pasta gansa, se terminan marchando porque están insatisfechos, aburridos, estresados, quemados.

¿Crees que serías más feliz si en vez de trabajar en tu empresa actual lo hicieras en Google? Pregúntales a los que se marcharon de Google (posiblemente te dirán que se fueron para trabajar en una empresa mejor). ¿Crees que vas a ser más feliz por trabajar en otra empresa? Puede que no porque las condiciones son sólo una parte pequeña de la ecuación de la satisfacción laboral. Cuanto antes te des cuenta de eso más tiempo tendrás para realmente planificar cómo quieres que evolucione tu carrera.

Conócete a ti mismo


Empieza por preguntarte qué es lo que realmente quieres conseguir de tu carrera profesional. ¿Te gustaría por encima de todo trabajar para una gran empresa? ¿Valoras más poder compartir tu lugar de trabajo con gente super inteligente? ¿Te gustaría dedicarte a gestionar en lugar de a hacer cosas por ti mismo? ¿Quieres trabajar en tu propia idea?

Mucha gente con la que he hablado no sabe en qué quiere que se convierta su carrera profesional. Muchos tienen dudas, les cuesta decidir, algunos ni siquiera se lo han preguntado. Y eso es lo que realmente nos hace infelices en el trabajo la mayoría de las veces.

Por eso, da igual lo que quiera que escojas, debes escoger algo y tener presente que no se puede tener todo. Si nos hicieran por separado cualquiera de las siguientes preguntas:

  • ¿Te gustaría que tus compañeros de trabajo fueran personas super inteligentes de las que pudieras aprender todos los días?
  • ¿Te gustaría trabajar en algo con un impacto global que potencialmente afecte a millones de usuarios?
  • ¿Te gusta ver que tu trabajo diario tiene un impacto directo en los resultados de tu compañía?
  • ¿Quieres ascender la escalera corporativa y llegar a ocupar el cargo con mayor responsabilidad posible?
  • ¿Quieres desarrollar tu propia idea (revolucionaria) y cambiar el mundo?

Posiblemente responderíamos que sí a un buen número de ellas. Pero, puestas todas juntas, es imposible decir que sí a todas a la vez (al menos, de una forma realista). Sencillamente son cosas diferentes, en muchos casos contradictorias, que requieren seguir caminos divergentes para alcanzarlas. Así que tenemos que decir que sí a unas pocas cosas y empezar a decir que no a muchas otras.

Y empieza a moverte


Y cuando ya sepas adónde quieres ir entonces es el momento de empezar a moverte, de buscar siempre mejores oportunidades para seguir desarrollándote de la forma que has decidido hacerlo. Puede ser que un trabajo te dure unos pocos meses hasta que cambies a otro o bien que continues en la misma empresa durante años. En cualquier caso, cada paso que des lo harás convencido de que te acerca a tu objetivo. Y si tu objetivo cambia, no pasa nada, ajusta tu plan y empieza a ponerlo en marcha.

Los planes son inútiles, pero planificar lo es todo.
Dwight D. Eisenhower

No ocurre nada si cuando llegas a una entrevista de trabajo presentas un curriculum que demuestra que durante 5 años quisiste ser experto en inteligencia artificial y durante los dos años siguientes cambiaste porque querías ser scrum master o agile coach. No se puede decir lo mismo si cambiaste diez veces de empresa para conseguir dos días más de vacaciones o una silla más cómoda.

Para quien navega sin rumbo ningún viento es favorable.
Séneca
Continue Reading

Tienes que crear un framework

Siempre recomiendo utilizar cuantos más componentes ya disponibles mejor. Nunca construyas tu propio framework para:
  • Persistir datos
  • Mostrar información basándote en plantillas
  • Construir la navegación de un sitio web
  • Comunicar componentes remotos
Y tantos otros ejemplos que me vienen a la cabeza.

Sin embargo, hay un framework que siempre deberías construir en cualquier proyecto en el que vayas a participar. Es algo que deberías tener claro antes incluso de empezar. Cuanto antes lo construyas será mucho mejor y no hacerlo pone seriamente en riesgo tu proyecto.

Se trata de tu propio framework para automatizar tus pruebas. No es algo que puedas tomar de alguna otra parte, ni que puedas copiar y pegar de un proyecto pasado (aunque, evidentemente, haber construido uno en el pasado te ayudará a hacerlo ahora). Y no puedes por una sencilla razón: todavía no ha sido inventado. Y el único que puede inventarlo eres tú porque sólo tendrá sentido para ti.

Pero espera un momento entonces. Si sólo va a tener sentido para mí, ¿sigue siendo un framework? Sí y no, lo veremos más adelante. Ahora mejor empecemos con un ejemplo.

Registrando usuarios en una aplicación web

Imaginemos que hemos construido nuestra nueva aplicación web y, como sabemos que vamos a tener que iterar muchas veces sobre nuestro producto para dejarlo a punto, tenemos claro que necesitamos tantas pruebas automáticas como sea posible. De lo contrario, cada vez que cambiemos algo estaremos corriendo un serio riesgo de que el sistema falle (básicamente nos hemos leído algún libro del tipo The Lean Startup, algo sobre desarrollo ágil o, por lo menos, algún artículo en internet publicado en los últimos 10 años. Así que decidimos empezar creando una prueba para una parte fundamental de nuestra aplicación: el login de usuarios.

Como estamos empezando y todavía no sabemos muy bien cómo será el producto final, decidimos hacer una versión muy sencilla (ver ejemplo).

http://cdpn.io/yqihp

Lo sé, es tan fea que casi hace que duelan los ojos. Pero tampoco Google tenía muy buena pinta cuando empezó y fíjate dónde está ahora. Así que no te preocupes, posiblemente cuando vayas por la iteración número 15 podrás pensar en dejarla bonita. De momento, nos sirve. El código HTML de nuestra página sería:

Automatizando pruebas

Podemos crear una prueba automática que haga login en nuestra aplicación usando, por ejemplo, Selenium:

A partir de aquí, podríamos usar este mismo código en cualquier test que requiera el login del usuario y todo estaría bien... hasta que llegásemos al punto en el que queramos cambiar nuestra pantalla de login (iba a ser en nuestra iteración número 15, ¿no?) y tengamos que modificar cientos o, si hemos sido realmente buenos, miles de tests que se basaban en el código anterior.

Mejorando nuestras pruebas automáticas

El problema anterior básicamente tiene su origen en que hemos hecho nuestro código de prueba dependiente de algo que cambia de forma frecuente (especialmente en las etapas iniciales de un proyecto): la interfaz de usuario. Así que algo tan trivial como cambiar el identificador a los elementos HTML de la página de login puede hacer que cientos de tests empiecen a fallar.

La solución, como otras tantas veces, consiste en añadir un nivel de indirección que se encargue de manejar los detalles acerca de cómo está implementada la interfaz de usuario y, al mismo tiempo, ofrezca una interfaz consistente y homogénea con la que puedan interactuar todos los tests que construyamos. Así es como empezaremos a construir el framework que nos ayudará a automatizar nuestras pruebas.

En nuestro caso, aquellos tests que necesiten hacer un login de usuario se podrían escribir de la siguiente manera:

Una vez empecemos con la construcción de nuestro propio framework podemos ir más allá de la simple abstracción de la UI. Por ejemplo, podemos hacer cosas como:

  • Ofrecer un acceso rápido a usuarios ya creados para los que además conocemos determinados parámetros interesantes desde el punto de vista de las pruebas.
  • Implementar procesos largos que son repetidos por muchos tests.
En mi actual proyecto, por ejemplo, tenemos una serie de usuarios prototípicos que son utilizados repetidamente por toda nuestra suite de pruebas. Por ejemplo, Thomas Müller: como buen alemán medio que es, tiene contratado un seguro de responsabilidad civil (también tiene el nombre y el apellido más repetido en Alemania). Así que siempre que alguien necesita un usuario que tenga registrado un seguro de responsabilidad civil durante la construcción de una prueba sólo tiene que hacer:

TestUsersRegistry.getThomasMueller();

O si queremos crearlo desde cero:

TestUsersFactory.createThomasMueller();

O cuando es necesario registrar un usuario nuevo, sin contratos, para probar una funcionalidad disponible para usuarios recieén registrados (tenemos unas cuantas de ese tipo) sólo hace falta hacer:

TestUsersUtils.registerNewUser("John", "Doe", "john.doe@example.com");

En este caso, este método está sobrecargado con diferentes opciones para darnos la flexibilidad necesaria que nos permita registrar usuarios con todas las opciones que sean relevantes desde el punto de vista de los casos de prueba que estamos automatizando. Lo que nos lleva al siguiente punto importante cuando creas tu propio framework de automatización de pruebas: complejidad.

Características de un framework de automatización de pruebas

Por su propia naturaleza, el códido que escribimos cuando automatizamos pruebas tiene (o sería deseable que tuviera) las siguientes características:

  • Es bastante repetitivo. Muchas acciones se repiten a lo largo de múltiples tests (especialmente cuando implementamos diferentes escenarios para una misma funcionalidad)
  • Es lineal (o debería serlo; si no lo es entonces deberías revisar cómo estás implementando tus pruebas)
  • Es fácil de escribir (porque queremos que escribir tests sea algo sencillo que todo el mundo haga de forma regular)
  • Es fácil de leer. Esto es más importante aún porque:
    • Queremos podemos entender más fácilmente qué esperamos de una funcionalidad con echarle un ojo al código que la prueba
    • Cuando un test falla y queremos arreglarlo lo último que queremos es tener que perder un montón de tiempo averiguando qué es lo que el test estaba intentando hacer
  • Se aproxima lo máximo posible al lenguaje natural y utiliza cuantos más términos propios del dominio de nuestro sistema mejor. Esto es básicamente un corolario del punto anterior, pero quería resaltarlo para dejarlo bien claro.
Por todo esto, queda claro dónde tenemos que poner la complejidad: en nuestro framework de automatización de pruebas. Porque las cosas nunca son sencillas y, si queremos tener un código de prueba que sea super sencillo y fácil de entender, tiene que haber un sitio donde se encuentren todas las complejidades inherentes al código que estamos intentando crear.

Por eso, no es ningún problema que nuetro código de automatización de pruebas sea complicado. Tiene que serlo para que nuestras pruebas sean sencillas. La parte positiva es que será un código que escribirás una vez y lo reutilizarás miles de veces (el beneficio es claro).

Recuerda: no se trata de hacer las cosas más complicadas de lo necesario por el gusto de hacerlas complicadas. Pero no hay ningún problema si tu framework de automatización de pruebas empieza a serlo porque tu objetivo es hacer sencillas las miles de pruebas que quieres automatizar.

Esto nos lleva a las características deseables para nuestro framework de automatización de pruebas:

  • Permite escribir muchos tests con poco esfuerzo.
  • Ofrece interfaces claras que pueden ser utilzadas fácilmente por quienes tengan que escribir las pruebas automáticas.
  • Se acerca lo máximo posible al lengaje natural (lo que permite que las pruebas automáticas puedan ser escritas por más personas distintas a los propios desarrolladores del sistema).
  • Maneja todas las complejidades que puedan aparecer cuando tenemos que escribir el código de una prueba.
  • Es lo suficientemente flexible para facilitar la creación de pruebas para todos los escenarios que necesitemos (no sólo para los más sencillos).
  • Incorpora atajos que hagan la vida más cómoda a quienes escriben los tests. Por ejemplo, si hemos parametrizado el método que crea usuarios para que se puedan crear todos los tipos de usuarios con los que nos interesa probar, es perfectamente válida crear métodos adicionales que permitan crear determinados tipos de usuarios de forma fácil (sin tener que pasar ningún parámetro, por ejemplo) aunque eso pudiera considerarse redundante.
Al principio, crear tu framework de automatización de pruebas puede parecer una tarea complicada pero te aseguro que el beneficio es demasiado grande para ignorarla. Además, es parecido a ir al gimnasio: puede costar mucho al principio pero, cuando te acostumbras, no cuesta tanto y, finalmente, verás que lo haces con naturalidad.

Es también una excelente inversión porque, una vez hecho el esfuerzo inicial y puesto en marcha el framework, verás cómo cada vez inviertes menos tiempo en desarrollarlo y perfeccionarlo y, en cambio, pasas más tiempo usándolo y creando más y más pruebas automáticas lo que te ayuda a mejorar la calidad del sistema que estás construyendo (y, de paso, te hacer sentire mucho mejor como desarrollador y ser humano).

Comentarios finales

Aquellos que están familiarizados con temas como automatización de pruebas, TDD,  etc. puede que se hayan sentido un poco confundidos por haber dejado en el aire algunas cuestiones importantes. Como, ¿para qué tipos de pruebas es importante crear nuestro framework? ¿Sólo para pruebas de aceptación? ¿Sólo cuando estamos probando a través de la UI, como en el ejemplo que hemos visto con Selenium?

Lo cierto es que he sido deliberadamente impreciso en este aspecto. En cualquier caso, debemos crear nuestro framework para cualquier tipo de prueba que automaticemos en nuestro proyecto, no importa que sean unitarias o integradas, de aceptación, de sistema... incluso para pruebas de humo.

Siempre que estemos repitiendo el mismo código una y otra vez cada vez que creamos un test y/o que dicho código dependa de cosas que van a cambiar con frecuencia estamos recibiendo una señal de que necesitamos refactorizar nuestro código de pruebas y empezar a crear nuestro propio framework. Es algo que debemos hacer a múltiples niveles y que tenemos que hacer con naturalidad (casi como respirar).

Referencias

Mi libro favorito sobre este tema, y con el que aprendí a escribir pruebas automáticas de una forma sostenible en el tiempo (y no el lío de código espagueti en el que solía convertir mis tests), es xUnit Test Patterns de G. Meszaros. Casi diría que es una lectura obligatoria (de la que ya hablé anteriormente aquí. Si no lo has leído, puedes empezar echándole un ojo al sitio web del libro donde se puede también información muy útil.
Continue Reading

10 lecturas recomendadas si eres ingeniero de software

Hace un tiempo un amigo me pidió que le recomendara algunas lecturas apropiadas ahora que se iba a dedicar oficialmente a ejercer como arquitecto de software (¿todavía alguien quiere ser arquitecto de software?). Al final, nunca le envié la lista pese a que me pareció una pregunta de lo más interesante.
Ahí van mis diez lecturas recomendadas para cualquier ingeniero de software, en ningún orden en particular:


No es una lista exhaustiva ni son todos los libros que te recomendaría. Podría haber incluido en la lista xUnit Test Patterns: Refactoring Test Code o The Pragmatic Programmer: From Journeyman to Master. O tantos y tantos grandes libros que hay. Pero, sinceramente, creo que si llegas a leerte todos estos libros y te dedicas de forma habitual al desarrollo de software siguiendo lo que en ellos se dice, habrás alcanzado el punto en que te convendría mucho más leer cosas no relacionadas tan directamente con el desarrollo del software. Anque ése es otro tema del que hablaremos en el futuro.

Notas finales

Para que no sirva de excusa el tema del idioma, os dejo los siguientes con sus enlaces a la edición en español:


Continue Reading

Economía básica aplicada a tu carrera profesional: búscate un buen nombre

Supongamos que ves un anuncio de una empresa que busca programadores para incorporar a un proyecto que está a punto de empezar. Por lo que te cuentan, en la empresa se trabaja bien, el proyecto puede ser interesante y (¡emoción!) hasta el salario parece bueno. Te empieza a gustar la idea de trabajar allí y decides intentarlo. ¿Qué es lo peor que podrías poner en tu curriculum antes de enviarlo?

Nombre: Fulanito
Apellido: Picateclas
Programador
Y no, no tiene nada que ver con el nombre ni el apellido (bastante ridículos) y esta entrada no trata sobre cómo cambiarlos para tener mejores oportunidades en la vida (aunque, sin duda, eso también ayuda). En realidad, esto va sobre programadores.

Si alguien me preguntara qué he sido durante el tiempo que llevo trabajando le diría que muchas cosas y le hablaría de roles que he ido teniendo, entre los que nunca estaría programador. Sí, exacto, yo nunca he sido programador. Y tú tampoco (¿cómo? ¿tú sí lo has sido? no, no lo has sido, calla y sigue leyendo). ¿Por qué?

Vamos a empezar con algo fácil de entender. Una empresa puede hacer muchas cosas (normalmente el grado de estupidez de lo que hace crece de forma pareja al tamaño de la empresa) pero, fundamentalmente, todas quieren aumentar los ingresos y reducir costes. Y una consecuencia lógica de esto es que las empresas normalmente recompensan a sus trabajadores en función de cómo son capaces de hacer alguna de estas dos cosas. Sí, las empresas recompensan a los empleados que hacen que sus ingresos aumenten o que consiguen reducir costes. ¿Crees que no? ¿Te parece mal? Monta tu propia empresa y lo entenderás.

Ahora viene la parte que hace que esto tenga algo que ver con tu carrera profesional. La persona que va a leer tu currículum y decide si contratarte o no también está buscando aumentar beneficios o reducir los costes. Después de  lo que hemos visto arriba debería ser algo bastante evidente,  ¿por qué se nos olvida tantas veces? Y a la persona que te paga no le interesan las arquitecturas limpias, utilizar lenguajes de programación de moda, frameworks chulos, el software craftsmanship ni resolver problemas complejos que constituyen un reto para tu mente de ingeniero. Aumentar los ingresos. Reducir los costes. Ése es su interés y lo demás son sólo medios para conseguirlo. Así que no te sorprenda que te a ti te vea como una pieza de la que espera que, encajada dentro de su equipo, haga que algún coste se reduzca o que los ingresos aumenten.

Y tradicionalmente los ingenieros son en sí mismos centros de costes, y bastante caros, por cierto. Y cuando tú mismo eres un centro de costes muy caro para una empresa te estás poniendo una diana para que todas esas mentes maquiavélicas que tienen un MBA o creen que saben dirigir una empresa empiecen a pensar cómo quitarte de en medio. Así es como aparecen ideas tan interesantes como el outsourcing. (Por cierto, nunca he escuchado que ninguna empresa haya externalizado su departamento de ventas. ¿Empiezas a ver la diferencia?)

Si te pones la etiqueta "programador" lo que estás diciendo sobre ti es "soy un peón muy caro que pica teclas". Y creo que esa no es la mejor idea sobre ti y tu trabajo que quieres que alguien se quede, ¿verdad?. Al menos, no lo es si quieres ser valorado (o sea, ganar más dinero). En vez de eso, intenta describir cómo has ayudado a las empresas en las que has estado anteriormente a reducir costes o aumentar los ingreso. Y si no has tenido oportunidad de hacerlo aún, busca una descripción que sugiera que eres capaz de hacerlo en el nuevo puesto que ocupes. 

Analista es mejor descripción que programador (y, piénsalo, seguro que has estado haciendo un montón de análisis hasta ahora aunque nadie te haya llamado así). Desarrollador es mejor que programador (si encima has desarrollado la solución que ha hecho ganar un montón de dinero a esa empresa en la que trabajaste, dilo). Arquitecto es mejor que programador. Creo que lo único peor que programador es progamador Java (además de ser un coste, tienes un ámbito de trabajo limitado).

A lo mejor ha sido algo que no has tenido en cuenta. ¿Te ha ido bastante bien hasta ahora sin prestarle atención? Sencillamente has tenido suerte de que alguien te haya estado viendo como un activo en lugar de como un coste (aunque si ignorabas esto, seguramente tú no hayas puesto mucho de tu parte y podrías haber ganado mucho más dinero si lo hubieras hecho).

Creo que inconscientemente casi todos saben esto. Al fin y al cabo, muchos queríamos promocionar rápidamente a categorías como analista dentro de las consultoras porque, de alguna forma, pensábamos que era mejor. ¿Mejor trabajo? ¿Más prestigio? ¿Quién sabe? Definitivamente, más dinero, sí. Pero ahora ya no debería preocuparte tanto siempre que sepas explicar lo realmente importante.

Grábalo en tu mente: reducir costes y aumentar ingresos. Eso es lo que le interesa a quien te paga así que es mejor que también te interese a ti. Y que seas capaz de reflejar ese interés.
Continue Reading

Siempre habrá un arquitecto


Os presento una situación bastante corriente: hemos conseguido un contrato para el mantenimiento de una aplicación web Java, con almacenamiento en una base de datos Oracle, donde se hace un amplio uso de procedimientos almacenados, y un carga fuerte de JavaScript en la capa de presentación. Se trata de un proyecto largo (más de un año) para un equipo pequeño (digamos que el presupuesto no es muy amplio). Hacemos algunos cálculos, pensamos en los escenarios más probables que nos vamos a encontrar y llegamos a la conclusión de que vamos a dimensionar el equipo con tres presonas.

Tenemos ahora que pensar en quiénes formarán parte del equipo. ¿Sabéis una cosa? Lo más probable es que la inmensa mayoría de los responsables de proyecto o, al menos, los que suelen completar sus proyectos con éxito, decidirán tener en su equipo a:

* Un desarrollador Java
* Un experto en bases de datos Oracle
* Un programador web con amplios conocimientos de JavaScript

Parece una decisión bastante lógica, ¿no? Pues bien, no es más que la inversión de una regla bastante antigua aunque, a veces, desconocida en el desarrollo de software: la ley de Conway.

Cualquier organización que afronte el proceso de diseñar un sistema de información diseñará uno que copie las estructuras de comunicación de la organización.

O, dicho de otra forma:

La arquitectura de un sistema refleja la estructura del equipo que la diseñó.

Junta a un desarrollador Java con un programador JavaScript y un experto Oracle y tendrás una aplicación en tres capas, posiblemente con procedimientos almacenados en la base de datos y una interfaz rica en el navegador. ¿No quieres procedimientos almacenados? Quita al experto en Oracle del equipo. Organiza un equipo de tres desarrolladores para construir un compilador y tendrás un compilador en tres fases. Estos diseños serán utilizados incluso aunque no sean los más apropiados para el sistema que queremos construir.

Las decisiones sobre la arquitectura del sistema se empiezan a tomar incluso antes de que nadie haya dibujado el primer diagrama ni escrito la primera línea de código: las tomamos cuando decidimos el equipo que queremos para nuestro proyecto.

Se toman cuando menos información tenemos sobre el sistema (justo al principio del proyecto) y las toman las personas que menos conocimientos tienen para hacerlo (los jefes de proyecto).

¿Cómo podemos evitar esto?


En primer lugar, intenta empezar el proyecto con el equipo más pequeño posible. Ten en cuenta que cada persona que añadas al equipo del proyecto supone una carga más en la arquitectura del sistema. Y demasiada arquitectura puede ser incluso más perjudicial para el resutado que tener muy poca arquitectura. Formando un equipo pequeño al principio, estarás tomando las mínimas decisiones sobre la arquitectura final del proyecto.

En segundo lugar, busca los perfiles más generalistas a la hora de formar el equipo en vez de meter a personas con una única (o un reducido número de) habilidades. En este caso, es mejor ese desarrollador Java al que no le importa trabajar en la base de datos y que puede defenderse a la hora de hacer interfaces web que un gurú de Oracle o un ninja del JavaScript. Puede que, con el tiempo, llegues a necesitas esos perfiles, pero te aseguro que es mejor retrasar su incorporación hasta que la arquitectura esté completamente clara.

Como podéis ver, todos los proyectos tienen, al menos, un arquitecto. Uno que decide sobre el diseño del sistema de una forma muy peculiar: sin diagramas ni documentos de arquitectura sino asignando personas al equipo que se va a encargar de construirlo.

Mi recomendación para cualquier jefe de proyecto es que evite en la medida de lo posible convertirse en ese arquitecto. Por el bien de su proyecto.

Bibliografía



Continue Reading

Algo rápido para leer en 5*60*1000

Me ha quedado un título un poco friki pero a quienes programan en Java y han tenido que tratar alguna vez con unidades de tiempo les sonará. Y es que muchas veces seguimos haciendo las cosas como siempre sin fijarnos si existen ya alternativas que nos puedan ayudar un poco a que nuestro trabajo sea más sencillo.
Desde hace tiempo está disponible en el juego de librerías estándar de Java, concretamente desde la versión 1.5, la clase TimeUnit. Esta clase representa intervalos de tiempo con distinta granularidad y provee de métodos de conversión entre diferentes unidades.
De esta forma, si queremos crear una constante con el valor de cinco minutos en milisegundos para poder utilizarla en nuestra clase, podríamos hacer:

Además del método convert, podemos usar métodos propios de cada una de las unidades de tiempo para convertirlas a otra unidad con un código más fácilmente legible:

La segunda características más destacable de TimeUnit es poder para la ejecución del hilo actual de una forma sencilla. Así, frente al uso del método sleep de la clase Thread podemos hacer:

Como vemos, TimeUnit no hace nada nuevo que no pudiera hacerse antes. Sencillamente, lo hace de un forma más expresiva que hace que nuestro código transmita mejor su intención (para qué está hecho) al hacerlo más limpio y directo.
Continue Reading

Haciendo pruebas de clases abstractas

El tema que vamos a tratar está relacionado con un tipo especial de testing automático que no suele ser frecuente en nuestro trabajo diario: las pruebas unitarias de clases abstractas.
Normalmente somos consumidores de librerías, utilidades y frameworks que nos dan el trabajo ya hecho para que podamos usar sus clases de forma sencilla y sin tener que preocuparnos de nada (o, mejor dicho, de casi nada).
En cualquier framework que analicemos, veremos que existe multitud de interfaces y clases abstractas. Nosotros nos limitamos en muchos casos a ir incorporando implementaciones y subclases concretas para que nuestras aplicaciones funcionen. En estos casos, las pruebas unitarias que automatizamos prueban estas implementaciones concretas y se enfrentan a problemas típicos como eliminar las dependencias de componentes externos (ya sea mediante mocks, stubs o algún otro tipo de test double).
Pero, ¿qué ocurre cuando somos nosotros los que estamos desarrollando un framework? ¿Cómo podemos probar de forma efectiva nuestro código si éste está plagado de componentes abstractos (ya sean interfaces o clases) y no podemos instanciar las clases más básicas que queremos probar?
Algunos pensarán que difícilmente van a tener que desarrollar un framework alguna vez. Sin embargo, esta opción no están tan alejada de nuestras posibilidades. A continuación os presento una forma sencilla de framework que podemos crear nosotros mismos: el patrón Template Method.

Template Method

Wikipedia:

es un patrón de diseño enmarcado dentro de los llamados patrones de comportamiento, que se caracteriza por la definición, dentro de una operación de una superclase, de los pasos de un algoritmo, de forma que todos o parte de estos pasos son redefinidos en las subclases herederas de la citada superclase[1].
La estructura básica de clases que le da soporte sería:


Esto nos recordar a otro patrón de diseño parecido que también habla de algoritmos: el patrón Strategy. La diferencia fundamental entre Template Method y Strategy es que, en el primero, las subclases modifican una parte del algoritmo mientras que, en el segundo, las implementaciones de la estrategia cambian el algoritmo completo.

Como podemos ver, el patrón Template Method nos da la base para construir un framework con la estructura básica de los pasos que hay que seguir para realizar una determinada acción, dejando que sean las clases concretas las que completen los detalles necesarios. Veamos ahora cómo lo vamos a aplicar en un ejemplo y de qué forma podemos hacer pruebas unitarias a la clase abstracta que define el método plantilla.

Pongamos un poco de orden

Vamos a hacer un ejemplo muy sencillo donde aplicaremos el patrón Template Method. En concreto, se trata de una clase que define el algoritmo para ordenar una lista de cadenas de caracteres.


La clase SortAlgorithm define un algoritmo genérico para la ordenación de una lista de cadenas de caracteres básandose en la implementación concreta del método compare, que se encarga de hacer la comparación básica entre dos cadenas de caracteres cualesquiera. Su implementación en Java sería algo parecido a:

    public List<String> sort(List<String> unsortedList) {
        List<String> sortedList = new ArrayList<String>();
        Iterator<String> it = unsortedList.iterator();
        while(it.hasNext()){
            String element = it.next();
            boolean inserted = false;
            for(int i=0;!inserted && i<sortedList.size();i++){
                if(compare(sortedList.get(i), element)>0){
                    sortedList.add(i, element);
                    inserted = true;
                }
            }
            if(!inserted){
                sortedList.add(element);
            }
        }
        return sortedList;
    }

    protected abstract int compare(String a, String b);

Por supuesto, no vamos a dejar nuestro método de ordenación de listas sin probar (eso no lo hacemos nunca, ¿verdad?). Así que, ¡vamos a crear una buena prueba unitaria!

La opción de testing más sencilla que podría llegar a funcionar

Como no podemos instanciar la clase SortAlgorithm por ser abstracta, lo primero que podríamos hacer para poder probarla unitariamente es proporcionar una subclase de prueba que nos dé una implementación por defecto para el método compare. De esta forma, podríamos hacer:

public class SortAlgormithmConcreteTestSubclass extends SortAlgorithm {

    @Override
    protected int compare(String a, String b) {
        return -1;
    }

}

Con esta implementación del método compare, la cadena a siempre se considerará menor que la cadena b por lo que, con nuestro algoritmo de ordenación genérico, los elementos se irán incorporando a la lista ya ordenada siempre al final. Así tendríamos que el algoritmo de ordenación nos devuelve siempre una lista igual que la primera. El test unitario para el método sort sería el siguiente:

public void testAlgorithm() {
    List<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");
    aList.add("c");
    aList.add("d");
    aList.add("e");
    SortAlgorithm sortAlg = new SortAlgormithmConcreteTestSubclass();
    List<String> anotherList = sortAlg.sort(aList);
    assertEquals(aList, anotherList);
}

Se trata de una buena implementación de una subclase específica de prueba ya que:

  • La implementación de compare es trivial (una línea de código) y nos devuelve un resultado predecible (útil para la prueba que vamos a programar).
  • Hace que probar el método sort sea sencillo pues sólo tenemos que comprobar que lo que nos devuelve es una lista igual que la original.
  • No sirve realmente para ordenar listas de cadenas de caracteres. Es decir, no estamos dando una implementación concreta del método plantilla que sirva para nada. Su propósito es única y exclusivamente permitirnos hacer pruebas unitarias de nuestro algoritmo con lo que no estaremos nunca tentados de utilizar esta implementación en nuestro código de producción.
Sin embargo también tiene algunos inconvenientes:

  • El test es más difícil de entender ya que, quien lo lee, tiene que buscar en la implementación específica de prueba, SortAlgormithmConcreteTestSubclass, para saber qué hace.
  • Hemos tenido que crear una nueva clase únicamente para poder hacer un test. En nuestro caso, ha sido una clase sencilla pero en otros podría ser mucho más complicada. Como, al final, todo el código que tenemos no es más que inventario, debemos intentar reducirlo al mínimo posible. Esto es más verdad aún para el código de test.

Haciéndolo fácil con Mockito

Este proceso lo podemos simplificar mucho utilizando alguna librería que nos ayude en la tarea de crear un comportamiento predeterminado para el método abstracto compare, de forma que así podamos probar convenientemente nuestro algoritmo de ordenación genérico. Y aquí es donde entra en juego Mockito.

En este caso, vamos a utilizar Mockito para dar comportamiento únicamente al método abstracto compare, mientras que seguimos utilizando toda la lógica ya implementada en el método de ordenación, sort. Nuestro test unitario quedaría ahora así:

    public void testAlgorithm() {
        List<String> aList = new ArrayList<String>();
        aList.add("a");
        aList.add("b");
        aList.add("c");
        aList.add("d");
        aList.add("e");
        SortAlgorithm sortAlg = mock(SortAlgorithm.class, CALLS_REAL_METHODS);
        doReturn(-1).when(sortAlg).compare(anyString(), anyString());
        List<String> anotherList = sortAlg.sort(aList);
        System.out.println(anotherList);
        assertEquals(aList, anotherList);
    }

El detalle fundamental en el que tenemos que prestar atención es el haberle pasado al método mock un parámetro extra: CALLS_REAL_METHODS. Gracias a este parámetro estamos construyendo una implementación de la clase abstracta SortAlgorithm que utiliza los métodos ya definidos en ésta y únicamente proporciona un comportamiento específico cuando se hacen llamadas al método compare con cualquier par de cadenas de caracteres, devolviendo siempre -1 (lo que coincide con la implementación que ya habíamos hecho en SortAlgorithmConcreteTestSubclass, que ya no es necesaria y puede ser deshechada).

De esta forma, podemos hacer pruebas unitarias a nuestras clases abstractas aunque las implementaciones que existan estén en proyectos distintos o, sencillamente, no dispongamos de ellas, y lo podemos hacer con una implementación limpia que nos genera el mínimo código necesario para llevarlas a cabo gracias a Mockito.

Código fuente


Puedes descargar el código fuente completo comprimido en ZIP aquí. O, si lo prefieres, lo puedes descargar del repositorio en Github.

Nota: Al proyecto final se le han incorporado un par de implementaciones que completan el uso del patrón Template Method.
Continue Reading