Cuando nos enfrentamos a un nuevo tipo de proyecto en el que vamos a usar tecnologías que no dominamos del todo o es la primera vez que las usamos en un proyecto profesional, suele ser un error que tu primer proyecto con esas tecnologías sea precisamente el que vas a entregar a un cliente; este, sin saberlo, va a ser un conejillo de indias del proveedor que decide con acierto o no los mejores frameworks y librerías para ese tipo de proyecto, si es que esta evaluación se ha llegado a hacer.

Lo peor se presenta cuando quien decide usar un stack de tecnologías en concreto lo hace no porque se ajuste a ese proyecto en particular, objetivamente, dada su naturaleza, sino por sencillo capricho y porque se presenta así una oportunidad maravillosa de aprender en el trabajo sobre aquello que fuera de él no se tiene tiempo... Esto lo he visto varias veces y es una tentación demasiado grande como para no hablar de ello; igualmente, quien cae en esa tentación se podrá considerar desarrollador de software, pero desde luego no profesional. En ocasiones, los desarrolladores de software parece que juegan en su trabajo en lugar de buscar realizarlo con la mayor profesionalidad posible.

Lo importante es tener la capacidad de seleccionar correctamente las mejores tecnologías a emplear en un proyecto dado su naturaleza y sus requerimientos. En esto tenemos que ser totalmente asépticos y no dejarnos llevar por modas o gustos particulares.

No hace mucho, he visto cómo un cliente ha tenido que tirar a la basura (literalmente) un front-end de comunicaciones de dispositivos realizado inicialmente con un CMS bastante popular y profesional (que, por cierto, a mí me gusta mucho y con el que está hecha esta web...), pero para nada adecuado para ese proyecto. Intuyo que tras esa decisión hubo o bien un gusto personal del desarrollor que tomó esa elección (sin evaluar riesgos futuros o sin plantearse la idedoneidad para ese proyecto) o bien forzar a usar ese CMS por la indolencia de no querer molestarse en aprender tecnologías más adecuadas. Para esta selección se requiere bastante experiencia y haber tocado un poco de esto y un poco de lo otro para conocer lo suficiente para tomar una elección, sin entrar en el diletantismo tecnológico que consistuye uno de los capítulos de El Libro Negro del Programador.

Si estamos en la situación en la que se ha elegido un stack de librerías / tecnologías que no conocemos en profundidad y que debemos usar en un nuevo proyecto, podemos caer en el error de comenzar este sin más. Si lo hacemos así, todos los errores que podamos comenter por ser neófitos en esas tecnologías los cometeremos en ¡el proyecto de un cliente!.

Es fácil no conocer las buenas prácticas asociadas a ciertas librerías o frameworks cuando no los hemos usado antes suficientemente, no conocer los tips & tricks y estar lejos de saber resolver los escenarios más elementales con esas nuevas tecnologías. Eso creará una deuda técnica en el proyecto que nos pasará factura con total seguridad.

¿Y entonces?

La solución está en que antes de comenzar a escribir código de producción, dediquemos un tiempo a realizar un prototipo que resuelva o nos permita aprender los elementos más importantes y críticos del proyecto. Una vez resueltos estos elementos, los sabremos estructurar e integrar mejor en el proyecto final que se entregará al cliente.

Al final vamos aprendiendo con muchísima prueba y error, por esta razón no podemos cometer errores de novato en un proyecto final para un cliente: antes debemos conocer suficientemente bien las tecnologías que se han decidido usar y, para ello, nada mejor que implementar un sencillo prototipo que nos aclare las dudas fundamentales.

Igualmente, la lectura de un grupo de libros sobre esas tecnologías resulta fundamental: leer un libro es aprender de la experiencia de otros, nada peor que aprender algo nuevo googleando y foreando resolviendo problemas puntuales, lo cual nos impide entender en su conjunto un framework o librería que tiene una filosofía de diseño particular que hay que comprender para usarla correctamente.

El tiempo dedicado tanto a esta formación como a prototipar la solución no es un gasto, sino una auténtica inversión, ya que así la solución final para el cliente se hará más rápido puesto que conocemos lo suficiente las tecnologías que se emplean y, además, si  invertimos ese tiempo en formarnos con libros y recursos de calidad, aumentaremos en varios niveles nuestro acervo profesional de cara al futuro.

 

No hace mucho he leído en un libro sobre AngularJS la siguiente sendencia, aplastante e ilustrativa:

"Software development is hard. As developers, we need to juggle customer requirements and pressure of deadlines, work effectively with other team members, and all this while being proficient with multitude of software development tools and programing languages. As the complexity of software rises so is the number of bugs. We are all human and we will make mistakes, tons of them. Lower-level, programming errors are only one type of defect that we need to constantly fight. Installation, configuration and integration issues will also plague any non-trivial project."

(Mastering Web Application Development with AngularJS)

