viernes, 30 de diciembre de 2011

Empezando con Node.js

El pasado día 27 de diciembre estuve en la charla organizada por el grupo MadridJS sobre Node.js y Heroku. Acudí porque había leído algo sobre el tema y tenía curiosidad por saber de qué trataba realmente este proyecto. En la charla se dió una explicación inicial de lo que es y para qué sirve e incluso se vió algún ejemplo real, pero además se habló de algunos otros proyectos y tecnologías relacionadas con Node.js, como es el manejo de WebSockets con la librería Socket.IO.

A partir de lo visto en dicha charla me ha picado la curiosidad y en lo últimos días he estado buscando información sobre Node.js para poder conocer algo más sobre este proyecto. Node.js lleva la programación en Javascript al lado del servidor. Es una plataforma que permite el desarrollo de servidores de alta concurrencia y que se ejecuta sobre la máquina de Javascript de Google V8. Se basa en el paradigma de programación orientada a eventos de entrada y salida de forma asíncrona.

¿Y cuál es la principal ventaja de Node.js? Pues su rapidez y el menor número de recursos que consume. Un servidor de Node.js se ejecuta en un único hilo y no crea un nuevo proceso o un nuevo hilo por cada petición entrante, con el consumo de recursos que esto supone. Sin embargo, las operaciones en Node.js se ejecutan de forma no bloqueante en base a eventos, por lo que se pueden aprovechar los tiempos muertos de las operaciones de entrada/salida para servir otras peticiones y aumentar la rapidez.

¿Que significa que las operaciones se ejecutan de forma no bloqueante y en base a eventos? Pues que todas las operaciones de entrada/salida se realizan de forma asíncrona. Cuando se llama a una operación de entrada/salida se le pasa una función callback que será el manejador del evento de ejecución y pasa a ejecutarse en segundo plano, continuando con la ejecución del programa. Cuando la operación de entrada/salida haya finalizado saltará el evento correspondiente y se llamará a la función callback que lo maneja, que dispondrá de la información recuperada y podrá trabajar con ella.

Además de los módulos que Node.js incorpora en su instalación, existen multitud de módulos adicionales que puedes ser usados, los cuales se pueden instalar a través de la herramienta de gestión de paquetes de Node.js: NPM (Node Package Manager). Dichos módulos permiten el acceso al sistema de archivos, bases de datos, ofrecen una implementación del patrón MVC... Dichos módulos están organizados según el estándar CommonJS.

En vez de incluir aquí un ejemplo de cosecha propia, voy a remitir a quien quiera profundizar más a “The Node Beginner Book”, un tutorial estupendo para iniciarse en Node.js y empezar a entender su forma de trabajar.

Otras referencias sobre Node.js:
Página oficial de Node.js
Node.js en español
Introducción a Node.js de Rafa Muñoz
Introducción a Node.js en debugmodeon
Understanding Node.js
How to Node
Understanding the Node.js event loop
Colección de enlaces sobre Node.js

viernes, 23 de diciembre de 2011

La evolución de JavaScript

En los últimos meses, y viendo la popularidad de JavaScript debido a fenómenos como HTML5 o NodeJs, me he propuesto ponerme al día con JavaScript, ese lenguaje que normalmente utilizo para hacer validaciones en mis páginas HTML, pero que, en realidad, permite mucho más que eso.

Lo cierto es que, debido al boom que está sufriendo este lenguaje, lo que más me está costando es saber cuál es el estado en que se encuentra y hacia donde se encamina. Por ello, sirva este post para recopilar las ideas que sobre ello he ido encontrando y para, a partir de él, seguir profundizando en el lenguaje y sus capacidades, tanto nuevas como antiguas.

JavaScript surgió como un lenguaje de script en el navegador que apareció por primera vez dentro de Netscape Navigator 2 allá por 1995, y que se basaba en el lenguaje CEnvi desarrollado anteriormente por Nombas.

Fue tal el éxito de JavaScript que Microsoft decidió incorporar su propia implementación de este lenguaje, llamada JScript, en la versión 3 de su navegador Internet Explorer.

Debido a los problemas que entrañaban las diferencias entre implementaciones, se envió la versión JavaScript 1.1 como propuesta a ECMA para su estandarización, que culminó con el estándar ECMA-262. Este estándar dicta la base del lenguaje ECMAScript a través de su sintaxis, tipos, sentencias, palabras clave y reservadas, operadores y objetos, y sobre la cual se pueden construir distintas implementaciones. La versión JavaScript 1.3 fue la primera implementación completa del estándar ECMAScript.

Actualmente, las implementaciones de JavaScript de los navegadores implementan la versión 3 del estándar ECMAScript, que data de 1999, y cuyas últimas mejoras han sido:
  • Soporte de expresiones regulares.
  • Nuevas sentencias de control.
  • Manejo de excepciones (bloque try-catch).
  • Definición de errores más precisa.
  • Formateo de salidas numéricas de datos.
  • Manejo de strings más avanzado.

Cuando en 2004 se empezó a trabajar en la evolución de JavaScript, surge la revolucionaria propuesta ECMAScript 4, que pretende convertir JavaScript en un nuevo lenguaje con nuevas reglas, principalmente introduciendo el tipado de variables y el concepto tradicional de clases e interfaces al estilo de lenguajes como Java.
Debido a la revolución que estos cambios en JavaScript suponían, surge ECMAScript 3.1 como contrapropuesta, que evita introducir nuevos conceptos y añade en su lugar características adicionales al lenguaje.

Finalmente se llegó a la decisión de enfocarse en la propuesta ECMAScript 3.1, que se ha cambiado de nombre, pasando a ser ECMAScript 5. ECMASCript 4 ha quedado relegada al proyecto Harmony. Harmony, que a su vez parece que pasará a ser ECMAScript 6, pretende incorporar a posteriori en ECMAScript 5 algunas de las mejoras propuestas inicialmente por ECMAScript 4.

Las características que se incluyen en ECMAScript5 son las siguientes:
  • Getters y setters.
  • Array extras y reductions.
  • Cambios en Object:
    • Rediseño de los atributos internos de las propiedades.
    • Introducción de métodos estáticos de Object, que permiten:
      • Acceder a la información del prototipo.
      • Manipular las propiedades de un objeto.
      • Crear objetos de forma dinámica.
      • Obtener los nombres de las propiedades.
      • Impedir que un objeto sea modificado.
    • Cambios a las funciones:
      • Soporte nativo del function currying a través del método bind.
    • Cambios en el objeto Date.
  • Soporte nativo de JSON.
  • Modo de ejecución strict, que define reglas de codificación más estrictas que el modo de ejecución estándar.

Los cambios que podrían ser introducidos en ECMAScript 6 se mencionan en esta charla de Dave Herman, ingeniero de Mozilla Labs y representante del TC39:
  • Módulos.
  • Ámbito a nivel de bloque (sentencia let).
  • Generators.
  • Proxys.
  • Destructuring assignments.
  • Rest y default arguments.
  • Name objects.
  • Iterators.
  • Array comprehensions.
  • String templates.
  • Hash tables y weak maps.

También parece que se podría incluir:
  • Constantes.
  • Clases y herencia.
  • Tipos Map, Set y StructType.

Algunos navegadores ya han ido incluyendo estas y otras mejoras del lenguaje, como se indica en estos datos obtenidos de Wikipedia:
  • Javascript 1.5 (Firefox 1.0, IE 8-, Opera 6, Safari 5-, Chrome 10-):
    • ECMAScript 3.
    • Constantes.
    • Getters y setters.
  • Javascript 1.6 (Firefox 1.5):
    • Array extras.
    • Array y String Generics.
  • Javascript 1.7 (Firefox 2.0):
    • Ámbito a nivel de bloque.
    • Generators.
    • Iterators.
    • Array Comprehensions.
    • Destructuring assignments.
  • Javascript 1.8 (Firefox 3.0, Opera 11.50):
    • Expression closures.
    • Generator Expressions.
    • Array Reductions.
  • Javascript 1.8.1 (Firefox 3.5):
    • Soporte nativo de JSON.
  • Javascript 1.8.2 (Firefox 3.6):
    • Actualizaciones menores.
  • JavaScript 1.8.5 (Firefox 4, IE 9):
    • 1.8.2.
    • ECMAScript 5.

Referencias:
- JavaScript en Wikipedia.
- ECMAScript en Wikipedia.
- Presentación sobre ECMAScript 5.
- ECMAScript 5: The Definitive Slides.
- Otra presentación sobre ECMAScript 5.
- Video: "Changes to JavaScript, Part 1".
- Video: "Dave Herman: The Future of JavaScript (ECMAScript 6)"

sábado, 8 de octubre de 2011

Compresión y comprobación de archivos JavaScript desde Maven

Estos últimos días he estado probando YUI Compressor Maven Mojo para la gestión de los archivos javascript de mis proyectos.

YUI Compressor permite, en primer lugar, comprobar la corrección del código javascript mediante JSLint. En segundo lugar, permite comprimir y ofuscar los archivos javascript, de forma que se reduce su peso y se dificulta la introducción de ataques a través del código javascript.

Todo ello, a través de la siguiente configuración del pom.xml:
<pluginRepositories>
<pluginRepository>
<name>oss.sonatype.org</name>
<id>oss.sonatype.org</id>
<url>http://oss.sonatype.org/content/groups/public</url>
</pluginRepository>
</pluginRepositories>

<build>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compress</goal>
<goal>jslint</goal>
</goals>
</execution>
</executions>
<configuration>
<jswarn>true</jswarn>
<failOnWarning>false</failOnWarning>
<nomunge>false</nomunge>
<excludes>
<exclude>**/jquery.js</exclude>
</excludes>
<aggregations>
<aggregation>
<includes>
<include>**/*-min.js</include>
</includes>
<output>${project.build.directory}/${project.artifactId}-
${project.version}/js/revistero.js</output>
</aggregation>
<aggregation>
<includes>
<include>**/*-min.css</include>
</includes>
<output>${project.build.directory}/${project.artifactId}-
${project.version}/css/revistero.css</output>
</aggregation>
</aggregations>
</configuration>
</plugin>

<build>

La configuración de los atributos jswarn y failonwarning hace que se realice la comprobación de los archivos javascript pero sin parar la ejecución en caso de error. El atributo nomunge indica que se debe ofuscar el archivo ("munge": Modify Until Not Guessed Easily).

Tanto la verificación como la compresión de los archivos se ejecutarán cada vez que se construya el proyecto mediante:
mvn package

O se despliegue con:
mvn jetty:run-war

Pero se pueden ejecutar de forma independiente. Para lanzar la compresión de los archivos javascript y css se ejecutará:
mvn net.alchim31.maven:yuicompressor-maven-plugin:compress

Y para realizar la verificación de los archivos javascript:
mvn net.alchim31.maven:yuicompressor-maven-plugin:jslint

Referencias:
YUI Compressor Maven Mojo
Minimizando el tiempo de carga: Maven YUI Compressor

miércoles, 31 de agosto de 2011

Apprenticeship Patterns

La Artesanía del Software es un enfoque del desarrollo de software distinto de la Ingeniería de Software tradicional. Está más orientada hacia las relaciones entre desarrolladores y clientes en busca de valor para los proyectos, y también hacia las relaciones entre los propios desarrolladores, favoreciendo el traspaso del conocimiento.

Este libro trata sobre el enfoque de la Artesanía del Software sobre el aprendizaje que debe realizar un desarrollador. Este aprendizaje no se limita sólo a la etapa inicial de formaciónsino que debe ser continuo a lo largo de toda la carrera profesional.

Los autores (Dave H. Hoover y Adewale Osinheye) han recopilado una serie de patrones que pueden guiar este aprendizaje y que, a continuación, trataré de presentar. Dichos patrones se presentan por capítulos según su intencionalidad:

Vaciando La Taza

Cómo iniciarse en el aprendizaje desde una postura abierta y receptiva, eliminando los prejuicios.
  • Tu Primer Lenguaje: Conocer un primer lenguaje es lo que servirá como base para todo el aprendizaje posterior.
  • El Cinturón Blanco: Hay que ser humildes y receptivos, conscientes de que para aprender algo nuevo hay que desprenderse en parte de lo que sabíamos.
  • Desata Tu Entusiasmo: El entusiasmo da energía a un aprendiz y a su equipo, por lo que es un valor que hay que fomentar.
  • Habilidades concretas: Una persona que está aprendiendo puede tener poco que ofrecer a un equipo de programadores expertos. Sin embargo, es necesario que pueda ofrecer algo para que se le acepte como un miembro más.
  • Expón Tu Ignorancia: Se debe hablar abiertamente de las carencias, y no engañar a nadie atribuyéndonos cualidades que no tenemos.
  • Confronta Tu Ignorancia: Las carencias identificadas se deben combatir. Esto aumenta el conocimiento y permite identificar nuevas carencias.
  • El Profundo Abismo: En determinados momentos es necesario llevar a cabo tareas que están aparentemente más allá de nuestra capacidad para poder crecer.
  • Refúgiate En Tu Competencia: Para ganar confianza en caso de vernos superados se dedicar un rato a tareas que se dominan.

Caminando Por El Largo Camino

El aprendizaje del desarrollo de software es un camino largo abarca toda la carrera profesional.
  • El Largo Camino: A lo largo de la carrera profesional la prioridad debe ser aumentar las habilidades, no en el beneficio económico ni el ascenso en la escala empresarial en sí mismo.
  • Oficio Más Que Arte: Hay que ser consciente de que se están construyendo herramientas que deben aportar valor a los clientes.
  • Motivaciones Sostenibles: Para poder llegar al final del camino, se debe trabajar sin sacrificar de forma permanente ningún otro aspecto importante de la vida personal.
  • Alimenta Tu Pasión: Durante algunos periodos puede que el trabajo del día a día no sea suficientemente gratificante, y habrá que buscar algo que permita mantener la pasión por el aprendizaje, ya sea en el trabajo o fuera de él.
  • Dibuja Tu Propio Mapa: Somos los únicos responsables de nuestra trayectoria profesional. Si nuestra empresa o equipo actuales no son favorables al aprendizaje, tenemos en nuestra mano cambiar estas condiciones.
  • Usa Tu Título: El título profesional es reflejo de la organización en la que nos encontramos. Un título que no refleja nuestra realidad es un síntoma de que debemos actuar para cambiar nuestra situación.
  • Permanece En Las Trincheras: Para aprender a desarrollar del software hay que mantenerse desarrollando, y no dejarse seducir por ascensos profesionales que nos separen de él, llevándonos, por ejemplo hacia la gestión.
  • Un Camino Diferente: Se puede aprender a desarrollar siguiendo muchos caminos, por lo que no se debe menospreciar a quién ha elegido uno distinto.

Auto-Evaluación Precisa

Hay que saber reconocer nuestra situación respecto del entorno en que nos encontramos, para poder actuar si se da el peligro de estancamiento en el aprendizaje.
  • Sé El Peor: Ser el peor en un nuevo equipo permite aprender cosas a costa del esfuerzo que supone ponerse al día.
  • Encuentra Mentores: Conseguir que alguien con más conocimientos nos supervise es la mejor manera de acelerar el aprendizaje.
  • Espíritus Afines: Gente con los mismos intereses nos ayudará a aprender, ofreciendo visiones alternativas, apoyo o nuevas oportunidades.
  • Juntar Los Codos: Aprender junto a alguien incentiva la comprensión y facilita la comunicación de aquello que se da por supuesto pero que supone la diferencia.
  • Barrer El Suelo: Si se es el miembro de menor experiencia en un equipo se deben aceptar las tareas menos llamativas para poder aspirar a tareas más complejas.

Aprendizaje Perpetuo

No se puede dejar de aprender porque la tecnología avanza y ofrece nuevas soluciones que debemos conocer en la medida de lo posible.
  • Expande Tu Ancho De Banda: Hay que abrirse a todas las fuentes de conocimiento disponibles (blogs, Twitter, listas de correo, grupos de usuarios, conferencias, libros, cursos…) para recibir información sobre aspectos distintos a nuestra experiencia.
  • Practica, Practica, Practica: Para poder aprender es útil practicar con ello en un entorno tolerante con los errores. Se puede practicar con ejercicios (katas) sólo o junto con otros desarrolladores (dojo). Una realimentación inmediata sobre los errores que podamos cometer es importante para poder corregirlos y aprender.
  • Juguetes Que se Pueden Romper: Se puede poner a prueba y aplicar lo aprendido construyendo sistemas con exigencias reales pero no críticos, tal vez para uso propio.
  • Usa La Fuente: Para reconocer buen código hay que haberlo visto antes. Leer código de otros permite entender la forma de desarrollar de otras personas y adquirir los mejores hábitos de ellas.
  • Reflexiona Mientras Trabajas: Periódicamente se debe reflexionar sobre las prácticas usadas y analizar si se pueden mejorarlas o cambiars por otras mejores. Como equipo, esto puede traducirse en reuniones retrospectivas sobre la marcha del proyecto.
  • Guarda Lo Que Aprendes: Guardando constancia de cómo se ha resuelto un problema se ahorra tiempo en el futuro cuando se vuelva a presentar.
  • Comparte Lo Que Aprendes: Esto permite la comunicación con otros desarrolladores con los mismos problemas.
  • Crea Ciclos de Realimentación: Sólo alguien distinto de nosotros puede evaluar de forma objetiva lo que estamos haciendo bien o mal.
  • Aprende Cómo Fallas: Identificar nuestros fallos permite afrontarlos o, si el esfuerzo fuese desmedido, aceptarlos y reconocer las tareas para las que no somos aptos.

Construye Tu Currículo