No lo habría podido expresar mejor: durante el desarrollo de cualquier proyecto, una de las tareas a las que nos dedicamos es a desarrollar nuevo código de producción, pero esta es una tarea más; al mismo tiempo se realizan muchas otras tareas relacionadas con el proyecto pero igualmente importantes. No obstante, en ocasiones se trivializa la importancia de esas otras tareas pensando que poco tienen que ver con el proyecto y que en realidad lastran el escribir nuevo código; porque lo divertido, lo que más nos gusta a los desarrolladores de software es "escribir código", ¿no es así?

Eso podría pensar una mente ingenua recién llegada a un sector en el que mirar hacia atrás varios años es ver la prehistoria por una renovación continua de nuevas tecnologías, flujos de trabajo y, por qué no, incluso nuevos tipos de proyecto.

Todas esas otras tareas alrededor del código son las que hacen que éste se escriba con calidad y que se trabaje de manera eficiente y productiva. Si esto no lo hemos asumido, entonces estamos aún varios pasos atrás de trabajar realmente como profesionales. La calidad de un proyecto no sólo se mide porque el cliente obtenga un producto software de utilidad, también por cómo el mismo proyecto software pueda evolucionar, ser mantenido y evolucionado, y, además, en mi opinión una cuestión que no siempre es evidente, que los desarrolladores puedan trabajar en él de manera eficiente, productiva y con satisfacción.

De ninguna manera podemos pasar a codificar como siguiente paso una vez que tenemos claro los requisitos. Cuando comenzamos un nuevo proyecto, antes de teclear la primera línea de código, tenemos que definir claramente, entre otras cosas:

  • Las tecnologías más apropiadas a usar y la fricción entre ellas. Como apuntaba en un post anterior, en el desarrollo de aplicaciones web modernas, por poner un ejemplo, nos podemos encontrar con una explosión de librerías y frameworks que continuamente te hacen dudar si estás eligiendo la mejor opción.
  • Definir el flujo de trabajo (workflow) de los desarrolladores y ciclo de vida.
  • Detectar todo aquello que se pueda automatizar (compilaciones automáticas, pruebas de validación finales, etc). Esto es un elemento de apalancamiento impresionante y no siempre se tiene en cuenta: cualquier esfuerzo dedicado al principio en este punto, nos ahorrará ese esfuerzo multiplicado a lo largo de la vida del proyecto. Como mantra, yo siempre me digo que "todo aquello que hacemos manualmente en el proyecto varias veces al día se debe intentar automatizar". Esto hace posible que una vez que estemos trabajando en el proyecto nos dediquemos básicamente a implementar historias de usuario, la funcionalidad que se nos ha pedido.
  • Definir también cuál va a ser la estructura del proyecto: en qué carpeta va a ir el código, dónde y cómo se van a generar las pruebas, dónde se van a incluir los assets del proyecto, etc. Esta definición evitará que los miembros del equipo vayan incluyedo todos estos artefactos donde mejor les parezcan y sin coherencia.
  • Desde un primer momento, definir el despliegue del proyecto en producción para así anticipar desde la primera entrega problemas que de otro modo surgirían al final.

Todo esto, digo, se tiene que definir y poner en práctica antes de teclear la primera línea de código. La tentación de pasar directamente a desarrollar para tener algo que mostrar cuanto antes es en engaño que nos autoinfligimos: de tener todo lo anterior bien claro y definido, dependerá que ahorremos muchísimo esfuerzo en el futuro y, por tanto, redundará en la calidad del producto que generemos.

De aquí se pueden crear algunas reglas sencillas que debemos seguir siempre, como aquella que repito tal que no se realiza ninguna actividad si esta no ha sido previamente planificada, por ejemplo:

  • No se implementa nada nuevo si no ha sido incluido en la fase actual de trabajo con los tiempos de desarrollo estimados.
  • No doy por finalizado nada si no he creado las pruebas necesarias para demostrar (hoy, mañana, la semana que viene, etc.) que funciona.
  • No genero ningún nuevo artefacto software si antes no se ha establecido dónde y cómo tiene que ser generado.
  • ...

Estas reglas, sencillas en esencia, suponen la base por la que un equipo de desarrollo trabajará de manera productiva y ordenada.

Desde hace unos años participo en cierta medida en asuntos comerciales además de mi dedicación casi completa a dirigir un equipo de desarrollo, involucrándome, cómo no, en todo tipo de tareas técnicas, decisiones de diseño y arquitectura, toma de feedback de clientes, etc.

En este tiempo me ha llamado mucho la atención cómo bastantes clientes pontenciales de los productos que comercializamos se interesan por conocer el motor de base de datos que usa nuestro software. Los hay quienes le dan todas las bendiciones a SQL Server, otros que no cambiarían Oracle por SQL Server y unos pocos que nos han preguntado si el sistema podría funcionar con Cassandra (...). Nada que objetar, salvo su percepción completamente errónea de que el producto va a ser rápido y eficiente o lento y poco escalable según la base de datos que use, nada más.

Esta suposición, entendible para cierto perfil no técnico, la he visto también en otro tipo de perfiles muy técnicos y entendidos en software, para mi asombro.

La escalabilidad de un producto software no depende necesariamente de la base de datos que use, ni mucho menos: ésta es un elemento más de la larga lista de características que se le deben exigir a un producto software escalable.

No sólo he tenido que sufrir este tipo de polémicas en el eterno debate que si SQL Server  / Oracle, sino que además, todo lo que huela ahora mismo a volúmenes grandes de información rápidamente se asocia a la necesidad de una solución big data con Hadoop o MongoDB, confundiendo necesidades de almacenamiento con necesidades de análisis y procesamiento de la información.

Pero, ¿qué entendemos por escalabilidad? Esto va a depender el tipo de productos del que estemos hablando: para un portal web es sin duda el soportar un número alto de usuarios simultáneos, para una aplicación de análisis de datos, el poder captar y procesar cierto volumen de información; para un sistema de gestión de dispositivos físicos, como contadores digitales, por citar algo más ligado a mi actividad, no es lo mismo gestionar mil que cien mil dispostivos en un mismo sistema, con cinco o con cien operadores simultáneos.

Si un producto software usa una base de datos, de cualquier tipo que sea esta (relacional, no-sql, documental, etc), ésta por sí misma no va determinar el rendimiento del producto.

Esto que parece una perogrullada lo he tenido que explicar más de una vez a personas que se dedican a desarrollar software...

¿Qué determina un buen uso de una base de datos?

Este es un tema extraordinariamente amplio, pero para dar una idea de que el buen uso de una base de datos no es del todo trivial, podemos decir que no puede haber deficiencias en el diseño, sin redundancias innecesarias, las consultas a la base de datos deben ser sencillas y eficientes, se debe minimizar el número de consultas, cada consulta debe traerse la información estrictamente necesaria, las transacciones se deben dejar para las operaciones imprescindibles, debe hacerse un estudio concienzudo de los índices necesarios según la naturaleza de las consultas a realizar, según los casos habría que optar por las estructuras eficientes que ofreza el mismo gestor de bases de datos (particionado horizontal y vertical, por ejemplo), no mezclar una base de datos pensada para almacenamiento histórico con la base de datos de trabajo, dejar, si se puede según la idiosincrasia del producto, los procesamientos masivos de información para momentos que no interfieran en las ventanas de operación de los usurios clientes, y un largo etcétera .

Si este tipo de cosas no están bien resueltas, de nada nos servirá el mejor motor de base de datos.

En mi experiencia he visto claramente cómo la forma de almacenar la información determina lo sencillo o complejo que puede ser gestionarla por el software cliente de alto nivel. En ocasiones no falla cómo accedemos a la información, sino cómo esta información se encuentra almacenada en el repositorio de datos.

La forma en que necesitamos la información y cómo ésta se encuentra almacenada van de la mano.

Podemos incidir y hacer un buen trabajo a ese nivel mejorando el modo con el que nuestra aplicación accede a la base de datos, pero me temo que no queda la cosa resuelta del todo.

¿Qué ocurre con la arquitectura del propio sistema?

En cierto sentido, esta arquitectura va emergiendo a medida que intentamos que admita más y más dispositivos, usuarios o las entidades que representen la escalabilidad para nuestro producto.

Sin embargo, cuando hablamos de sistemas con grandes volumenes de procesamiento de información, tareas, procesos, etc. la arquitectura del sistema software es fundamental. Aquí ya hablamos de desarrollar software a otro nivel, con otro tipo de estrategias de diseño y desacoplamiento entre subsistemas para que cada uno haga su tarea de manera muy eficiente.

No hay estrategias generales que resuelvan cada caso concreto, pero sí buenas prácticas arquitecturales.

El poder distribuir estos subsistemas entre servidores distintos es todo un reto también de diseño para que todas las instancias puedan realizar su trabajo concurrentemente balanceando el trabajo, tanto si nos apoyamos en terceros sistemas para este propósito como si no.

Una aplicación que funciona bien para diez usuarios concurrentes no tiene nada que ver en diseño, arquitectura y diseño de repositorios de datos para la misma aplicación que tenga que dar servicio a 10k usuarios, por poner un ejemplo. La arquitectura no tendrá nada que ver, el diseño general y los microdiseños serán muy diferentes de un sistema a otro y el diseño y uso de la base de datos también serán extraordinariamente distintos.

La cuestión es que pocas veces comenzamos a desarrollar un nuevo producto sabiendo a ciencia cierta lo escalable que tiene que llegar a ser y en qué momento de su tiempo de vida deberá admitir más usuarios, dispositivos, etc. Hace falta mucha experiencia para prever esta arquitectura y los diseños adecuados. ¿Y entonces?