La necesidad de seguir aprendiendo será continua. Estos nuevos conocimiento s irán definiendo nuestro propio currículo.
  • Lista de Lecturas: Existe una enorme cantidad de libros que pueden resultar interesantes. Mantener una lista de estos libros ayuda a priorizar y registrar lo que leemos y hacerla pública la deja abierta a sugerencias por parte de otras personas.
  • Lee Constantemente: Leer de forma periódica permite adquirir un conocimiento aceptable de la tecnología y diferenciarse de la mayoría de quienes nos rodean.
  • Estudia Los Clásicos: Las referencias a clásicos del desarrollo del software son constantes y es importante leer estos libros para entender las referencias de otras personas.
  • Excava Más Profundo: El conocimiento superficial de varias tareas no es suficiente. Habrá casos en que haya que profundizar en una herramienta o lenguaje concreto en busca de la solución a un problema.
  • Herramientas Familiares: Para que los proyectos puedan desarrollarse según lo estimado no se puede innovar siempre. Hay que mantener un conjunto de herramientas constantes en las que basarse.

Personalmente, este libro me parece muy inspirador y me ha dado un montón de ideas sobre cómo afrontar mi carrera como desarrollador de software, al verme reflejado en muchas de las situaciones que en él se describen.

Los patrones que presenta, en ciertas ocasiones parecen sencillas e incluso evidentes. La complejidad está en saber combinarlos y aplicarlos en nuestro día a día.

lunes, 13 de junio de 2011

Apuntes de XP

Recientemente he estado leyendo el libro de Kent Beck "Extreme Programming Explained" y, lo que presento es este post, es el resumen que he hecho sobre el mismo.

XP es una metodología ligera para el desarrollo proyectos de software con requisitos variables o poco definidos. Pone énfasis en el código e introduce prácticas que permiten entregar el software a tiempo, reducir el riesgo, mejorar la respuesta al cambio, aumentar la productividad y hacer más divertido el trabajo en grupo.

El problema del desarrollo de software

El principal problema del desarrollo de software es el riesgo. XP propone soluciones para luchar contra los distintos tipos de riesgo que pueden existir en un proyecto:
  • Las iteraciones cortas permiten planificar mejor y reaccionar a los cambios.
  • Desarrollar primero las tareas de mayor valor acelera el retorno de la inversión.
  • Asegurar la calidad introduciendo pruebas automáticas permite hacer modificaciones de forma segura.
  • Probar constantemente el sistema permite encontrar defectos más rápido.
  • Involucrar al cliente mejora la especificación del sistema y las expectativas sobre él.
  • Otorgar mayor responsabilidad y respeto al programador reduce su frustración. Promueve el espíritu de equipo y crea un mejor ambiente de trabajo.

La Economía del Software

Se debe obtener el mayor valor haciendo la mínima inversión para obtener beneficios de la forma más rápida e incrementar la vida del proyecto.

Para ello, se estudian las distintas opciones de cada funcionalidad del proyecto (implementarla ya, descartarla o posponerla) y se calcula su valor según el beneficio original, el precio de la opción, el beneficio si se ejecuta la opción, el periodo de tiempo durante el que se puede ejecutar la opción y la certeza en el valor final.

Las cuatro variables que permiten gestionar un proyecto son: coste, tiempo, calidad y alcance. El alcance es la variable más potente. XP reduce el riesgo de retrasar el desarrollo de una funcionalidad y juega con el alcance centrándose en realizar aquello que está definido. Se puede modificar el alcance a medida que el proyecto avanza y el conocimiento sobre el producto deseado aumenta.

Se asume que el coste que implica un cambio en el proyecto aumenta exponencialmente según avanza el proyecto. Sin embargo, combinando tecnología y buenas prácticas, se puede conseguir un aumento prácticamente lineal del coste del cambio. Esta es la premisa principal de XP, según la cual las decisiones se deben retrasar hasta obtener la mayor certeza.

Para reducir el coste del cambio se debe usar un diseño simple, pruebas automáticas que puedan ejecutarse en cualquier momento y refactorización para modificar el diseño cuando sea necesario. Se pueden así realizar ajustes durante el desarrollo, de la misma manera que se realizan correcciones al volante cuando se conduce.

Valores, principios, actividades y prácticas de XP

Los 4 valores que dirigen el proceso de XP son:
  • Comunicación: la información debe fluir entre el equipo.
  • Sencillez: hay que hacer las cosas de la forma más sencilla posible.
  • Realimentación: hay que conocer el estado del proyecto en todo momento.
  • Coraje: ser capaz de tomar decisiones difíciles cuando sea necesario.

Los valores anteriores inspiran los principios que guían a XP:
  • Realimentación rápida: acelerando es el aprendizaje.
  • Sencillez: resolver los problemas de forma sencilla.
  • Cambio incremental: los cambios son más sencillos si se realizan poco a poco.
  • Abrazar el cambio: abierto a lo que depare el futuro.
  • Trabajo de calidad: si el trabajo no se hace bien, se pierde la motivación.
  • Enseñar a aprender: demostrar todo aquello sobre lo que no haya certeza.
  • Pequeña inversión inicial: manejando el alcance, no hacen falte grandes presupuestos.
  • Jugar para ganar: la confianza en la propia capacidad ayuda al éxito del proyecto.
  • Experimentos concretos: probar cada decisión para asegurar que es correcta.
  • Comunicación abierta y honesta: hablar del proyecto de forma directa y sincera.
  • Trabajar con los instintos de las personas, no contra ellos: a la gente le gusta hacer un buen trabajo. Hay que ayudar a que esto ocurra.
  • Aceptar la responsabilidad: A nadie le gusta que le digan lo que tiene que hacer. Es preferible que cada persona lo elija, pero como miembro del equipo, también debe asumir la responsabilidad de hacer lo que el equipo estime necesario.
  • Adaptarse de forma local: decidir qué cosas funcionan mejor en cierto entorno.
  • Viajar ligero: mantener pocas cosas, que sean simples y de valor.
  • Medida honesta: usar medidas significativas que reflejen el estado del proyecto.

Las actividades principales del proceso de desarrollo en XP son codificar, probar, escuchar y diseñar:
  • El código sirve para plasmar ideas y aprender sobre ellas a través de la comunicación.
  • El código puede ser probado para ver si cumple con lo esperado. Las pruebas automáticas permiten realizar estas pruebas una y otra vez asegurando el funcionamiento del sistema.
  • Para implementar las pruebas, el desarrollador escucha al usuario, que dice cómo debe funcionar el sistema, y al propio sistema, que da realimentación sobre el sistema y sus problemas.
  • A medida que el conocimiento evoluciona y se amplían las funcionalidades se revisa el diseño del sistema para poder satisfacer las nuevas necesidades sin afectar a las anteriores. El diseño consiste en organizar el sistema de forma que los cambios lo afecten lo menos posible.

Las actividades anteriores se realizan mediante las siguientes prácticas:
  • El Juego de la Planificación: en cada iteración los desarrolladores estiman las tareas y el cliente decide las prioridades, para seleccionar el conjunto de tareas a realizar.
  • Entregas cortas: entregar el mayor valor en producción lo más rápidamente posible.
  • Metáfora: se utiliza una historia sobre el funcionamiento del sistema, que comparte todo el equipo, para describir el funcionamiento del sistema y guiar el desarrollo.
  • Diseño simple: el sistema debe ser lo más simple posible en cada momento.
  • Pruebas: se crean pruebas unitarias y funcionales.
  • Refactorización: el sistema se reestructura constantemente, sin cambiar su comportamiento, para mejorar el diseño o la comunicación.
  • Programación en pareja: el código se escribe en pareja, aumentando la comunicación entre los miembros del equipo y otorgando realimentación inmediata.
  • Propiedad colectiva: cualquier miembro del equipo puede modificar el código.
  • Integración continua: el código se integra cada vez que se completa una tarea para mantenerlo estable.
  • Jornada de trabajo estable: XP supone un esfuerzo y se debe permitir el descanso necesario.
  • Contacto directo con el cliente: el cliente debe estar disponible en todo momento para responder preguntas sobre el sistema.
  • Estándares de codificación: se programa en base a reglas de codificación que mejoran la comunicación.

No son prácticas novedosas, sin embargo, al llevarlas a cabo de manera conjunta, unas se refuerzan a otras aumentando su potencia.

Gestión de proyectos en XP

La gestión del proyecto no puede ser cosa de una sola persona, nadie tiene conocimiento total del sistema. XP permite una estrategia de gestión descentralizada en la que el gestor sólo debe intervenir en situaciones que no se puedan resolver de forma distribuida.

A la hora de gestionar un proyecto debe existir tanto un perfil administrativo como uno técnico. El perfil administrativo se encarga de que se realice el Juego de la Planificación, de recoger métricas sobre el seguimiento del proyecto y mostrárselas a los interesados. El perfil técnico hace de consejero (coach). No toma decisiones técnicas, sino que ayuda a los de demás a tomarlas y colabora con ellos en el desarrollo. También es el encargado de velar por la buena marcha del proceso de desarrollo.

A la hora de realizar la planificación hay que separar las responsabilidades. Negocio decide el ámbito del proyecto y de las iteraciones, y las prioridades de las tareas. Desarrollo informa sobre las estimaciones de las funcionalidades y las alternativas técnicas. Desarrollo decide el proceso que va a seguir para el desarrollo y las prácticas que llevará a cabo.

La planificación se basa en las estimaciones que los desarrolladores hacen sobre las tareas que el cliente ha priorizado. Sólo se debe planificar en detalle la iteración actual, pero se puede planificar a largo plazo con menor detalle el resto de las tareas.

El objetivo de la planificación es el de maximizar el valor entregado al cliente en cada iteración. Para ello, el equipo de desarrollo deberá invertir lo mínimo posible poniendo el mayor valor en producción lo más rápidamente posible.

La planificación se realiza en las siguientes fases:
  • Exploración: trata de descubrir lo que debe hacer el sistema. Se escriben historias que describen lo que debe realizar el sistema, se estiman y, si fuesen demasiado grandes, se dividen en historias más pequeñas.
  • Compromiso: las historias se priorizan. A partir de la velocidad de desarrollo del equipo se elije el alcance del proyecto según el tiempo que se disponga.
  • Dirección: se realizan iteraciones de alcance restringido durante las que se pueden detectar nuevas historias que pueden ser incorporadas a la planificación. Se vuelve a estimar teniendo en cuenta la velocidad obtenida durante las iteraciones anteriores.

Desarrollo en XP

Durante el desarrollo, las prácticas principales son la integración continua, la propiedad colectiva del código y la programación en parejas.

La integración continua consiste en que los desarrolladores integren sus cambios cada pocas horas, manteniendo la corriente principal del desarrollo en sincronía. Gracias a las pruebas automáticas se verifica en todo momento que el sistema sigue funcionando, y gracias a la refactorización se transforma el sistema en elementos cada vez más pequeños e independientes, de forma que los cambios a realizar están mucho más localizados.

La propiedad colectiva del código hace que cualquiera pueda modificar cualquier parte del código cuando lo necesite. Para ello es preciso atenerse a unas reglas de codificación comunes que faciliten la comprensión del código a todo el equipo.

La programación en pareja obliga a programar, analizar, diseñar y probar de forma conjunta. Esto favorece la comunicación y el aprendizaje. Uno de los miembros se centra en programar, mientras que el otro piensa de una manera más estratégica. Hace que la gente se mantenga fiel a las prácticas de XP, puesto que hay cierta supervisión.

La disposición del entorno de trabajo debe favorecer el trabajo en equipo. Es importante tener espacios comunes donde el equipo pueda reunirse a debatir o donde programar en pareja.

Diseño en XP

En XP se procura tener en todo momento un diseño lo más simple posible. Se realiza una inversión inicial pequeña para proporcionar valor rápidamente, haciendo cosas simples que permitan evolucionar el diseño fácilmente.

El diseño comienza por el desarrollo de pruebas que verifiquen lo que tiene que hacer el sistema. Se irán desarrollando nuevas pruebas que verifiquen más requisitos. Si en el proceso se ve la posibilidad de simplificar lo ya hecho, hay que hacerlo. Así, la primera vez que nos encontremos con un problema, lo solucionaremos de la manera más rápida posible. Las siguientes veces, se trata de flexibilizar la solución para acomodarla a nuevas necesidades.

Lo más simple posible es aquello que comunique su función, no contenga código duplicado y tenga el menor número de clases y métodos.

En el diseño se pueden usar gráficos, pero hay que ser conscientes de que entrañan el riesgo de que la funcionalidad del sistema que definen pueden ser comprobada a través de ellos. Se pueden usar gráficos en los primeros pasos de un diseño y luego convertir el diseño a código para eliminar este riesgo lo antes posible.

La arquitectura del sistema se expresa a través de una metáfora que permite hablar sobre el sistema. Para obtener esta arquitectura se recoge un conjunto básico de historias y se cambiará a medida que el conocimiento sobre el sistema aumente.

Pruebas en XP

XP propone realizar las pruebas a la vez que el desarrollo. Antes de codificar una funcionalidad se debe crear una prueba que valide su funcionamiento. Como las pruebas son automáticas se pueden ejecutar en cualquier momento y verificar que el sistema sigue funcionando.

Las pruebas permiten aprender sobre el sistema. Son realizadas por los desarrolladores, pero también por los clientes, que escriben pruebas que verifican si el sistema cumple con el funcionamiento descrito en sus historias de usuario.

Comenzar con XP

A introducir XP en un nuevo entorno se debe hacer paso a paso, comenzando por aquello que suponga el mayor problema. Normalmente se empieza por las pruebas y la planificación. Es importante reorganizar el espacio de trabajo para favorecer la comunicación desde el primer instante.

Hay que cambiar la estrategia en las siguientes áreas:
  • Pruebas: las pruebas otorgan confianza en el código. Hay que ir introduciendo pruebas en el código existente a medida que sea necesario modificarlo por cualquier razón.
  • Diseño: XP provocará un aumento en la refactorización. No se debe realizar al mismo tiempo que se añade una nueva funcionalidad. Los objetivos de la refactorización deben ser identificados cuanto antes y ser abordados antes de avanzar en funcionalidades nuevas.
  • Planificación: se debe educar al cliente en las nuevas reglas que guían la planificación y convencerle para que escriba historias de usuario.
  • Gestión: una de las partes más difíciles es el cambio de mentalidad sobre la gestión del equipo. El gestor debe de dejar de tomar las decisiones que son responsabilidad de los programadores. Los programadores deben asumir la responsabilidad de su trabajo.
  • Desarrollo: se debe aprender a desarrollar en pareja y el entorno debe facilitarlo.

Ciclo de vida del proyecto en XP

El ciclo de vida de XP trata de adelantar cuanto lo máximo posible la puesta en producción de valor para el cliente, aunque sólo sea una parte de la funcionalidad. A partir de ahí, se irá incrementando el valor en las siguientes iteraciones.

En una primera fase de exploración se debe analizar la viabilidad del proyecto y estudiar su utilidad y si tenemos las capacidades necesarias para llevarlo adelante. Esta fase termina en el momento en que existen suficientes historias de usuaria para una puesta en producción.

En esta fase se explora la arquitectura del sistema a través de varias alternativas. Se exploran las tecnologías a usar para conocer sus límites y rendimiento. El cliente también debe aprender en esta etapa a escribir historias de usuario. Para ello debe recibir realimentación por parte de los desarrolladores hasta llegar al nivel adecuado.

En la fase de planificación se recogen las historias más valiosas que puedan servir para una primera puesta en producción y se trata de estimar la fecha. A partir de este punto se realizan varias iteraciones. En la primera iteración se tratará de asentar la arquitectura del proyecto y en las subsecuentes iteraciones se irán incluyendo las historias de usuario seleccionadas.

Posteriormente se pasará producción, para lo cual puede ser necesario hacer pruebas de rendimiento del sistema y refinarlo en base a los resultados.

A partir de este momento el proyecto está en la fase de mantenimiento, que es el estado natural de un proyecto en XP. En esta fase se incorpora nueva funcionalidad manteniendo la existente, manejando las altas y bajas en el equipo.

Cada nueva versión necesitará de una fase de exploración que puede implicar la refactorización de parte del sistema. También hay que responder a los problemas que puedan surgir en el funcionamiento normal del sistema, lo que puede disminuir la velocidad del equipo para aportar nueva funcionalidad.

El día en que el cliente ni tiene nuevas historias es el momento en que se debe redactar el funcionamiento del sistema para la posteridad. Este es el fin del proyecto. También se puede llegar al fin del proyecto porque éste no reporte la rentabilidad esperada.

Roles en XP

En un equipo XP existen los siguientes roles:
  • Programador: programa y comunica sobre lo que hace. Debe buscar la sencillez del sistema. Es miembro de un equipo que le ayuda a mejorar.
  • Cliente: decide lo que debe hacer el sistema en función de la información de que dispone y la que le da el avance del proyecto. También se encarga de escribir las historias que debe cumplir el sistema y crea pruebas para comprobarlo.
  • Probador (tester): ayuda al cliente a escribir y ejecutar las pruebas funcionales. Puede ser un programador.
  • Rastreador (tracker): recoge las estimaciones, verifica su cumplimiento e informa al equipo.
  • Consejero (coach): vela por el cumplimiento del proceso. Su trabajo disminuye a medida que el equipo madura y se hace responsable del proceso.
  • Consultor: aporta conocimiento técnico al equipo en un determinado momento, pero no resuelve el problema por ellos.
  • Jefe: responde a las necesidades del equipo para la realización el proyecto. Debe pedir explicaciones al equipo sobre su forma de trabajar y, si algo no tiene sentido, hacerlo saber para que el equipo lo corrija.

Observaciones generales

En XP es necesario realizar moderadamente bien el conjunto de todas las prácticas para empezar a obtener resultados notables. Esto es porque las prácticas se apoyan unas en otras y es esta conjunción lo que asegura el buen funcionamiento de XP.