No existen soluciones mágicas para casi nada, pero lo que sí puedo asegurar es que si se comienza haciendo diseños limpios, con mucho esfuerzo en generar código desacoplado y de calidad, con una batería de pruebas suficientemente amplia, exhaustiva y mantenible, si nos esforzamos en cada fase del desarrollo del producto en identificar qué partes presentan cuellos de botella mediante análisis de rendimiento, etc. podremos tener un sistema para el que poder escalarlo no se convierta en algo dramático.

En ciertos sistemas complejos y más o menos grandes, todo, absolutamente todo cuenta: una iteración simple entre los elementos de una lista parece algo inocuo, pero cuando se ejecuta en un servidor cientos de miles de veces en un día, puede presentar un problema de rendimiento cuyo efecto se acumula desastrosamente a otros, presentando finalmente un problema global de rendimiento. Los detalles sí que importan.

Escribir código que funcione es nuestro trabajo, también es escribirlo de manera que funcione y que sea lo más limpio y simple posible, pero también que esa eficiente, lo que ya no es tan trivial en algunas ocasiones.

De hecho, existen libros dedicados a este asunto, como uno al que recurro habitualmente: Pro .NET Performance, por poner un ejemplo.

Otro recurso que uso confrecuencia (ligado a las tecnologías que más uso), es Improving :NET Application Performance And Scalability.

Por último, nada peor que encontrar problemas de rendimiento cuando el producto ya está en producción, si eso ocurre es que no se ha hecho un trabajo del todo bueno probando el sistema con anterioridad. No sólo hay que realizar tests unitarios, de validación e integración, también de rendimiento.

Así las cosas, cada vez que un cliente potencial con el que seguramente tenga media hora sólo para hablar, me pregunta qué base de datos usa el sistema, comprenderéis lo complicado que a veces resulta defender una respuesta cuando el cliente está más predispuesto hacia otro tipo de gestor de bases de datos que la que usamos.

En El Libro Negro del Programador se insiste mucho (como en cualquier otro libro que nos enseñe a ser mejores profesionales) que la refactorización de código no es sólo una herramienta que debemos usar sino que ésta es un hábito que emplear en el día a día y que distingue a los mejores desarrolladores de software.

No obstante, el concepto de refactorizar se suele entender exclusivamente como un trabajo de modificar líneas de código y mejorarlo sin afectar a su comportamiento externo; conseguimos, efectivamente, limpiar en cierta medida el código para que sea de mejor calidad, más legible y mantenible.

Siempre insisto en que esto no es algo que se hace o no, sino que forma parte del hecho de programar una solución, es algo consustancial a la misma.

Cuando terminamos de implementar una nueva funcionalidad, siempre nos debemos preguntar ¿podemos mejorar lo que hemos implementado en algún aspecto?, ¿se puede simplificar?, ¿se puede mejorar en relación al proyecto general como parte que se pueda reutilizar?, etc. Lo sorprendente es que cuando te creas el hábito de plantearte este tipo de cosas apenas termina haciendo falta hacerlo al final de implementar algo, ya que tienes el hábito de hacer las cosas limpias desde un principio de modo que al final, es esfuerzo o tiempo que empleas como puro trabajo de refactorizar es mínimo.

No obstante, se suele entender por refactorizar el trabajo de mejora del código, pero ¿qué ocurre con la misma estructura de un proyecto que va creciendo y creciendo con el tiempo? No hablo de diseño, sino de cómo están organizadas las carpetas, subproyectos, etc.

Cuando un proyecto crece en tamaño (en forma de librerías dispersas, multitud de sub-proyectos, demasiadas dependencias externas, incluso una estructura de los proyectos de pruebas compleja) se debe y tiene que refactorizar igualmente.

Recientemente hemos cerrado una nueva revisión de uno de los proyectos en los que trabajamos actualmente (la Plataforma de Telegestión y MDM IRIS para dispositivos de telemedida PRIME). Pues bien, he planteado unos días de trabajo para mejorar la estructura interna del mismo para evitar que sea cada vez más compleja (de gestionar y de trabajar sobre ella).

En concreto, algunas de las cosas que vamos a evaluar son:

  • Unificación de algunas librerías dispersas que podrían vivir en un único proyecto.
  • Integración de algunos subproyectos pequeños que podrían vivir coherentemente en otros.
  • Mejora de la distribución y estructura de las puebas de integración.
  • Plantear las bases para que el gestor de tareas, ahora muy ligado a la idiosincrasia de la solución, viva como un framework independiente del proyecto (y reutilizable por otros proyectos que pongamos en marcha).
  • etc.

¿Qué pretendo conseguir con esto?

Cuando un proyecto crece en complejidad, cuando hablamos de cientos de miles de líneas de código, aunque esté todo realizado con extremada limpieza y con una arquitectura general y microdiseños muy cuidados, la misma distribución de los artefactos del proyecto puede ser un problema que impida llegar a los sitios con sencillez y comodidad. La espaguetización del proyecto no sólo se puede producir en su código sino también en la estructura misma de sus módulos.