Las prácticas de XP son simples, pero es difícil realizarlas de forma conjunta. Cuando hay problemas, se tiende a relajar la aplicación de las prácticas más exigentes y el proceso se desequilibra si no se corrige rápidamente. También se requiere humildad y coraje para reconocer lo que uno no sabe y preguntar por ello. Hay que estar dispuesto a colaborar.

En ciertas condiciones hay que descartar XP, como cuando tratamos con equipos grandes, clientes desconfiados o tecnología que no soporta el cambio. Estos casos dificultan o imposibilitan la aplicación de algunas de las prácticas de XP, desestabilizando el proceso de desarrollo.

Esto último es muy importante, puesto que por más que nos gusten las ideas que XP propone, puede ser contraproducente adoptar XP a ciegas en determinados entornos en los que pueden llevar al caos. Sin embargo, aunque introducir el conjunto de prácticas de XP no sea una buena opción, algunas de ellas son recomendables en sí mismas en el desarrollo de software, como la integración continua (hasta cierto grado), el uso de estándares de codificación y las pruebas auto

Referencias:
  • Extreme Programming Explained (Kent Beck)


viernes, 18 de marzo de 2011

The Mythical Man-Month

Este libro, un clásico de referencia sobre la ingeniería del software, es una colección de ensayos en los que el autor, Frederick P. Brooks, expone sus experiencias como gestor en un gran proyecto de desarrollo.

Aunque se trata de un libro de hace más de 30 años, que refleja experiencias de un proyecto realizado 20 años atrás, este libro sigue resultando valioso. En lugar de centrarse en los ya obsoletos problemas técnicos del proyecto, se centra en los problemas organizacionales de los mismos, y en cómo gestionar a las personas involucradas en ellos.

A continuación, haré un resumen de cada uno de los capítulos que forman parte de este libro:

1. El pozo de alquitrán

Los proyectos de software con como un pozo de alquitrán, en el cual uno se adentra sin darse cuenta y, cuando comienzan los problemas, luchar contra ellos sólo hace que uno se hunda más.

No hay razón aparente para la dificultad de los proyectos, sino un cúmulo factores. Para conocer la naturaleza de este problema es necesario identificar la finalidad de la programación y las satisfacciones y dificultades que entraña.

Existen distintos productos de software en función su usuario final que requiere más o menos diseño, integración o pruebas, e implican diferentes grados de esfuerzo. Un producto que vaya ser usado por otros supone el triple de esfuerzo que un programa de uso privado. Un programa que va a ser usado dentro un sistema supone el triple de esfuerzo que uno que va a ser usado de forma independiente. Estas implicaciones son independientes entre sí, por lo que un producto de software que va a ser usado por sistema cuesta nueve veces más esfuerzo que un programa independiente de uso privado.

La programación proporciona la satisfacción de crear cosas complicadas que sean de utilidad para los demás. Es algo tan maleable que permite hacer casi cualquier cosa. Sin embargo, se sufre la necesidad de perfección y el estar trabajando con objetivos impuestos por otros.

2. El mito del mes-persona

La principal causa de fracaso de los proyectos de software es la desviación respecto a la planificación estimada. Esto se debe al optimismo a la hora de estimar por parte de los programadores, que no tienen en cuenta que las ideas iniciales pueden ser incompletas e incluso erróneas. Además se carece de datos fiables sobre las estimaciones, lo que hace difícil justificar estimaciones que vayan en contra de los intereses del gestor o del propio cliente.

A la hora de estimar, se suele caer en el error de creer que personas y meses son intercambiables. Se olvida que al añadir más personas a una tarea se incrementa el esfuerzo total necesario debido a la necesidad de reorganizar las tareas, de formar a los nuevos miembros y al aumento de la intercomunicación.

La ley de Brooks dice que: “Añadir gente a un proyecto retrasado lo retrasa más”.

Como regla general, Brooks propone repartir el tiempo de un proyecto de la siguiente forma: 1/3 para diseño, 1/6 para la codificación, 1/4 para las pruebas de componentes y 1/4 para las pruebas de sistema.

3. El equipo de cirujía

Los buenos programadores son mucho más productivos que los malos. Además, los equipos, cuanto más pequeños sean menos los problemas de intercomunicación tendrán.

Hay que favorecer el trabajo de los buenos programadores simplificando la comunicación dentro del equipo.

Los equipos pequeños son demasiado lentos para realizar grandes sistemas y, por otra parte, un equipo de mucha gente es costoso, lento e ineficiente, y produce sistemas que no están conceptualmente integrados.

Una solución es tener un equipo organizado en torno a un programador jefe a quien da apoyo, de la misma manera que un equipo de cirugía. De esta forma se logra mantener la integridad de un producto de un número reducido de mentes con la ayuda de unos cuantos colaboradores, reduciendo la comunicación de forma radical.

4. Aristocracia, democracia, y diseño de sistemas

La consideración más importante en el diseño de un sistema es preservar la integridad conceptual. La integridad conceptual otorga mayor facilidad de uso, y permite construir y probar el sistema más rápidamente.
Para lograrla, el diseño debe ser fruto de una sola mente o de un grupo reducido de mentes que están de acuerdo. La separación entre arquitectura e implementación es la forma de conseguir esta integridad conceptual en sistemas grandes.

Alguien debe controlar los conceptos fijando la arquitectura, pero ello mejora la creatividad en la implementación, puesto que permite enfocar el esfuerzo.

De cualquier manera, arquitectura (requisitos), implementación (diseño) y realización (programación) pueden discurrir en paralelo.

5. El efecto del segundo sistema

Una buena comunicación puede dar al arquitecto una lectura adecuada de los costes y al constructor confianza en el diseño, sin difuminar la clara división de responsabilidades entre ellos.

Para que el arquitecto pueda influir con éxito en la implementación debe tener en cuenta que:
  • Es el constructor quien tiene la responsabilidad de la implementación. El arquitecto solo sugiere.
  • El arquitecto debe ser capaz de sugerir una forma de implementar lo que especifica, pero debe estar preparado para aceptar cualquier otra forma igualmente válida.
  • Debe escuchar las sugerencias del constructor para mejorar la propia arquitectura.

El segundo sistema es el más peligroso, porque se tiende a sobrediseñarlo. Asignar a priori valores de memoria y tiempo para cada función ayuda a detectar el sobrediseño durante la implementación.

6. Pasar la Palabra

A la hora de redactar el diseño de un sistema, por grande que sea, es mejor que lo hagan pocas personas (preferiblemente una) para que el resultado sea consistente.

Se necesita tanto una descripción formal que aporte precisión como una descripción en prosa que aporte claridad. Una de ellas debe ser la de referencia y la otra servir como apoyo. Utilizar una implementación como definición es una fuente de problemas.

Es importante registrar y publicar todas las consultas de los desarrolladores a los arquitectos.

7. ¿Por qué cayó la Torre de Babel?

Si no hay comunicación cada uno hace sus propias suposiciones, provocando retrasos, desajuste funcional y errores en el sistema. Los equipos de deben comunicar de todas las formas posibles, tanto informal como formalmente.

Se debe especificar cuanto antes el conjunto de documentos a través de los cuales se producirá la comunicación formal en el proyecto. Esta documentación debe estar al alcance de cualquier miembro del equipo. Se debe actualizar constantemente y resaltar los cambios realizados respecto a la última versión.

Sin embargo, Parnas propone que nadie debe ver las interioridades de ninguna otra parte que no sea aquella en la que se está trabajando. Sólo se deben publicar las interfaces.

La organización debe encargarse de reducir la cantidad de comunicación y coordinación necesaria, mediante la división de tareas y la especialización en las funciones.

En cada proyecto se deben establecer dos roles, el productor y el arquitecto o director técnico, que requieren de distintas habilidades.

8. Apuntando para disparar

No se puede estimar el esfuerzo total de un proyecto estimando el tiempo de codificación y extrapolándolo a las demás tareas.

Tampoco sirve extrapolar la construcción de pequeños sistemas a proyectos de sistemas. El incremento en el esfuerzo es una potencia del tamaño del proyecto.

Hay estudios que dicen que los programadores sólo dedican el 50% de su tiempo a la programación y el resto a otras tareas.

Otros estudios sobre la productividad indican que ésta se mantiene constante en términos de secuencias elementales. La productividad se incrementa por 5 cuando se pasa a usar un lenguaje de alto nivel.

9. 10 libras en una bolsa de 5

Además del tiempo de ejecución, el espacio de memoria que ocupa un programa es su coste principal. Se deben establecer límites de tamaño en cada función para que los costes no se disparen.

Durante la implementación, los arquitectos deben velar por la continuidad de la integridad del sistema.

Para poder hacer un buen balance entre tiempo y espacio, el equipo debe estar entrenado en las peculiaridades del lenguaje de programación y de la máquina que se están usando.

Todo proyecto de programación necesita una librería de componentes estándar.

Los programas sencillos y rápidos son resultado de la innovación estrategia más que de una buena táctica. Esto se puede lograr a través del uso de un nuevo algoritmo o de rehacer la representación de los datos.

10. La hipótesis documental

Sólo unos pocos de todos los documentos del proyecto son el eje sobre el que gira toda la gestión del proyecto: objetivos, manual, calendario, presupuesto, diagrama de organización y situación física de los miembros del equipo.

El gestor debe formalizar este conjunto de documentos. Mantener estos documentos proporciona un mecanismo de vigilancia y alarma.

El gestor debe encargarse de que todo el mundo vaya en la misma dirección a través de la comunicación, y no tomando todas las decisiones. Solo una pequeña parte de las tareas del gestor están relacionadas con información que está fuera de su mente.

11. Planea deshacerte de uno

Cuando se pasa de un entorno protegido a uno de producción es necesario un paso intermedio de prueba. Al probar el primer sistema construido se suele comprobar que éste es demasiado lento, grande o difícil de usar, y que debe ser rediseñado.

Este rediseño se puede hacer de golpe o pieza a pieza, pero habrá que hacerlo de todas formas. Si se entrega el primer sistema, esto será en detrimento del usuario y del desarrollador que lo debe mantener. Por tanto, debe incluirse en la planificación el rediseño de la primera solución.

Las necesidades del cliente y la percepción de las mismas cambiarán a medida que avanza el proyecto. La maleabilidad del software hace que los requerimientos no dejen de cambiar porque los cambios se presuponen fáciles. Algunos de estos cambios son válidos y hay que estar preparados para poder afrontarlos. Las técnicas que nos permiten hacer esto son conocidas pero no son usadas.

Los esfuerzos de los desarrolladores son tentativas de satisfacer los requisitos, sujetos a modificaciones y, por tanto, hace que sean reacios a documentarlos.

Para acomodar una organización al cambio los miembros de los equipos de ben ser intercambiables y reubicables, tanto en la parte de gestión como en la técnica. Una posible solución es la propuesta organizar el equipo en torno a un programador jefe.

El mantenimiento del software consiste principalmente en reparar defectos de diseño, añadir nuevas funcionalidades y adaptarse a los cambios en el entorno o la configuración. El coste de mantenimiento de un programa es aproximadamente el 40% del coste de su desarrollo.

El número de errores encontrados aumenta con el número de usuarios. El número de errores a lo largo de la vida de un proyecto primero aumenta y luego se estabiliza.
Al corregir un error existe una alta probabilidad de introducir otros, por los que se debe disponer de casos de prueba que aseguren que esto no se produce.

Durante la vida de un proyecto el número de módulos se incrementa de forma lineal, pero el número de módulos afectados por las modificaciones se incrementa de forma exponencial. Toda corrección tiende a romper la estructura e incrementa la entropía y el desorden del sistema. Inevitablemente, se llegará a un punto donde la mejor solución es el rediseño.

12. Herramientas precisas

Todo proyecto necesita herramientas personalizadas, por lo que el gestor debe establecer la filosofía para establecer los recursos necesarios para su construcción.

Es necesario instrumentalizar las máquinas sobre las que se va probar para poder extraer resultados de ellas. A la hora de probar en la máquina es mejor repartir periodos separados de tiempo para los distintos miembros.

La documentación voluminosa introduce mayor complejidad al proyecto.

Se debe construir de forma temprana un simulador de rendimiento que analice el programa desde fuera, y hay que prestar atención a lo que dice.

Las mejores herramientas que existen actualmente (1975) son los lenguajes de alto nivel (aumentan el rendimiento y facilitan la depuración) y la programación interactiva (reduce el tiempo de respuesta en la depuración).

13. El todo y las partes

Una buena arquitectura hace que un sistema sea más fácil de usar, más fácil de construir y que tenga menos errores. Antes de empezar la construcción se debe entregar las especificaciones al grupo de test, que debe comprobar si está completa y es clara.

Diseñar de lo general a lo específico refinando la especificación paso a paso reduce los errores y permite volver atrás cuando sea necesario. Hay que usar una notación de tan alto nivel como sea posible en cada paso.

La programación estructurada es el diseño de programas formados por un conjunto reducido de estructuras de control.

Se demuestra que en la primera interacción de depuración se un progreso hace tres veces mayor que en las subsecuentes. La depuración de un sistema siempre lleva más tiempo de lo planeado.

Se debe empezar la depuración sólo cuando las piezas parezcan funcionar, en vez de adelantarlo a cuando se hayan detectado errores pero sigan sin ser corregidos.

Merece la pena construir código de apoyo para la depuración, que puede llegar a ser el 50% del código a depurar.

Durante la depuración se deben añadir los componentes de uno en uno. Es mejor introducir grandes cambio de forma poco frecuente porque esto introduce menos inestabilidad.

14. Vislumbrando una catástrofe

Los proyectos se retrasan día a día. Esto es difícil de reconocer, de prevenir y de arreglar. Para poder controlar un proyecto es necesario tener un calendario con hitos concretos y bien definidos y fechas identificables.

Diversos estudios han demostrado que las estimaciones no suelen variar hasta el arranque del proyecto, posteriormente se van reduciendo las sobreestimaciones y, tres semanas antes del fin estimado del proyecto, lo que cambian son las infraestimaciones.

La planificación de ruta crítica permite identificar que desviaciones son las que realmente importan. Se debe identificar esta ruta crítica lo antes posible.

Todo jefe necesita ser alertado de las excepcionen que precisen de acciones correctoras y que se muestre el estado del proyecto para su conocimiento. Esto es difícil puesto que los subordinados temen la intromisión del jefe en sus responsabilidades. Se debe disponer de técnicas de revisión que ofrezcan el estado del proyecto a todo el mundo.

15. La otra cara

La documentación para el usuario es tan importante como el propio producto. Es importante también para el autor como memoria de lo que se ha hecho.

Por regla general no se entrega una buena documentación porque no se ha instruido a los desarrolladores en cómo hacerlo de forma efectiva y económica.

La documentación crítica para el usuario se debe establecer antes de la construcción ya que contiene decisiones de planificación básica.

A nivel de documentación interna, para mantener la documentación actualizada es necesario incorporarla junto con el código fuente. Se debe explicar por qué las cosas son como son, no solamente cómo están hechas. Los lenguajes de alto nivel permiten técnicas de auto-documentación dentro del propio código.

16. No hay balas de plata – Esencia y accidente

El autor compara el desarrollo de software con los hombres-lobo, gente normal que se convierte de repente en algo fuera de control que solo pueden ser eliminados con una bala de plata. Sin embargo, no existen balas de plata para el software que den una mejora de un orden de magnitud en su desarrollo, aunque existen innovaciones que de forma conjunta sí pueden ofrecer esta mejora.

La construcción de software implica tareas complejas, acomodar sus estructuras conceptuales, y tareas accidentales, representarlas en lenguajes de programación.

Las dificultades esenciales del desarrollo de software son la complejidad, conformidad, variabilidad e invisibilidad a la hora de acomodar las complejas estructuras conceptuales que implica.

El desarrollo de software también implica tareas accidentales, que son la representación en lenguajes de programación.

Las mejoras de los últimos tiempos han estado enfocadas en solucionar las dificultades accidentales a través de:
  • Lenguajes de alto nivel.
  • Tiempo de ejecución compartido.
  • Entornos de programación unificados.

Sin embargo, la mayor parte del tiempo se dedica a acomodar estructuras conceptuales abstractas de gran complejidad y, por tanto, es esta tarea esencial la que se debe atacar. Se han propuesto las siguientes formas:
  • Avances en lenguajes de alto nivel.
  • Programación orientada a objetos.
  • Inteligencia artificial.
  • Sistemas expertos.
  • Programación automática.
  • Programación gráfica.
  • Verificación de programas.
  • Entornos y herramientas de trabajo.
  • Estaciones de trabajo.

Estas alternativas, aunque introducen mejoras, no han alcanzado los niveles esperados. Es necesario atacar la esencia del software elevando el nivel de abstracción que implica su desarrollo. Los avances más prometedores en este sentido son:
  • Comprar en lugar de desarrollar.
  • Refinado de requisitos y prototipado rápido.
  • Desarrollo incremental.
  • Encontrar y educar a los mejores diseñadores.


17. “No hay balas de plata” revisado

En este capítulo el autor rebate las críticas recibidas al artículo presentado en el capítulo anterior “No hay balas de plata”. Sobre todo aquellas que van contra el principal argumento del mismo (que no hay ningún avance en el mundo del software que pueda dar por sí solo una mejora de n orden de magnitud), para presentar la propia bala de plata de cada investigador de turno.

El autor se reafirma en la idea de que la complejidad es la mayor dificultad inherente al software, aunque la mayor parte de esta complejidad se suele deber a problemas de organización de la propia realidad que se intenta modelar. Esta complejidad se puede atacar aumentando el nivel de abstracción de los elementos utilizados y manteniendo en todo momento sistemas que funcionen y que incorporen nuevas funcionalidades de forma incremental.

Se introduce el enfoque a la calidad, que aumentaría de la productividad reduciendo el tiempo dedicado a la corrección de errores.