De este modo, al mejorar y simplificar cómo están distribuidas y organizadas las cosas, ganamos agilidad y comodidad a la hora de trabajar en una nueva revisión. Lo que no es fácil de ver es que esta agilidad y comodidad se traduce después en ahorro de muchas horas de trabajo. Coseguimos la máxima calidad en algo cuando tenemos un entorno cómodo de usar y de trabajar. La facilidad de mantenimiento también tiene mucho que ver con esto.

Detrás de cada acto de refactorización, del tipo que sea, se esconde un ahorro de tiempo futuro al dedicar menos esfuerzo y tiempo en evolucionar una aplicación bien hecha, o lo que es lo mismo, el tiempo invertido en refactorizar se traduce en una mejor productividad (= menos recursos económicos necesarios para hacer algo).

Me sorprende a menudo cómo los desarrolladores de software nos complicamos la vida por no dar un paso atrás e identificar aquello que nos impide centrarnos en escribir código de producción y testearlo. Los problemas casi siempre surgen de no montar en entorno adecuado de pruebas que permita testear la nueva funcionalidad que incorporamos con comodidad. El extremo más radical de esto consiste cuando la única posibilidad de probar algo es ¡en el entorno de producción del cliente!. Me temo que sólo mecionarlo se me pone la piel de gallina, aunque lamentablemente lo he vivido y lo he visto en varias ocasiones.

Cuando comienzo un nuevo proyecto, aunque sea sencillamente probar una tecnología que desconozco y con la que comienzo a jugar, lo primero que hago es montar un entorno en el que trabajar con absoluta comodidad: si no lo hacemos así, a los problemas habituales de resolver errores y avanzar en aquello que no conocemos bien le añadiremos los inconvenientes de trabajar con ese entorno incómodo (o lo que es lo mismo, perderemos más tiempo en problemas con el entorno que en crear software útil).

Por poner un ejemplo, últimamente estoy haciendo algunas pruebas de concepto implementando algunos módulos en node.js que acceden a un servidor MongoDB. Lo primero que he hecho ha sido instanciar una máquina virtual con un Ubuntu Server en el que arrancar MongoDB; una vez que aprendo a limpiar completamente todas sus colecciones y realizar las tareas de administración básicas y suficientes para las pruebas que quiero implementar, todo lo demás lo puedo realizar mucho más rápidamente y cómodamente y, sobre todo, en pocos pasos.

Esto parece una trivialidad, pero si no lo hacemos así y estas operaciones elementales no las hiciera con sencillez, me resultaría muy complicado avanzar en las pruebas de node.js que estoy realizando: terminaría hastiado y acabaría abandonando más pronto que tarde.

El pricipio que me encanta aplicar en esto es aquel que indica que para las tareas repetitivas, sólo deberíamos trabajar una vez.

Efectivamente, sólo dedico algo de tiempo al comienzo para crearme un entorno de trabajo lo suficientemente cómodo como para poder centrarme en las pruebas o lo más importante del trabajo que me propongo realizar.

No hace mucho terminé de leer un libro que me ha encantado; se trata de El Efecto Compuesto de Darren Hardy (se puede encargar en español aquí), sobre cómo los pequeños hábitos y las pequeñas acciones, mantenidas en el tiempo y con constancia, representan realmente una gran diferencia.

Del mismo modo, ¿como sería nuestro trabajo si cada vez que quisiera ejecutar la prueba X necesitara pasar cinco minutos limpiando una base de datos, levantar ciertos servicios, etc.?, ¿cuánto tiempo perderíamos al usar un editor que no dominamos del todo?, ¿cuánto tiempo perdido si tuviéramos que probar algo leyendo el resultado en un documento txt?.Para ser productivos escribiendo el código de producción en el que trabajamos tenemos que crearnos entornos sencillos y eficientes, lo que requerirá seguramente dedicar algo de tiempo la primera vez que se genera ese entorno, aunque esa pequeña inversión de trabajo y tiempo la rentabilizaremos con creces posteriormente.

De nuevo, vemos cómo un buen y profesional desarrollador de software, competente en su ámbito, con suficiente experiencia, no llegaría al 10 si no se rodea de hábitos y condiciones que permiten que su trabajo se haga con comodidad y eficiencia.

Es recurrente la pregunta que me hacen según la cual siempre se pone en duda la necesidad de realizar software sencillo, fácil de entender (por nosotros y por los demás) pero sobre todo, mantenible. Muchos de quienes nos dedicamos a desarrollar software profesional no tenemos claro aún ciertos conceptos esenciales, como por ejemplo que un diseño matenible no tiene nada que ver con un diseño de algo que es imposible modificar, depurar, evolucionar, etc. Aun funcionando bien, el primero nos dará un mayor rendimiento del trabajo realizado, mientras que en el segundo caso el software pasa a producción moribundo y sin apenas solución de continuidad.

No obstante, nos cuesta reconocer la relación que existe entre la sencillez y la facilidad de mantenimiento.

Mantenible (palabra mantra en el Libro Negro del Programador) no significa trivial, que un principiante sin apenas esfuerzo pueda entender a la primera, sino coherente, un diseño eficiente que facilita los cambios en el futuro y que la adaptación a nuevos requerimientos relacionados son viables (a un coste asumible).

A diferencia del autor de un best-seller que cuando publica un libro que funciona bien recoge royalties según las ventas, cuando publicamos un producto software tendremos más o menos éxito si este puede evolucionar, si puede ser cambiado y fácilmente mantenido.

En Code Simplicity (libro que recomiendo aunque no se lea ni una línea de código) se pueden leer frases auténticamente reveladoras que el autor intenta sintetizar a modo de leyes comunes que gobiernan el desarrollo de cualquier software.

Una de estas afirmaciones que sostiene el autor es que la facilidad de mantenimiento de cualquier pieza de código es proporcional a la simplicidad de sus partes individuales ("The ease of maintenance of any piece of software is proportional to the simplicity of its individual pieces").

Cuando leí esta frase pensé que cuánto tiempo había intuido esta relación pero sin embargo había sido incapaz de sintetizarla de esta manera tan magistral. Una virtud tiene ese libro: sintetiza de una manera genial auténticas verdades que aún siendo sencillas de entender no lo es tanto de aprehender e introducir en nuestro adn de programadores profesionales.

Efectivamente, parece básico pero no es tan elemental tenerlo presente en nuestro día a día.

Si queremos hacer software mantenible, la clave está en conseguir que cada una de sus partes sean sencillas. La cuestión no es contraponer sencillez versus mantenibilidad, sino que ambos conceptos están íntimamente relacionados (y además su relación es proporcional).

¿Por qué nos debemos preocupar de que lo que desarrollamos pueda ser mantenido con relativa facilidad?, por la sencilla razón de que durante la vida de cualquier producto software, sea algo que se venda bajo licencia, un desarrollo interno en nuestra compañía o incluso cualquier proyecto personal, éste va a ser mantenido durante mucho más tiempo que el que ha costado desarrollarlo. Es más, cuanto más éxito tenga, más cambios y mejoras se nos van a pedir, por lo que el ritmo de evolución de un producto puede ser considerado también como un síntoma de su éxito.

Yo siempre pongo el mismo ejemplo: ¿compraríamos un coche que es casi imposible de reparar o al que es muy difícil cambiarle el aceite?.

¿Verdaderamente tenemos esto presente cuando escribimos líneas y líneas de código?.

Estos conceptos de sencillez y facilidad de mantenimiento son fáciles de entender, pero lo que no es tan fácil es tenerlos presentes en nuestro día a día: nadie es capaz de escribir código limpio a la primera, sino que para llegar a él debemos buscar esa solución sencilla mediante refactorings, repaso continuo de lo ya realizado, etc.

Un ingenuo desarrollador simplificará cuando le venga bien, no de manera sistemática, no se preocupará de que lo que hace pueda ser matenido / depurado con facilidad (no percibe que esto le puede golpear en el rostro con efecto boomerang); un programador cándido busca exclusivamente que lo que está haciendo funcione a cualquier precio, sin tener en cuenta eso de que pan para hoy y hambre para mañana...

Si nuestro software es mantenible, estaremos poniendo los cimientos del éxito del mismo. Si no lo es, nacerá moribundo y terminará convirtiéndose en un fracaso y fuente de clientes insatisfechos. Conseguimos este objetivo de mantenibilidad cuidando de que las partes individuales de nuestro producto sean sencillas.

Hay diferentes maneras de aprender y mejorar nuestro trabajo como desarrolladores de software. El aprender una nueva tecnología (o mejorar las habilidades en aquellas que dominamos) requiere a menudo repensar cómo hacemos las cosas.

Hay quien se siente cómodo buscando en foros continuamente para resolver cualquier problema; esto para mí es algo así como aprender por prueba y error llevado al extremo. Hay quien, como yo, preferimos aproximarnos a una nueva herramienta o tecnología leyendo un buen libro de arriba abajo. Las opciones son diversas y como se suele decir cada maestro tiene su librillo.

No obstante, he visto durante mucho tiempo el que es un gran defecto de muchos programadores profesionales: cuando conocen en profundidad las herramientas, lenguajes, etc. que utilizan en el día a día, se anquilosan haciendo siempre, durante años, las mismas cosas y del mismo modo. El desarrollar software tiene un grado versatilidad y flexibilidad que difícilmente hay una única forma de hacer algo, todo lo contrario, siempre existen cientos de formas de resolver un problema; entonces, ¿por qué nos abstenemos de descubrir esos otros enfoques?.