La productividad también aumenta con mejores equipos y herramientas de trabajo y el uso de la programación orientada a objetos (que promueve la modularidad, la encapsulación, la herencia y el tipado de datos). Aunque se lamenta de la poca introducción de la orientación a objetos y la reutilización.

18. Proposiciones de “El mito del mes-persona”

Este capítulo muestra una recopilación de las afirmaciones expuestas en el libro a modo de resumen.

19. “El mito del mes-persona” 20 años después

Este capítulo es una revisión del libro hecha tras 20 años por el propio autor. En ella expone los argumentos que mantiene tras este tiempo y aquellos en los que ha cambiado de opinión.

El autor mantiene que la integridad conceptual es el factor más importante para la facilidad de uso de un sistema. Esta integridad conceptual suele lograrse cuando el producto ha sido diseñado por una única persona, pero este enfoque no es productivo para grandes sistemas que sufren presiones competitivas.

En estos entornos es necesario tener un arquitecto (o un grupo de ellos organizados de forma jerárquica) que se encargue de la integridad conceptual de todos los aspectos del producto percibibles por el usuario. Además sus tareas estarán separadas de la implementación.

Otro peligro que se recalca en el desarrollo de sistemas es el de cargarlos de funcionalidades de mínimo valor que compliquen el diseño y el uso de los mismos. Se debe ser consciente de quienes son los usuarios y ponderar las nuevas capacidades que se introducen en función del mayor valor ofrecido al mayor número de usuarios.

En lo que sí cambia de opinión es en el consejo de que el primer sistema implementado deba ser desechado. Esto era una consecuencia de la aplicación del desarrollo en cascada, que asume que los pasos del proyecto avanzan uno a continuación de otro, y supone que todos se realizan de forma correcta. El sistema se construye de una sola vez y se asume que todos los errores se introducen en la implementación.

Un modelo de desarrollo incremental permitiría el refinamiento progresivo del sistema. Se puede construir un sistema inicial de funcionalidad limitada y, a partir del cual hacer crecer el sistema introduciendo nuevas funcionalidades. De esta forma se pueden adelantar las pruebas del sistema y se puede entregar un sistema que funciona cuando sea necesario, aunque de funcionalidad reducida.

En cuanto al ocultamiento de información que Parnas proponía y del que el autor desconfiaba, éste se retracta y asume que los programadores son más efectivos si no están expuestos a las interioridades de módulos que no son de su propiedad.

El avance en el desarrollo del software se producirá al aumentar el nivel de abstracción de los elementos que se utilizan. En este sentido se han dado los siguientes pasos:
  • La definición del ocultamiento de información de los módulos hecha por Parnas.
  • La aparición de los tipos de dato abstractos de los que pueden derivar otros objetos.
  • La programación orientada a objetos.

El hecho de disponer de clases diseñadas y probadas para ser reutilizadas es lo que permitiría elevar dicho nivel de abstracción.

La Ley de Brooks (añadir más gente a un proyecto retrasado lo retrasa más) ha sido refinada gracias a varios estudios, aunque se mantiene como una buena aproximación a la realidad. En realidad, añadir más gente a un proyecto retrasado aumenta su coste, pero no siempre lo retrasa.

Otra idea que se ha reforzado durante este tiempo es la importancia de las personas que forman parte de los proyectos, y cómo los factores humanos deben ser tomados en consideración. Es importante dar responsabilidades a la gente y no centralizar todo en una única entidad, puesto que esto fomenta la creatividad.

Se hace eco del avance en el campo de la informática y de la explosión del número de ordenadores. Esto ha cambiado la forma de desarrollar el software y creado una nueva industria, los programas empaquetados.

Respecto al futuro de la ingeniería del software, presume que, a través del conocimiento, será capaz de lidiar en el futuro podrá con la complejidad del comportamiento humano y los aspectos no lineales que introduce.

miércoles, 5 de enero de 2011

Clean Code

Durante los últimos meses he estado leyendo y releyendo el libro “Clean Code: A Handbook of Agile Software Craftmanship” de Robert Martin. En este libro, el autor trata por qué hemos de escribir código limpio y cómo hacerlo.


Código Limpio. Este tema es algo que me interesa particularmente en estos momentos. Cuando eres un desarrollador con responsabilidades puntuales dentro de un proyecto y sin relevancia ninguna en cuanto a las decisiones que toma el arquitecto de turno, lo único importante es quitarte el muerto de encima cuanto antes para evitar las típicas preguntas de: ¿Cómo vamos (¿Vamos? Pero si lo estoy haciendo sólo yo…)? ¿En qué estado está esta tarea? ¿Qué pongo en el Project, 80%, 90%...? ¿Podemos hacer la entrega ya?

Sin embargo, cuando tienes cierto grado de responsabilidad sobre un proyecto y te interesa que su evolución no se complique es importante hacer entender a los miembros del equipo que la responsabilidad de cada uno va más allá de quitarse el muerto de encima, sino que cada uno debe hacer lo mejor que pueda por tener un código lo más comprensible posible. Al código comprensible es a lo que Robert Martin llama código limpio.

Este libro está dividido en tres partes:
  • En una primera parte, se explica qué es el código limpio y cómo llegar a él.
  • La segunda parte son ejemplos más complejos de refactorización de código en busca de código más limpio.
  • La tercera parte es una recopilación de pistas que ayudan a encontrar código que no está todo lo bien que debería estar y que debería ser refactorizado.

En este resumen del libro me voy a centrar en la primera parte de él, puesto que la segunda y la tercera parte son bastante directas.

Capítulo 1 - NOMBRES

La lectura del código es la actividad a la que se dedica más tiempo, incluso para escribir nuevo código hay que leer lo que ya hay escrito. Un mal código puede llegar a arruinar un proyecto, dificultando las modificaciones y alargando los plazos de entrega. El único responsable de ello es el programador que no se toma en serio su trabajo y no es profesional.

Para evitar estas situaciones se debe hacer un código limpio, pero, ¿qué es código limpio?. Según varios autores conocidos, las características del código limpio son:
  • Fácil de leer, expresivo y sencillo.
  • Probado mediante tests automáticos.
  • Hace una única cosa.
  • No existe duplicidad en él.
  • Utiliza el menor número de elementos posibles.
  • Realiza abstracciones de elementos similares.

Para conseguir un código limpio se debe prestar atención a cada uno de los elementos que componen un sistema a nivel de código: variables, funciones, clases, comentarios, tests y el propio sistema. Además existen una serie de aspectos, como son la diferenciación entre objetos y estructuras de datos, el comportamiento en los límites del sistema, el manejo de errores y la concurrencia que afectan directamente al código y a su comprensión. Capítulo a capítulo se va analizando cómo mejorar en cada uno de estos elementos

Tembién habla de la regla del Boy Scout “Hay que dejar el código mejor de cómo lo encontraste”. Esto va en la línea de la mejora continua del código y de la imposibilidad de hacerlo perfecto a la primera.

Capítulo 2 - NOMBRES

Un buen nombre da mucha más información que cualquier otra cosa. Para conseguir buenos nombres hay que usar nombres descriptivos y claros. Deben ser legibles y evitar codificaciones complejas.

Capítulo 3 - FUNCIONES

Una buena función es aquella de la que se puede inferir su comportamiento de un solo vistazo. Para ello deben ser cortas, hacer una única cosa y mantenerse dentro del mismo nivel de abstracción.

Es importante usar buenos nombres y reducir al mínimo el número de argumentos. Pero lo principal es eliminar toda la duplicidad.

Capítulo 4 - COMENTARIOS

Los comentarios no pueden maquillar el mal código. La necesidad de comentarios para aclarar algo es síntoma de que hay código mal escrito que debería ser rediseñado. Es preferible expresarse mediante el propio código.

Hay situaciones en las que los comentarios son apropiados, como cuando tratan de:
  • Aspectos legales del código.
  • Advertir de consecuencias.
  • Comentarios TODO.
  • Remarcar la importancia de algo.
  • Javadocs en APIs públicas.

Sin embargo, en el resto de los casos pueden llevar a confusión y deberían ser evitados.

Capítulo 5 - FORMATEO

El formateo afecta directamente a la legibilidad del código. El código se lee de arriba abajo y de izquierda a derecha. Los espacios verticales y horizontales permiten separar ideas y conceptos distintos.

Se debe mantener un estilo uniforme, y para ello se debe consensuar con todo el equipo las reglas de formateo. Las reglas elegidas no son tan importantes como el hecho de que todo el mundo se atenga a ellas.

Capítulo 6 - OBJETOS Y ESTRUCTURAS DE DATOS.

No todo se puede solucionar usando sólo objetos o sólo estructuras de datos. Hay que aprender a diferenciar en qué situaciones son más convenientes unos u otros. Los objetos esconden sus datos y exponen funciones que permiten manipularlos, mientras que las estructuras de datos exponen datos pero no tiene funciones que operen sobre ellos.

Capítulo 7 - MANEJO DE ERRORES

El código de manejo de errores oculta la verdadera funcionalidad del código. Hay que mantenerlo lo más separado posible de la lógica de negocio para no dificultar la comprensión de ésta última.