Una manera fundamental de aprender a mejorar, conocer alternativas y producir el efecto pero-por-qué-a-mí-no-se-me-ha-ocurrido-esto es sencillamente leer el código de proyectos realizados por otros.

Desde hace muchos años soy asiduo lector de CodeProject, web colaborativa donde se puede encontrar casi de todo; igualmente, existen repositorios públicos bien conocidos donde se encuentran verdaderas joyas de programación; hablo de GitHub, CodePlex y Google Code, por poner unos ejemplos. Cualquiera quiera compartir un proyecto lo puede publicar en alguno de esos portales.

Sin embargo, lo que aquí quiero señalar es que no hay mejor manera de aprender a programar que leyendo proyectos realizados por otros. Así de sencillo. En esos repositorios públicos que apuntaba antes se encuentra verdaderamente el state-of-the-art de muchas áreas y tecnologías.

Husmear en las tripas del código de otros proyectos te permite descubrir nuevas formas de hacer, te enseña cómo otros han resuelto ciertos problemas e incluso nuevos estilos de programación.

Por mucho que dominemos ciertos lenguajes de programación, de scripts o de cualquier cosa, cada problema, solución o proyecto que tengamos que afrontar se puede resolver de muchas formas diversas; yo siempre digo lo mismo: tonto del todo no soy, o eso creo, pero seguro que hay otras mentes más privilegiadas a las que se les ha ocurrido a resolver algo de una manera más smart. Bucear en otros proyectos te lleva a ese tipo de aprendizaje que difícilmente se va a encontrar leyendo un libro.

Por poner un sólo ejemplo, he aquí una de esas joyas de desarrollo por su buen diseño e implementación: moq.

CodePlex

github

Google Code

 

En ocasiones me han preguntado por qué insisto tanto en que el código que se escriba sea lo más legible posible, que se pueda leer y entender sin que construcciones extrañas o clases de cientos de líneas te arañen la vista, por poner unos ejemplos.

Se pueden dar muchos argumentos a favor de añadir un poco de esfuerzo a nuestro trabajo para escribir código que otros puedan entender; de todos los posibles a mí me parece que hay un criterio fundamental que no siempre se tiene tan en cuenta.

Cualquier trozo de código va a ser muchas veces leído que escrito o modificado. Esto es, cualquier método, función, clase, etc. va a ser repasado y leído en más ocasiones que las veces que hay que ir a ellos para hacer una pequeña modificación. Por tanto, si a todas esas veces le añadimos un coste de tiempo por la complejidad de entender lo que leemos estamos ante un obstáculo que nos quitará tiempo para dedicarnos a tareas más productivas o creativas.

No escribimos software para nosotros sino para que otros lo entiendan. Esto es clave para conseguir soluciones mantenibles y evolucionables. A veces ignoramos que ese otro podemos ser nosotros mismos.

¿He mencionado un extra de esfuerzo?. En realidad ese esfuerzo lo tenemos que hacer explícitamente si no estamos acostumbrados a fluir con los buenos hábitos de programación que existen para este tema en particular; elemenos tan sencillos y triviales como elegir buenos nombres descriptivos para nuestros artefactos de software (¿por qué llamar a una variable _nde cuando le podemos poner _numeroDeEntidades, por ejemplo), métodos o funciones que hacen una única cosa sin más de tres o cuatro argumentos, elementos de una clase ordenados siempre de la misma manera en toda la solución y un larguísimo y extenso etcétera.

Soy de la opinión de que quien no intenta escribir legiblemente realmente no le interesa hacer un buen trabajo y está dejando piedras en el camino con el que un compañero se tropezará cuando tenga que asumir ese código.

Lo sorprendente es que muchas veces el esfuerzo de escribir algo de manera complicada ¡es el mismo que escribirla de manera simple!. Si tenemos las dos opciones, ¿por qué entonces no hacer la que supone un mejor trabajo?.

La productividad de un equipo de trabajo en ocasiones viene dada por pequeños detalles que en sí mismos pueden parecer insignificantes pero sumados marcan una gran diferencia. El Libro Negro del Programador habla intensivamente acerca de cómo desterrar malos hábitos e incorporar buenos precisamente para aumentar la productividad. En ocasiones se entiende ésta exclusivamente como echar más horas: yo digo que trabajar con productividad tiene más que ver con usar herramientas correctamente, tener un equipo de trabajo bien organizado y buenos procedimientos.

Uno de estos procedimientos que pueden ahorrar muchísimo tiempo de esfuerzo inútil es subir correctamente al repositorio de código los últimos cambios introducidos. Como en muchas ocasiones, los mejores hábitos y técnicas son precisamente los más sencillos.

¿Cuánto tiempo hemos pasado resolviendo conflictos de código introducido al mismo tiempo o después de nuestros últimos cambios?. Demasiado si se tiene en cuenta que ese es tiempo no productivo y, en ocasiones, frustrante.