Para no complicar el manejo de errores hay que escribir los bloques try-catch-finally en primer lugar permite identificar los puntos del programa en los cuáles se puede producir una excepción. Se deben usar excepciones en lugar de códigos de retorno. Las excepciones deben proporcionar suficiente información sobre el error y el momento en que se ha producido. Además, las excepciones no comprobadas son menos invasivas a nivel de código.

También se debe evitar pasar null como parámetro de una llamada a un método o devolverlo como valor de retorno. Esto introduce complejidad adicional debido a las comprobaciones necesarias.

Capítulo 8 - FRONTERAS

Los sistemas dependen de paquetes de terceros o de componentes desarrollados por otros equipos. Hay que definir de forma clara la frontera entre el código y el exterior para poder acomodar de forma sencilla los futuros cambios, minimizando las partes de nuestro código que dependan de elementos externos.

Los tests ayudan a experimentar con el código externo viendo cómo puede cubrir nuestras necesidades. También permiten comprobar que las nuevas versiones de la librería siguen cumpliendo con nuestras necesidades.

Encapsular el conocimiento adquerido a través de los tests en un interfaz entre nuestro sistema y el código de terceros permite localizar en ún único punto las modificaciones debidas a posibles cambios en código que está fuera de nuestro control.

Capítulo 9 - TESTS UNITARIOS

En TDD los tests de seben escribir en primer lugar. Las 3 reglas de TDD son:
  • No se debe escribir código de producción hast que no se tenga un test unitario que falle.
  • No se debe escribir más de un test unitario que lo necesario para que éste falle.
  • No se debe escribir más código de producción uqe el necesario para que pase un tests unitario que fallaba.

Se debe dar la misma importancia al código de test que al de producción. Los tests permiten que el código de producción se pueda modificar sin temor a introducir nuevos errores, asegurando su mantenibilidad.

El número de assert por cada test debe ser lo más bajo posible. Se debe testear un único concepto en cada test, lo que permite ir aclarando los distintos conceptos progresivamente, mejorando el conocimiento sobre el código.

Las reglas FIRST sobre el código de test son:
  • Fast: Se deben ejecutar rápido y muy a menudo.
  • Independent: Las condiciones de un test no deben depender de un test anterior.
  • Repeteable: Se deben poder ejecutar en cualquier entorno.
  • Self-Validating: El propio test debe decir si se cumple o no, no debe hacer falta realizar comprobaciones posteriores al test.
  • Timely: Los tests se deben escribir en el momento adecuado, que es justo ante de escribir el código de producción, lo que permite escribir código fácilmente testeable.

Capítulo 10 – CLASES

Las clases se deben organizar situando en primer lugar las constantes públicas, después las variables estáticas privadas, variables de instancia privadas y, a continuación, los métodos. Los métodos privados se encuentran junto a los métodos públicos que los usan.

Se debe mantener la encapsulación de las clases. Las clases deben ser pequeñas y con un número reducido de responsabilidades. Se consigue una alta cohesión si todos los métodos de una clase hacen uso de todas sus variables privadas.

Las nuevas funcionalidades se deben de introducir extendiendo el sistema, no modificando el código existente. Las clases concretas contienen detalles de implementación y las clases abstractas expresan conceptos. La dependencia en clases concretas es un riesgo en caso de cambio. Según el principio de inversión de dependencia las clases deben depender de abstracciones.

Capítulo 11 – SISTEMAS

Es importante reconocer y separar las distintas responsabilidades de un sistema.

En primer lugar, se deben separar el proceso de construcción de un sistema de su uso. El proceso de construcción se encarga de crear y conectar entre sí los objetos necesarios para la ejecución de la aplicación. Se debe modularizar y separar de la lógica de ejecución, permitiendo una estrategia independiente para resolver las dependencias de la aplicación.

La inyección de dependencias permite que los objetos sólo se encarguen únicamente de la lógica de negocio. Un elemento contenedor se encarga de inyectar las dependencias de cada objeto de forma externa.

No se puede construir sistemas de forma correcta a la primera hay que ir implementando las historias de que se dispone, y después refactorizar y expandir el sistema para seguir implementando nuevas historias. TDD, refactorización y código limpio permiten esto a nivel de código.

A nivel de sistema, es difícil crecer de sistemas simples a sistemas complejos debido a las dependencias en la arquitectura que se usa. Para evitar estos problemas, se deben separar las distintos responsabilidades de un sistema. Para lograr esta separación se pueden usar proxys, frameworks AOP Java puros o aspectos AspectJ.

Se debe construir la lógica de la aplicación en base a POJOs a través de tests, e ir evolucionando de lo simple a lo complicado, interconectando los distintos aspectos necesarios.

Capítulo 12 – EMERGENCIA

Las siguientes 4 reglas dadas por Kent Beck permiten crear buenos diseños según se trabaja en ellos, conocer nuestro sistema y aplicar buenos principios de diseño:
  • Ejecutar todos los tests: Los tests verifican que el sistema se comporta según lo esperado. Al construir un sistema testeable se intenta que las clases sean simples y tengan un único propósito, y se trata de reducir el acoplamiento.
  • Eliminar la duplicación: La duplicación implica trabajo adicional, más riesgo y mayor complejidad.
  • Expresar la intención del programador: Usar un código lo más expresivo posible facilita el mantenimiento. Se deben escoger buenos nombres, funciones y clases pequeñas y tests bien escritos.
  • Minimizar el número de clases y métodos: Siguiendo las recomendaciones anteriores uno se puede exceder creando demasiadas clases pequeñas. Hay que tener cuidado y mantener un número reducido de clases.

Durante la refactorización de puede aplicar todo nuestro conocimiento para mejorar el diseño: aumentar la cohesión, reducir el acoplamiento, separar responsabilidades, reducir funciones y clase, escoger nombres mejores, etc.

Se dedicar tiempo a estas tareas después hacer que el software funcione. No se debe pasar a la siguiente tarea sin repasar lo hecho. Es imposible conseguir un código limpio a la primera, hay que repasarlo una y otra vez para conseguir mejorarlo progresivamente.

Capítulo 13 – CONCURRENCIA

La concurrencia es otro de los aspectos que pueden estar presentes en el código. Permite desacoplar lo que que ocurre de cuándo ocurre, y mejorar tanto el rendimiento como la estructura de una aplicación.

Desde el punta de vista estructural se puede percibir la aplicación como un grupo de computadoras colaborando entre sí, haciendo el sistema más fácil de entender y permitiendo separar las responsabilidades. La concurrencia permite mejorar los tiempos de respuesta y la eficiencia de una aplicación.

Se deben tener en cuenta las siguientes ideas sobre la concurrencia:
  • Introduce cierta sobrecarga.
  • Es compleja de manejar.
  • Los errores causados por ella son difícilmente reproducibles
  • Normalmente requiere cambios en el diseño.

El problema de la concurrencia es que los distintos hilos de una aplicación pueden entrelazarse siguiendo múltiples flujos de ejecución, lo que puede provocar problemas inesperados en situaciones normales.

Para defendernos de los problemas de concurrencia es importante que cada clase tenga una única responsabilidad,seperando la gestión de hilos se del resto del código. Hay que conocer las librerías que manejas y entender los distintos modelos de ejecución. Las secciones sincronizadas lo más pequeñas posibles.

Ejecutar pruebas de forma frecuente es la mejor manera de encontrar los posibles errores en el código. Sin embargo, es difícil hacer tests cuando hay concurrencia. Se deben tratar los fallos espúreos como posibles problemas de concurrencia e instrumentar el código para forzar la aparición de errores.

CONCLUSIONES FINALES

Todas estas buenas prácticas sobre cómo escribir código limpio se deben ejecitar de forma constante para adquirir buenos hábitos y ser capaces de hacerlo de forma natural. Siguiendo estas prácticas el autor nos promete conseguir un código con el que será mas fácil trabajar y hará mucho menos frustrante nuestro día a día.

Una idea que se repite en varios puntos del libro es que debemos de ser capaces de exprimir las capacidades de nuestro entorno de trabajo. Conocer nuestro entorno de trabajo nos puede simplificar enormemente nuestro trabajo, puesto que puede realizar por nosotros un montón de actividades de forma automática y nos ofrece una serie de ayudas que nos pueden ayudar en el día a día haciendo innecesarias algunas convenciones que sólo sirven para complicar el código.

Otra cosa que me ha gustado de este libro es la bibliografía en que se basa. No es excesivamente grande, pero me ha servido para sacar unas buenas recomendaciones sobre otros libros a leer:
  • Agile Software Development: Principles, Patterns and Practices, Robert Martin.
  • Implementation Patterns, Kent Beck.
  • Test Driven Development, Kent Beck.
  • Extreme Programming Explained: Embrace Change, Kent Beck.
  • Refactoring: Improving the Design of Existing Code, Martin Fowler et al..
  • Design Patterns: Elements or Reusable Object Oriented Software, Gamma et al..
  • The Pragmatic Programmer, Andrew Hunt, Dave Thomas.