La siguiente técnica busca minimizar las posibilidades de generar conflictos; cuanto mayor sea el número de desarrolladores en un equipo, más posibilidades habrá de generar este tipo de problemas.

Para reducir las posibilidades de conflicto en el código cuando vamos a introducir un nuevo cambio:

  • Obtenemos la última versión (lo que nos garantiza que no vamos a usar en ningún momento código obsoleto).
  • Introducimos los cambios o nueva funcionalidad.
  • Ejecutamos todos los tests; deben pasar el 100%.
  • Obtenemos de nuevo la última versión. De este modo vemos si nuestros cambios han introducido algún error de compilación con los cambios introducidos por otros (o al revés).
  • Antes de subir, volvemos a ejecutar todos los tests.
  • Ahora sí que subimos los cambios.

De este modo reducimos radicalmente la posibilidad de conflictos.

Hay diversas opiniones sobre la frecuencia de subir los cambios (cada X horas, cuando se termina una funcionalidad completamente, etc.). Esto dependerá de la naturaleza de cada proyecto. Personalmente me siento cómodo con subir frecuentemente (varias veces al día).

Lo mejor es acordar entre los miembros del equipo el procedimiento a seguir en este proceso (check-in process): ¡mejor uno aunque no sea el mejor que ninguno!.

Las reglas para desarrollar buen software son en ocasiones de un total sentido común y están al alcance casi de cualquiera que se proponga realizar su trabajo en condiciones.

No hace mucho re-descubrí un conjunto de sencillas preguntas que nos dicen si tenemos un buen equipo de trabajo y trabajamos desarrollando software con un mínimo de calidad: es el test de Joel que resumo a continuación y que no es más que sentido común aplicado a nuestra actividad diaria:

  1. ¿Usas un repositorio de código?
  2. ¿Puedes realizar la compilación de tu aplicación en un único paso?
  3. ¿Realizas compilaciones diarias?
  4. ¿Trabajas con una lista o herramienta de gestión de errores?
  5. ¿Corrijes errores pendientes antes de escribir nuevo código?
  6. ¿Trabajas con una planificación actualizada?
  7. ¿Tienes una especificación de lo que tienes que hacer?
  8. ¿Tienes lo programadores unas condiciones de trabajo convenientes?
  9. ¿Usas las mejores herramientas que te puedas permitir con tu presupuesto?
  10. ¿Tienes testers en el equipo o, en su defecto, existe al menos tal rol?
  11. En las entrevistas de nuevos candidatos, ¿escriben código?
  12. ¿Compruebas la usabilidad del código que desarrollas con la opinión de otros desarrolladores?

Lo ideal, según el autor de este sencillo test, es responder a todo con un SÍ, pero si respondemos a diez o menos de estas preguntas afirmativamente, entonces tenemos serios problemas en nuestra manera de enfocar el desarrollo de software.

Podemos cumplir más o menos con la lista de preguntas del test o la podemos cumplir horriblemente mal, y, al mismo tiempo, podemos entregar una aplicación que funciona, nuestro jefe estará contento y seguramente nuestro cliente también, pero... ¿a qué coste?. ¿Cuánto tiempo habremos perdido resolviendo compilaciones rotas?, ¿cuánto tiempo habremos dedicado a resolver este error que surgió hace dos semanas?, ¿será nuestro código mantenible cuando el mismo cliente nos pida nueva funcionalidad?, ¿podremos recuperar la versión X de una clase porque hemos introducido cambios desastrosos?, ¿trabajamos eficientemente sin una mínima planificación?, ¿sabemos exactamente qué hay que hacer si no existe una especificación clara?. ¿Somos productivos si no tenemos un entorno de trabajo productivo?.

La mayoría de estas cuestiones las deben resolver y responder los responsables, coordinadores y jefes de los desarrolladores y no éstos, aunque sí es fundamental que los mismos programadores alerten de estas carencias a sus responsables de equipo.

A las preguntas del clásico test de Joel, yo añadiría además las siguientes:

  1. ¿Está el código suficientemente respaldado por pruebas?
  2. ¿Existe una correcta gestión de la configuración?
  3. ¿Se sigue algún tipo de metodología?
  4. ¿Se esfuerza el equipo por simplificar y mejorar continuamente la estructura y el diseño de la aplicación?
  5. ¿Es el código que se desarrolla suficientemente simple?
  6. ¿Es bajo el esfuerzo que tendría que hacer un nuevo miembro del equipo para realizar código nuevo de producción?

Páginas

Mis libros en todas las tiendas:

Amazon
Google Play
Apple
Kobo
Barnes and Noble
Scribd
Smashwords
Payhip
Gumroad

Rafael Gómez Blanes Rafael Gómez Blanes
¿Hablamos?

 

Trabajo en...

Archivo

Mis novelas...